Add locking to avoid races when changing the git-annex branch.

This commit is contained in:
Joey Hess 2011-10-03 16:32:36 -04:00
parent f77979b8b5
commit d357556141
4 changed files with 29 additions and 12 deletions

View file

@ -29,6 +29,8 @@ import Data.Maybe
import System.IO import System.IO
import System.IO.Binary import System.IO.Binary
import System.Posix.Process import System.Posix.Process
import System.Posix.IO
import System.Posix.Files
import System.Exit import System.Exit
import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.ByteString.Lazy.Char8 as L
@ -129,16 +131,17 @@ create = unlessM hasBranch $ do
{- Stages the journal, and commits staged changes to the branch. -} {- Stages the journal, and commits staged changes to the branch. -}
commit :: String -> Annex () commit :: String -> Annex ()
commit message = whenM stageJournalFiles $ do commit message = lockJournal $
g <- Annex.gitRepo whenM stageJournalFiles $ do
withIndex $ liftIO $ Git.commit g message fullname [fullname] g <- Annex.gitRepo
withIndex $ liftIO $ Git.commit g message fullname [fullname]
{- Ensures that the branch is up-to-date; should be called before {- Ensures that the branch is up-to-date; should be called before
- data is read from it. Runs only once per git-annex run. -} - data is read from it. Runs only once per git-annex run. -}
update :: Annex () update :: Annex ()
update = do update = do
state <- getState state <- getState
unless (branchUpdated state) $ withIndex $ do unless (branchUpdated state) $ withIndex $ lockJournal $ do
{- Since branches get merged into the index, it's important to {- Since branches get merged into the index, it's important to
- first stage the journal into the index. Otherwise, any - first stage the journal into the index. Otherwise, any
- changes in the journal would later get staged, and might - changes in the journal would later get staged, and might
@ -154,6 +157,7 @@ update = do
g <- Annex.gitRepo g <- Annex.gitRepo
unless (null updated && not staged) $ liftIO $ unless (null updated && not staged) $ liftIO $
Git.commit g "update" fullname (fullname:updated) Git.commit g "update" fullname (fullname:updated)
Annex.changeState $ \s -> s { Annex.branchstate = state { branchUpdated = True } } Annex.changeState $ \s -> s { Annex.branchstate = state { branchUpdated = True } }
invalidateCache invalidateCache
@ -215,13 +219,7 @@ updateRef ref
{- Applies a function to modifiy the content of a file. -} {- Applies a function to modifiy the content of a file. -}
change :: FilePath -> (String -> String) -> Annex () change :: FilePath -> (String -> String) -> Annex ()
change file a = do change file a = lockJournal $ get file >>= return . a >>= set file
lock
get file >>= return . a >>= set file
unlock
where
lock = return ()
unlock = return ()
{- Records new content of a file into the journal. -} {- Records new content of a file into the journal. -}
set :: FilePath -> String -> Annex () set :: FilePath -> String -> Annex ()
@ -277,7 +275,7 @@ setJournalFile file content = do
writeBinaryFile tmpfile content writeBinaryFile tmpfile content
renameFile tmpfile jfile renameFile tmpfile jfile
{- Gets journalled content for a file in the branch. -} {- Gets any journalled content for a file in the branch. -}
getJournalFile :: FilePath -> Annex (Maybe String) getJournalFile :: FilePath -> Annex (Maybe String)
getJournalFile file = do getJournalFile file = do
g <- Annex.gitRepo g <- Annex.gitRepo
@ -346,3 +344,14 @@ journalFile repo file = gitAnnexJournalDir repo </> concatMap mangle file
- filename on the branch. -} - filename on the branch. -}
fileJournal :: FilePath -> FilePath fileJournal :: FilePath -> FilePath
fileJournal = replace "//" "_" . replace "_" "/" fileJournal = replace "//" "_" . replace "_" "/"
{- Runs an action that modifies the journal, using locking to avoid
- contention with other git-annex processes. -}
lockJournal :: Annex a -> Annex a
lockJournal a = do
g <- Annex.gitRepo
h <- liftIO $ createFile (gitAnnexJournalLock g) stdFileMode
liftIO $ waitToSetLock h (WriteLock, AbsoluteSeek, 0, 0)
r <- a
liftIO $ closeFd h
return r

View file

@ -18,6 +18,7 @@ module Locations (
gitAnnexBadLocation, gitAnnexBadLocation,
gitAnnexUnusedLog, gitAnnexUnusedLog,
gitAnnexJournalDir, gitAnnexJournalDir,
gitAnnexJournalLock,
isLinkToAnnex, isLinkToAnnex,
hashDirMixed, hashDirMixed,
hashDirLower, hashDirLower,
@ -109,6 +110,10 @@ gitAnnexUnusedLog prefix r = gitAnnexDir r </> (prefix ++ "unused")
gitAnnexJournalDir :: Git.Repo -> FilePath gitAnnexJournalDir :: Git.Repo -> FilePath
gitAnnexJournalDir r = addTrailingPathSeparator $ gitAnnexDir r </> "journal" gitAnnexJournalDir r = addTrailingPathSeparator $ gitAnnexDir r </> "journal"
{- Lock file for the journal. -}
gitAnnexJournalLock :: Git.Repo -> FilePath
gitAnnexJournalLock r = gitAnnexDir r </> "journal.lck"
{- Checks a symlink target to see if it appears to point to annexed content. -} {- Checks a symlink target to see if it appears to point to annexed content. -}
isLinkToAnnex :: FilePath -> Bool isLinkToAnnex :: FilePath -> Bool
isLinkToAnnex s = ("/.git/" ++ objectDir) `isInfixOf` s isLinkToAnnex s = ("/.git/" ++ objectDir) `isInfixOf` s

1
debian/changelog vendored
View file

@ -6,6 +6,7 @@ git-annex (3.20110929) UNRELEASED; urgency=low
* When displaying a list of repositories, show git remote names * When displaying a list of repositories, show git remote names
in addition to their descriptions. in addition to their descriptions.
* Contain the zombie hordes. * Contain the zombie hordes.
* Add locking to avoid races when changing the git-annex branch.
-- Joey Hess <joeyh@debian.org> Thu, 29 Sep 2011 18:58:53 -0400 -- Joey Hess <joeyh@debian.org> Thu, 29 Sep 2011 18:58:53 -0400

View file

@ -49,3 +49,5 @@ Make Branch.change transactional, so it takes a lock, reads a file,
applies a function to it, and writes the changed file. applies a function to it, and writes the changed file.
Make Branch.update hold the same lock. Make Branch.update hold the same lock.
> [[Done]].