summaryrefslogtreecommitdiff
path: root/Utility
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2012-06-18 16:18:59 -0400
committerGravatar Joey Hess <joey@kitenet.net>2012-06-18 16:18:59 -0400
commit90d565149abd7d752e22beb4aa57bf99522e5851 (patch)
tree72e18c7fcbfecc2bfb689e03023480f407dd6d25 /Utility
parent89fcee03d0f542c25d1afa9962839916f70994b3 (diff)
flesh out kqueue library
Have not tried to build this yet. But barring minor mistakes, I think it's good.
Diffstat (limited to 'Utility')
-rw-r--r--Utility/Kqueue.hs40
-rw-r--r--Utility/libkqueue.c64
-rw-r--r--Utility/libkqueue.h3
3 files changed, 81 insertions, 26 deletions
diff --git a/Utility/Kqueue.hs b/Utility/Kqueue.hs
index e8ce73b26..a3d8aff2d 100644
--- a/Utility/Kqueue.hs
+++ b/Utility/Kqueue.hs
@@ -25,6 +25,8 @@ import qualified Data.Map as M
type DirMap = M.Map Fd FilePath
+data Kqueue = Kqueue Fd DirMap
+
{- Builds a map of directories in a tree, possibly pruning some.
- Opens each directory in the tree. -}
scanRecursive :: FilePath -> (FilePath -> Bool) -> IO DirMap
@@ -43,25 +45,33 @@ addSubDir dirmap dir prune = M.union dirmap <$> scanRecursive dir prune
removeSubDir :: FilePath -> DirMap -> DirMap
removeSubDir dir = M.filter (not . dirContains dir)
-foreign import ccall unsafe "libkqueue.h waitchange" c_waitchange
- :: Ptr Fd -> IO Fd
+foreign import ccall unsafe "libkqueue.h init_kqueue" c_init_kqueue
+ :: CInt -> Ptr Fd -> IO Fd
+foreign import ccall unsafe "libkqueue.h waitchange_kqueue" c_waitchange_kqueue
+ :: Fd -> IO Fd
+
+{- Initializes a Kqueue to watch a map of directories. -}
+initKqueue :: DirMap -> IO Kqueue
+initKqueue dirmap = withArrayLen (M.keys dirmap) $ \fdcnt c_fds ->
+ h <- c_init_kqueue (fromIntegral fdcnt) c_fds
+ return $ Kqueue h dirmap
-{- Waits for a change in a map of directories, and returns the directory
- - where the change took place.
+{- Stops a Kqueue. Note: Does not directly close the Fds in the dirmap,
+ - so it can be reused. -}
+stopKqueue :: Kqueue -> IO
+stopKqueue (Kqueue h _) = closeFd h
+
+{- Waits for a change on a Kqueue, and returns the directory
+ - or directories where a change took place.
-
- The kqueue interface does not tell what type of change took place in
- the directory; it could be an added file, a deleted file, a renamed
- file, a new subdirectory, or a deleted subdirectory, or a moved
- subdirectory.
-
- - Note that if subdirectories have changed, the caller will want to
- - update the map before calling this again. -}
-waitChange :: DirMap -> IO (Maybe FilePath)
-waitChange dirmap = withArray (M.keys dirmap) $ \c_fds -> do
- changed <- c_waitchange c_fds
- ifM (safeErrno <$> getErrno)
- ( return $ M.lookup changed dirmap
- , return Nothing
- )
- where
- safeErrno (Errno v) = v == 0
+ - Note that if subdirectories have changed, the caller should re-run
+ - initKqueue to get them watched. -}
+waitChange :: Kqueue -> IO [FilePath]
+waitChange (Kqueue h dirmap) = do
+ changed <- c_waitchange_kqueue h
+ return $ M.lookup changed dirmap
diff --git a/Utility/libkqueue.c b/Utility/libkqueue.c
index 0ef42b801..a919a60c7 100644
--- a/Utility/libkqueue.c
+++ b/Utility/libkqueue.c
@@ -5,18 +5,62 @@
* Licensed under the GNU GPL version 3 or higher.
*/
-#include <errno.h>
#include <stdio.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/event.h>
+#include <sys/time.h>
-/* Waits for a change event on one of the array of directory fds,
- * and returns the one that changed. */
-int waitchange(const int *fds) {
-// if (kqueue(blah, &fds) != 0)
-// return 0; /* errno is set */
-// else
- errno = 0;
+/* Initializes a kqueue, with a list of fds to watch for changes.
+ * Returns the kqueue's handle. */
+int init_kqueue(const int fdcnt, const int *fdlist) {
+ struct nodelay = {0, 0};
+ int kq;
- printf("in waitchange!, %i %i\n", fds[0], fds[1]);
+ if ((kq = kqueue()) == -1) {
+ perror("kqueue");
+ exit(1);
+ }
- return fds[0];
+ /* Prime the pump with the list of fds, but don't wait for any
+ * change events. */
+ helper(kq, fdcnt, fdlist, &nodelay);
+
+ return kq;
+}
+
+/* Waits for a change event on a kqueue.
+ *
+ * Returns the fd that changed, or -1 on error.
+ */
+signed int waitchange_kqueue(const int kq) {
+ helper(kq, 0, NULL, NULL);
+}
+
+/* The specified fds are added to the set of fds being watched for changes.
+ * Fds passed to prior calls still take effect, so it's most efficient to
+ * not pass the same fds repeatedly.
+ */
+signed int helper(const int kq, const int fdcnt, const int *fdlist, cont struct *timeout) {
+ int i, nev;
+ struct kevent evlist[1];
+ struct kevent chlist[fdcnt];
+
+ for (i = 0; i < fdcnt; i++) {
+ EV_SET(&chlist[i], fdlist[i], EVFILT_VNODE,
+ EV_ADD | EV_ENABLE | EV_CLEAR,
+ NOTE_WRITE,
+ 1,
+ timeout);
+ }
+
+ nev = kevent(info->kq, info->chlist, info->cnt, info->evlist,
+ 1, NULL);
+
+ if (nev == 1)
+ return evlist[0].ident;
+ else
+ return -1;
}
diff --git a/Utility/libkqueue.h b/Utility/libkqueue.h
index 75af9eeba..1a285b8da 100644
--- a/Utility/libkqueue.h
+++ b/Utility/libkqueue.h
@@ -1 +1,2 @@
-int waitchange(const int *fds);
+int init_kqueue(const int fdcnt, const int *fdlist);
+signed int waitchange_kqueue(const int kq);