summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Command/Watch.hs105
-rw-r--r--debian/control1
-rw-r--r--git-annex.cabal5
3 files changed, 84 insertions, 27 deletions
diff --git a/Command/Watch.hs b/Command/Watch.hs
index 7c1bc5c17..34282e46c 100644
--- a/Command/Watch.hs
+++ b/Command/Watch.hs
@@ -12,9 +12,6 @@ module Command.Watch where
import Common.Annex
import Command
-#if defined linux_HOST_OS
-import Utility.Inotify
-#endif
import Utility.ThreadLock
import qualified Annex
import qualified Annex.Queue
@@ -25,11 +22,16 @@ import qualified Backend
import Annex.Content
import Control.Concurrent
+import Control.Concurrent.STM
+import Data.Time.Clock
#if defined linux_HOST_OS
+import Utility.Inotify
import System.INotify
#endif
+type ChangeChan = TChan UTCTime
+
def :: [Command]
def = [command "watch" paramPaths seek "watch for changes"]
@@ -42,8 +44,10 @@ start = notBareRepo $ do
showStart "watch" "."
showAction "scanning"
inRepo $ Git.Command.run "add" [Param "--update"]
- next $ next $ withStateMVar $ \mvar -> liftIO $ withINotify $ \i -> do
- let hook a = Just $ runHook mvar a
+ next $ next $ withStateMVar $ \st -> liftIO $ withINotify $ \i -> do
+ changechan <- atomically newTChan
+ _ <- forkIO $ commitThread st changechan
+ let hook a = Just $ runHook st changechan a
let hooks = WatchHooks
{ addHook = hook onAdd
, delHook = hook onDel
@@ -52,7 +56,6 @@ start = notBareRepo $ do
, errHook = hook onErr
}
watchDir i "." (ignored . takeFileName) hooks
- _ <- forkIO $ commitThread mvar
putStrLn "(started)"
waitForTermination
return True
@@ -91,12 +94,12 @@ runStateMVar mvar a = do
-
- Exceptions are ignored, otherwise a whole watcher thread could be crashed.
-}
-runHook :: MVar Annex.AnnexState -> (FilePath -> Annex ()) -> FilePath -> IO ()
-runHook mvar a f = handle =<< tryIO (runStateMVar mvar go)
+runHook :: MVar Annex.AnnexState -> ChangeChan -> (FilePath -> Annex ()) -> FilePath -> IO ()
+runHook st changetimes a f = handle =<< tryIO (runStateMVar st go)
where
go = do
a f
- Annex.Queue.flushWhenFull
+ signalChange changetimes
handle (Right ()) = return ()
handle (Left e) = putStrLn $ show e
@@ -160,20 +163,72 @@ stageSymlink file linktext =
Annex.Queue.addUpdateIndex =<<
inRepo (Git.UpdateIndex.stageSymlink file linktext)
-{- This thread wakes up periodically and makes git commits. -}
-commitThread :: MVar Annex.AnnexState -> IO ()
-commitThread mvar = forever $ do
- threadDelay 1000000 -- 1 second
- commit
+{- Signals that a change has been made, that needs to get committed. -}
+signalChange :: ChangeChan -> Annex ()
+signalChange chan = do
+ liftIO $ (atomically . writeTChan chan) =<< getCurrentTime
+ -- Just in case the commit thread is not flushing
+ -- the queue fast enough.
+ Annex.Queue.flushWhenFull
+
+{- Gets the times of all unhandled changes.
+ - Blocks until at least one change is made. -}
+getChanges :: ChangeChan -> IO [UTCTime]
+getChanges chan = atomically $ do
+ c <- readTChan chan
+ go [c]
+ where
+ go l = do
+ v <- tryReadTChan chan
+ case v of
+ Nothing -> return l
+ Just c -> go (c:l)
+
+{- Puts unhandled changes back into the channel.
+ - Note: Original order is not preserved. -}
+refillChanges :: ChangeChan -> [UTCTime] -> IO ()
+refillChanges chan cs = atomically $ mapM_ (writeTChan chan) cs
+
+{- This thread makes git commits. -}
+commitThread :: MVar Annex.AnnexState -> ChangeChan -> IO ()
+commitThread st changechan = forever $ do
+ -- First, a simple rate limiter.
+ threadDelay $ oneSecond
+ liftIO $ putStrLn "running"
+ -- Next, wait until at least one change has been made.
+ cs <- getChanges changechan
+ -- Now see if now's a good time to commit.
+ ifM (shouldCommit <$> getCurrentTime <*> pure cs) $
+ ( commit
+ , do
+ liftIO $ putStrLn $ "no commit now " ++ show (length cs)
+ refillChanges changechan cs
+ )
+ where
+ commit = void $ tryIO $ runStateMVar st $ do
+ Annex.Queue.flush
+ {- Empty commits may be made if tree
+ - changes cancel each other out, etc. -}
+ inRepo $ Git.Command.run "commit"
+ [ Param "--allow-empty-message"
+ , Param "-m", Param ""
+ , Param "--allow-empty"
+ , Param "--quiet"
+ ]
+ oneSecond = 1000000 -- microseconds
+
+{- Decide if now is a good time to make a commit.
+ - Note that the list of change times has an undefined order.
+ -
+ - Current strategy: If there have been 10 commits within the past second,
+ - a batch activity is taking place, so wait for later.
+ -}
+shouldCommit :: UTCTime -> [UTCTime] -> Bool
+shouldCommit now changetimes
+ | len == 0 = False
+ | len > 4096 = True -- avoid bloating queue too much
+ | length (filter thisSecond changetimes) < 10 = True
+ | otherwise = False -- batch activity
where
- commit = tryIO $ runStateMVar mvar $
- whenM ((>) <$> Annex.Queue.size <*> pure 0) $ do
- Annex.Queue.flush
- {- Empty commits may be made if tree
- - changes cancel each other out, etc. -}
- inRepo $ Git.Command.run "commit"
- [ Param "--allow-empty-message"
- , Param "-m", Param ""
- , Param "--allow-empty"
- , Param "--quiet"
- ]
+ len = length changetimes
+ thisSecond t = now `diffUTCTime` t <= 1
diff --git a/debian/control b/debian/control
index bfb0017bc..6534fef31 100644
--- a/debian/control
+++ b/debian/control
@@ -21,6 +21,7 @@ Build-Depends:
libghc-bloomfilter-dev,
libghc-edit-distance-dev,
libghc-hinotify-dev,
+ libghc-stm-dev,
ikiwiki,
perlmagick,
git,
diff --git a/git-annex.cabal b/git-annex.cabal
index 114a4069f..e1ad16453 100644
--- a/git-annex.cabal
+++ b/git-annex.cabal
@@ -36,7 +36,7 @@ Executable git-annex
pcre-light, extensible-exceptions, dataenc, SHA, process, json, HTTP,
base == 4.5.*, monad-control, transformers-base, lifted-base,
IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance,
- hinotify
+ hinotify, STM
Other-Modules: Utility.Touch
C-Sources: Utility/libdiskfree.c
Extensions: CPP
@@ -52,7 +52,8 @@ Test-Suite test
unix, containers, utf8-string, network, mtl, bytestring, old-locale, time,
pcre-light, extensible-exceptions, dataenc, SHA, process, json, HTTP,
base == 4.5.*, monad-control, transformers-base, lifted-base,
- IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance
+ IfElse, text, QuickCheck >= 2.1, bloomfilter, edit-distance,
+ hinotify, STM
C-Sources: Utility/libdiskfree.c
Extensions: CPP