From 5e9fdac92fe91a60d8f4570ec5d785976fe6c3ee Mon Sep 17 00:00:00 2001 From: Joey Hess Date: Mon, 18 Jun 2012 21:46:04 -0400 Subject: [PATCH] update kqueue when new directories are added --- Utility/Kqueue.hs | 27 +++++++++++++++++++++------ Utility/libkqueue.c | 25 ++++++++++++------------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/Utility/Kqueue.hs b/Utility/Kqueue.hs index 911eb71a95..08029d703e 100644 --- a/Utility/Kqueue.hs +++ b/Utility/Kqueue.hs @@ -100,7 +100,9 @@ removeSubDir dirmap dir = do (toremove, rest) = M.partition (dirContains dir . dirName) dirmap foreign import ccall unsafe "libkqueue.h init_kqueue" c_init_kqueue - :: CInt -> Ptr Fd -> IO Fd + :: IO Fd +foreign import ccall unsafe "libkqueue.h addfds_kqueue" c_addfds_kqueue + :: Fd -> CInt -> Ptr Fd -> IO () foreign import ccall unsafe "libkqueue.h waitchange_kqueue" c_waitchange_kqueue :: Fd -> IO Fd @@ -108,9 +110,16 @@ foreign import ccall unsafe "libkqueue.h waitchange_kqueue" c_waitchange_kqueue initKqueue :: FilePath -> Pruner -> IO Kqueue initKqueue dir pruned = do dirmap <- scanRecursive dir pruned + h <- c_init_kqueue + let kq = Kqueue h dirmap pruned + updateKqueue kq + return kq + +{- Updates a Kqueue, adding watches for its map. -} +updateKqueue :: Kqueue -> IO () +updateKqueue (Kqueue h dirmap _) = withArrayLen (M.keys dirmap) $ \fdcnt c_fds -> do - h <- c_init_kqueue (fromIntegral fdcnt) c_fds - return $ Kqueue h dirmap pruned + c_addfds_kqueue h (fromIntegral fdcnt) c_fds {- Stops a Kqueue. Note: Does not directly close the Fds in the dirmap, - so it can be reused. -} @@ -155,10 +164,16 @@ handleChange kq@(Kqueue h dirmap pruner) fd olddirinfo = -- Update the cached dirinfo just looked up. let newmap'' = M.insertWith' const fd newdirinfo newmap' - ret (newmap'', changes) + + -- When new directories were added, need to update + -- the kqueue to watch them. + let kq' = Kqueue h newmap'' pruner + unless (null newdirinfos) $ + updateKqueue kq' + + return (kq', changes) go Nothing = do -- The directory has been moved or deleted, so -- remove it from our map. newmap <- removeSubDir dirmap (dirName olddirinfo) - ret (newmap, []) - ret (newmap, changes) = return $ (Kqueue h newmap pruner, changes) + return (Kqueue h newmap pruner, []) diff --git a/Utility/libkqueue.c b/Utility/libkqueue.c index 5b38cdd339..b7f9595dc1 100644 --- a/Utility/libkqueue.c +++ b/Utility/libkqueue.c @@ -18,11 +18,12 @@ * 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, - struct timespec *timeout) { +signed int helper(const int kq, const int fdcnt, const int *fdlist, int nodelay) { int i, nev; struct kevent evlist[1]; struct kevent chlist[fdcnt]; + struct timespec avoiddelay = {0, 0}; + struct timespec *timeout = nodelay ? &avoiddelay : NULL; for (i = 0; i < fdcnt; i++) { EV_SET(&chlist[i], fdlist[i], EVFILT_VNODE, @@ -43,30 +44,27 @@ signed int helper(const int kq, const int fdcnt, const int *fdlist, return -1; } -/* Initializes a kqueue, with a list of fds to watch for changes. - * Returns the kqueue's handle. */ +/* Initializes a new, empty kqueue. */ int init_kqueue(const int fdcnt, const int *fdlist) { - struct timespec nodelay = {0, 0}; int kq; - if ((kq = kqueue()) == -1) { perror("kqueue"); exit(1); } - - /* Prime the pump with the list of fds, but don't wait for any - * change events. */ - helper(kq, fdcnt, fdlist, &nodelay); - return kq; } +/* Adds fds to the set that should be watched. */ +void addfds_kqueue(const int kq, const int fdcnt, const int *fdlist) { + helper(kq, fdcnt, fdlist, 1); +} + /* Waits for a change event on a kqueue. * * Returns the fd that changed, or -1 on error. */ signed int waitchange_kqueue(const int kq) { - return helper(kq, 0, NULL, NULL); + return helper(kq, 0, NULL, 0); } /* @@ -74,7 +72,8 @@ main () { int list[1]; int kq; list[0]=open(".", O_RDONLY); - kq = init_kqueue(1, list); + kq = init_kqueue(); + addfds_kqueue(kq, 1, list) printf("change: %i\n", waitchange_kqueue(kq)); } */