diff options
author | Joey Hess <joey@kitenet.net> | 2012-06-18 16:18:59 -0400 |
---|---|---|
committer | Joey Hess <joey@kitenet.net> | 2012-06-18 16:18:59 -0400 |
commit | 90d565149abd7d752e22beb4aa57bf99522e5851 (patch) | |
tree | 72e18c7fcbfecc2bfb689e03023480f407dd6d25 /Utility | |
parent | 89fcee03d0f542c25d1afa9962839916f70994b3 (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.hs | 40 | ||||
-rw-r--r-- | Utility/libkqueue.c | 64 | ||||
-rw-r--r-- | Utility/libkqueue.h | 3 |
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); |