, branchstate :: BranchState
, repoqueue :: Maybe (Git.Queue.Queue Annex)
, catfilehandles :: CatFileHandles
- , hashobjecthandle :: Maybe HashObjectHandle
+ , hashobjecthandle :: Maybe (ResourcePool HashObjectHandle)
, checkattrhandle :: Maybe (ResourcePool CheckAttrHandle)
, checkignorehandle :: Maybe (ResourcePool CheckIgnoreHandle)
, globalnumcopies :: Maybe NumCopies
mergeIndex :: JournalLocked -> [Git.Ref] -> Annex ()
mergeIndex jl branches = do
prepareModifyIndex jl
- hashhandle <- hashObjectHandle
- withCatFileHandle $ \ch ->
- inRepo $ \g -> Git.UnionMerge.mergeIndex hashhandle ch g branches
+ withHashObjectHandle $ \hashhandle ->
+ withCatFileHandle $ \ch ->
+ inRepo $ \g -> Git.UnionMerge.mergeIndex hashhandle ch g branches
{- Removes any stale git lock file, to avoid git falling over when
- updating the index.
g <- gitRepo
let dir = gitAnnexJournalDir g
(jlogf, jlogh) <- openjlog (fromRawFilePath tmpdir)
- h <- hashObjectHandle
- withJournalHandle gitAnnexJournalDir $ \jh ->
- Git.UpdateIndex.streamUpdateIndex g
- [genstream dir h jh jlogh]
+ withHashObjectHandle $ \h ->
+ withJournalHandle gitAnnexJournalDir $ \jh ->
+ Git.UpdateIndex.streamUpdateIndex g
+ [genstream dir h jh jlogh]
commitindex
liftIO $ cleanup (fromRawFilePath dir) jlogh jlogf
where
{- git-annex concurrent state
-
- - Copyright 2015-2021 Joey Hess <id@joeyh.name>
+ - Copyright 2015-2022 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
import Types.CatFileHandles
import Annex.CatFile
import Annex.CheckAttr
+import Annex.HashObject
import Annex.CheckIgnore
import qualified Data.Map as M
fromnonconcurrent = do
catFileStop
checkAttrStop
+ hashObjectStop
checkIgnoreStop
cfh <- liftIO catFileHandlesPool
cah <- mkConcurrentCheckAttrHandle c
+ hoh <- mkConcurrentHashObjectHandle c
cih <- mkConcurrentCheckIgnoreHandle c
Annex.changeState $ \s -> s
{ Annex.concurrency = newc
, Annex.catfilehandles = cfh
, Annex.checkattrhandle = Just cah
+ , Annex.hashobjecthandle = Just hoh
, Annex.checkignorehandle = Just cih
}
-{- git hash-object interface, with handle automatically stored in the Annex monad
+{- git hash-object interface
-
- - Copyright 2016 Joey Hess <id@joeyh.name>
+ - Copyright 2016-2022 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
module Annex.HashObject (
hashFile,
hashBlob,
- hashObjectHandle,
hashObjectStop,
+ mkConcurrentHashObjectHandle,
+ withHashObjectHandle,
) where
import Annex.Common
import qualified Git.HashObject
import qualified Annex
import Git.Types
-
-hashObjectHandle :: Annex Git.HashObject.HashObjectHandle
-hashObjectHandle = maybe startup return =<< Annex.getState Annex.hashobjecthandle
- where
- startup = do
- h <- inRepo $ Git.HashObject.hashObjectStart True
- Annex.changeState $ \s -> s { Annex.hashobjecthandle = Just h }
- return h
+import Utility.ResourcePool
+import Types.Concurrency
+import Annex.Concurrent.Utility
hashObjectStop :: Annex ()
hashObjectStop = maybe noop stop =<< Annex.getState Annex.hashobjecthandle
where
- stop h = do
- liftIO $ Git.HashObject.hashObjectStop h
+ stop p = do
+ liftIO $ freeResourcePool p Git.HashObject.hashObjectStop
Annex.changeState $ \s -> s { Annex.hashobjecthandle = Nothing }
- return ()
hashFile :: RawFilePath -> Annex Sha
-hashFile f = do
- h <- hashObjectHandle
+hashFile f = withHashObjectHandle $ \h ->
liftIO $ Git.HashObject.hashFile h f
{- Note that the content will be written to a temp file.
- So it may be faster to use Git.HashObject.hashObject for large
- blob contents. -}
hashBlob :: Git.HashObject.HashableBlob b => b -> Annex Sha
-hashBlob content = do
- h <- hashObjectHandle
+hashBlob content = withHashObjectHandle $ \h ->
liftIO $ Git.HashObject.hashBlob h content
+
+withHashObjectHandle :: (Git.HashObject.HashObjectHandle -> Annex a) -> Annex a
+withHashObjectHandle a =
+ maybe mkpool go =<< Annex.getState Annex.hashobjecthandle
+ where
+ go p = withResourcePool p start a
+ start = inRepo $ Git.HashObject.hashObjectStart True
+ mkpool = do
+ -- This only runs in non-concurrent code paths;
+ -- a concurrent pool is set up earlier when needed.
+ p <- mkResourcePoolNonConcurrent start
+ Annex.changeState $ \s -> s { Annex.hashobjecthandle = Just p }
+ go p
+
+mkConcurrentHashObjectHandle :: Concurrency -> Annex (ResourcePool Git.HashObject.HashObjectHandle)
+mkConcurrentHashObjectHandle c =
+ Annex.getState Annex.hashobjecthandle >>= \case
+ Just p@(ResourcePool {}) -> return p
+ _ -> mkResourcePool =<< liftIO (maxHashObjects c)
+
+{- git hash-object is typically CPU bound, and is not likely to be the main
+ - bottleneck for any command. So limit to the number of CPU cores, maximum,
+ - while respecting the -Jn value.
+ -}
+maxHashObjects :: Concurrency -> IO Int
+maxHashObjects = concurrencyUpToCpus
automatically upgrade to v10 in a year's time.
To avoid this upgrade, you can set annex.autoupgraderepository to false.
* Use v10 by default for new repositories.
+ * Avoid starting an unncessary number of git hash-object processes when
+ concurrency is enabled.
-- Joey Hess <id@joeyh.name> Mon, 25 Jul 2022 15:35:45 -0400
<*> newTVarIO []
{- When there will not be multiple threads that may
- - may concurrently try to use it, using this is more
+ - concurrently try to use it, using this is more
- efficient than mkResourcePool.
-}
mkResourcePoolNonConcurrent :: (MonadMask m, MonadIO m) => m r -> m (ResourcePool r)
[[!tag projects/dandi]]
+> [[done]]; this is now handled like other git helper processes
+> and will be capped to the maximum of the number of jobs or cpu cores,
+> and in practice usually fewer than that will be started. --[[Joey]]