reorganize and refactor lock code
Added a convenience Utility.LockFile that is not a windows/posix portability shim, but still manages to cut down on the boilerplate around locking. This commit was sponsored by Johan Herland.
This commit is contained in:
parent
0a4d301051
commit
d279180266
9 changed files with 90 additions and 76 deletions
|
@ -56,10 +56,7 @@ import Annex.Perms
|
||||||
import Annex.Link
|
import Annex.Link
|
||||||
import Annex.Content.Direct
|
import Annex.Content.Direct
|
||||||
import Annex.ReplaceFile
|
import Annex.ReplaceFile
|
||||||
|
import Utility.LockFile
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
import Utility.WinLock
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{- Checks if a given key's content is currently present. -}
|
{- Checks if a given key's content is currently present. -}
|
||||||
inAnnex :: Key -> Annex Bool
|
inAnnex :: Key -> Annex Bool
|
||||||
|
@ -177,24 +174,21 @@ lockContent key a = do
|
||||||
nukeFile lockfile
|
nukeFile lockfile
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
lock contentfile Nothing = opencontentforlock contentfile >>= dolock
|
lock contentfile Nothing = opencontentforlock contentfile >>= dolock
|
||||||
lock _ (Just lockfile) = openforlock lockfile >>= dolock . Just
|
lock _ (Just lockfile) = createLockFile Nothing lockfile >>= dolock . Just
|
||||||
{- Since content files are stored with the write bit disabled, have
|
{- Since content files are stored with the write bit disabled, have
|
||||||
- to fiddle with permissions to open for an exclusive lock. -}
|
- to fiddle with permissions to open for an exclusive lock. -}
|
||||||
opencontentforlock f = catchMaybeIO $ ifM (doesFileExist f)
|
opencontentforlock f = catchMaybeIO $ ifM (doesFileExist f)
|
||||||
( withModifiedFileMode f
|
( withModifiedFileMode f
|
||||||
(`unionFileModes` ownerWriteMode)
|
(`unionFileModes` ownerWriteMode)
|
||||||
(openforlock f)
|
(createLockFie Nothing f)
|
||||||
, openforlock f
|
, createLockFile Nothing f
|
||||||
)
|
)
|
||||||
openforlock f = openFd f ReadWrite Nothing defaultFileFlags
|
|
||||||
dolock Nothing = return Nothing
|
dolock Nothing = return Nothing
|
||||||
dolock (Just fd) = do
|
dolock (Just fd) = do
|
||||||
v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
v <- tryIO $ setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
case v of
|
case v of
|
||||||
Left _ -> alreadylocked
|
Left _ -> alreadylocked
|
||||||
Right _ -> do
|
Right _ -> return $ Just fd
|
||||||
setFdOption fd CloseOnExec True
|
|
||||||
return $ Just fd
|
|
||||||
unlock mlockfile mfd = do
|
unlock mlockfile mfd = do
|
||||||
maybe noop cleanuplockfile mlockfile
|
maybe noop cleanuplockfile mlockfile
|
||||||
liftIO $ maybe noop closeFd mfd
|
liftIO $ maybe noop closeFd mfd
|
||||||
|
|
|
@ -19,13 +19,10 @@ import Annex
|
||||||
import Types.LockPool
|
import Types.LockPool
|
||||||
import qualified Git
|
import qualified Git
|
||||||
import Annex.Perms
|
import Annex.Perms
|
||||||
|
import Utility.LockFile
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
import Utility.WinLock
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{- Create a specified lock file, and takes a shared lock, which is retained
|
{- Create a specified lock file, and takes a shared lock, which is retained
|
||||||
- in the pool. -}
|
- in the pool. -}
|
||||||
lockFileShared :: FilePath -> Annex ()
|
lockFileShared :: FilePath -> Annex ()
|
||||||
|
@ -35,10 +32,7 @@ lockFileShared file = go =<< fromLockPool file
|
||||||
go Nothing = do
|
go Nothing = do
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
mode <- annexFileMode
|
mode <- annexFileMode
|
||||||
lockhandle <- liftIO $ noUmask mode $
|
lockhandle <- liftIO $ noUmask mode $ lockShared (Just mode) file
|
||||||
openFd file ReadWrite (Just mode) defaultFileFlags
|
|
||||||
liftIO $ setFdOption lockhandle CloseOnExec True
|
|
||||||
liftIO $ waitToSetLock lockhandle (ReadLock, AbsoluteSeek, 0, 0)
|
|
||||||
#else
|
#else
|
||||||
lockhandle <- liftIO $ waitToLock $ lockShared file
|
lockhandle <- liftIO $ waitToLock $ lockShared file
|
||||||
#endif
|
#endif
|
||||||
|
@ -48,11 +42,7 @@ unlockFile :: FilePath -> Annex ()
|
||||||
unlockFile file = maybe noop go =<< fromLockPool file
|
unlockFile file = maybe noop go =<< fromLockPool file
|
||||||
where
|
where
|
||||||
go lockhandle = do
|
go lockhandle = do
|
||||||
#ifndef mingw32_HOST_OS
|
|
||||||
liftIO $ closeFd lockhandle
|
|
||||||
#else
|
|
||||||
liftIO $ dropLock lockhandle
|
liftIO $ dropLock lockhandle
|
||||||
#endif
|
|
||||||
changeLockPool $ M.delete file
|
changeLockPool $ M.delete file
|
||||||
|
|
||||||
getLockPool :: Annex LockPool
|
getLockPool :: Annex LockPool
|
||||||
|
@ -73,16 +63,10 @@ withExclusiveLock getlockfile a = do
|
||||||
lockfile <- fromRepo getlockfile
|
lockfile <- fromRepo getlockfile
|
||||||
createAnnexDirectory $ takeDirectory lockfile
|
createAnnexDirectory $ takeDirectory lockfile
|
||||||
mode <- annexFileMode
|
mode <- annexFileMode
|
||||||
bracketIO (lock lockfile mode) unlock (const a)
|
bracketIO (lock mode lockfile) dropLock (const a)
|
||||||
where
|
where
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
lock lockfile mode = do
|
lock mode = noUmask mode . lockExclusive (Just mode)
|
||||||
l <- noUmask mode $ createFile lockfile mode
|
|
||||||
setFdOption l CloseOnExec True
|
|
||||||
waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0)
|
|
||||||
return l
|
|
||||||
unlock = closeFd
|
|
||||||
#else
|
#else
|
||||||
lock lockfile _mode = waitToLock $ lockExclusive lockfile
|
lock _mode = waitToLock . lockExclusive
|
||||||
unlock = dropLock
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -35,6 +35,7 @@ import Config.Files
|
||||||
import Utility.Env
|
import Utility.Env
|
||||||
import Types.CleanupActions
|
import Types.CleanupActions
|
||||||
import Annex.Index (addGitEnv)
|
import Annex.Index (addGitEnv)
|
||||||
|
import Utility.LockFile
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
import Annex.Perms
|
import Annex.Perms
|
||||||
#endif
|
#endif
|
||||||
|
@ -151,9 +152,7 @@ sshCleanup = mapM_ cleanup =<< enumSocketFiles
|
||||||
let lockfile = socket2lock socketfile
|
let lockfile = socket2lock socketfile
|
||||||
unlockFile lockfile
|
unlockFile lockfile
|
||||||
mode <- annexFileMode
|
mode <- annexFileMode
|
||||||
fd <- liftIO $ noUmask mode $
|
fd <- liftIO $ noUmask mode $ createLockFile (Just mode) lockfile
|
||||||
openFd lockfile ReadWrite (Just mode) defaultFileFlags
|
|
||||||
liftIO $ setFdOption fd CloseOnExec True
|
|
||||||
v <- liftIO $ tryIO $
|
v <- liftIO $ tryIO $
|
||||||
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
case v of
|
case v of
|
||||||
|
|
|
@ -18,16 +18,12 @@ import Annex.Direct
|
||||||
import Annex.Hook
|
import Annex.Hook
|
||||||
import Annex.View
|
import Annex.View
|
||||||
import Annex.View.ViewedFile
|
import Annex.View.ViewedFile
|
||||||
import Annex.Perms
|
import Annex.LockFile
|
||||||
import Logs.View
|
import Logs.View
|
||||||
import Logs.MetaData
|
import Logs.MetaData
|
||||||
import Types.View
|
import Types.View
|
||||||
import Types.MetaData
|
import Types.MetaData
|
||||||
|
|
||||||
#ifdef mingw32_HOST_OS
|
|
||||||
import Utility.WinLock
|
|
||||||
#endif
|
|
||||||
|
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
|
|
||||||
def :: [Command]
|
def :: [Command]
|
||||||
|
@ -92,20 +88,4 @@ showMetaDataChange = showLongNote . unlines . concatMap showmeta . fromMetaData
|
||||||
|
|
||||||
{- Takes exclusive lock; blocks until available. -}
|
{- Takes exclusive lock; blocks until available. -}
|
||||||
lockPreCommitHook :: Annex a -> Annex a
|
lockPreCommitHook :: Annex a -> Annex a
|
||||||
lockPreCommitHook a = do
|
lockPreCommitHook = withExclusiveLock gitAnnexPreCommitLock
|
||||||
lockfile <- fromRepo gitAnnexPreCommitLock
|
|
||||||
createAnnexDirectory $ takeDirectory lockfile
|
|
||||||
mode <- annexFileMode
|
|
||||||
bracketIO (lock lockfile mode) unlock (const a)
|
|
||||||
where
|
|
||||||
#ifndef mingw32_HOST_OS
|
|
||||||
lock lockfile mode = do
|
|
||||||
l <- liftIO $ noUmask mode $ createFile lockfile mode
|
|
||||||
setFdOption l CloseOnExec True
|
|
||||||
liftIO $ waitToSetLock l (WriteLock, AbsoluteSeek, 0, 0)
|
|
||||||
return l
|
|
||||||
unlock = closeFd
|
|
||||||
#else
|
|
||||||
lock lockfile _mode = liftIO $ waitToLock $ lockExclusive lockfile
|
|
||||||
unlock = dropLock
|
|
||||||
#endif
|
|
||||||
|
|
|
@ -16,10 +16,9 @@ import Types.Remote
|
||||||
import Types.CleanupActions
|
import Types.CleanupActions
|
||||||
import qualified Annex
|
import qualified Annex
|
||||||
import Annex.LockFile
|
import Annex.LockFile
|
||||||
|
import Utility.LockFile
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
import Annex.Perms
|
import Annex.Perms
|
||||||
#else
|
|
||||||
import Utility.WinLock
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
{- Modifies a remote's access functions to first run the
|
{- Modifies a remote's access functions to first run the
|
||||||
|
@ -84,9 +83,7 @@ runHooks r starthook stophook a = do
|
||||||
unlockFile lck
|
unlockFile lck
|
||||||
#ifndef mingw32_HOST_OS
|
#ifndef mingw32_HOST_OS
|
||||||
mode <- annexFileMode
|
mode <- annexFileMode
|
||||||
fd <- liftIO $ noUmask mode $
|
fd <- liftIO $ noUmask mode $ createLockFile (Just mode) lck
|
||||||
openFd lck ReadWrite (Just mode) defaultFileFlags
|
|
||||||
liftIO $ setFdOption fd CloseOnExec True
|
|
||||||
v <- liftIO $ tryIO $
|
v <- liftIO $ tryIO $
|
||||||
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
setLock fd (WriteLock, AbsoluteSeek, 0, 0)
|
||||||
case v of
|
case v of
|
||||||
|
|
|
@ -5,20 +5,12 @@
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
|
||||||
{-# LANGUAGE CPP #-}
|
|
||||||
|
|
||||||
module Types.LockPool (
|
module Types.LockPool (
|
||||||
LockPool,
|
LockPool,
|
||||||
LockHandle
|
LockHandle
|
||||||
) where
|
) where
|
||||||
|
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
|
import Utility.LockFile
|
||||||
#ifndef mingw32_HOST_OS
|
|
||||||
import System.Posix.Types (Fd)
|
|
||||||
type LockHandle = Fd
|
|
||||||
#else
|
|
||||||
import Utility.WinLock -- defines LockHandle
|
|
||||||
#endif
|
|
||||||
|
|
||||||
type LockPool = M.Map FilePath LockHandle
|
type LockPool = M.Map FilePath LockHandle
|
||||||
|
|
20
Utility/LockFile.hs
Normal file
20
Utility/LockFile.hs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
{- Lock files
|
||||||
|
-
|
||||||
|
- Posix and Windows lock files are extremely different.
|
||||||
|
- This module does *not* attempt to be a portability shim, it just exposes
|
||||||
|
- the native locking of the OS.
|
||||||
|
-
|
||||||
|
- Copyright 2014 Joey Hess <joey@kitenet.net>
|
||||||
|
-
|
||||||
|
- License: BSD-2-clause
|
||||||
|
-}
|
||||||
|
|
||||||
|
{-# LANGUAGE CPP #-}
|
||||||
|
|
||||||
|
module Utility.LockFile (module X) where
|
||||||
|
|
||||||
|
#ifndef mingw32_HOST_OS
|
||||||
|
import Utility.LockFile.Posix as X
|
||||||
|
#else
|
||||||
|
import Utility.LockFile.Windows as X
|
||||||
|
#endif
|
47
Utility/LockFile/Posix.hs
Normal file
47
Utility/LockFile/Posix.hs
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
{- Posix lock files
|
||||||
|
-
|
||||||
|
- Copyright 2014 Joey Hess <joey@kitenet.net>
|
||||||
|
-
|
||||||
|
- License: BSD-2-clause
|
||||||
|
-}
|
||||||
|
|
||||||
|
module Utility.LockFile.Posix (
|
||||||
|
lockShared,
|
||||||
|
lockExclusive,
|
||||||
|
dropLock,
|
||||||
|
createLockFile,
|
||||||
|
LockHandle
|
||||||
|
) where
|
||||||
|
|
||||||
|
import System.IO
|
||||||
|
import System.Posix
|
||||||
|
|
||||||
|
type LockFile = FilePath
|
||||||
|
|
||||||
|
newtype LockHandle = LockHandle Fd
|
||||||
|
|
||||||
|
-- Takes a shared lock, blocking until the lock is available.
|
||||||
|
lockShared :: Maybe FileMode -> LockFile -> IO LockHandle
|
||||||
|
lockShared = lock ReadLock
|
||||||
|
|
||||||
|
-- Takes an exclusive lock, blocking until the lock is available.
|
||||||
|
lockExclusive :: Maybe FileMode -> LockFile -> IO LockHandle
|
||||||
|
lockExclusive = lock WriteLock
|
||||||
|
|
||||||
|
-- The FileMode is used when creating a new lock file.
|
||||||
|
lock :: LockRequest -> Maybe FileMode -> LockFile -> IO LockHandle
|
||||||
|
lock lockreq mode lockfile = do
|
||||||
|
l <- createLockFile mode lockfile
|
||||||
|
waitToSetLock l (lockreq, AbsoluteSeek, 0, 0)
|
||||||
|
return (LockHandle l)
|
||||||
|
|
||||||
|
-- Create and opens lock file, does not lock it.
|
||||||
|
-- Close on exec flag is set so child processes do not inherit the lock.
|
||||||
|
createLockFile :: Maybe FileMode -> LockFile -> IO Fd
|
||||||
|
createLockFile mode lockfile = do
|
||||||
|
l <- openFd lockfile ReadWrite mode defaultFileFlags
|
||||||
|
setFdOption l CloseOnExec True
|
||||||
|
return l
|
||||||
|
|
||||||
|
dropLock :: LockHandle -> IO ()
|
||||||
|
dropLock (LockHandle fd) = closeFd fd
|
|
@ -5,7 +5,7 @@
|
||||||
- License: BSD-2-clause
|
- License: BSD-2-clause
|
||||||
-}
|
-}
|
||||||
|
|
||||||
module Utility.WinLock (
|
module Utility.LockFile.Windows (
|
||||||
lockShared,
|
lockShared,
|
||||||
lockExclusive,
|
lockExclusive,
|
||||||
dropLock,
|
dropLock,
|
||||||
|
@ -17,9 +17,6 @@ import System.Win32.Types
|
||||||
import System.Win32.File
|
import System.Win32.File
|
||||||
import Control.Concurrent
|
import Control.Concurrent
|
||||||
|
|
||||||
{- Locking is exclusive, and prevents the file from being opened for read
|
|
||||||
- or write by any other process. So for advisory locking of a file, a
|
|
||||||
- different LockFile should be used. -}
|
|
||||||
type LockFile = FilePath
|
type LockFile = FilePath
|
||||||
|
|
||||||
type LockHandle = HANDLE
|
type LockHandle = HANDLE
|
||||||
|
@ -30,7 +27,11 @@ lockShared :: LockFile -> IO (Maybe LockHandle)
|
||||||
lockShared = openLock fILE_SHARE_READ
|
lockShared = openLock fILE_SHARE_READ
|
||||||
|
|
||||||
{- Tries to take an exclusive lock on a file. Fails if another process has
|
{- Tries to take an exclusive lock on a file. Fails if another process has
|
||||||
- a shared or exclusive lock. -}
|
- a shared or exclusive lock.
|
||||||
|
-
|
||||||
|
- Note that exclusive locking also prevents the file from being opened for
|
||||||
|
- read or write by any other progess. So for advisory locking of a file's
|
||||||
|
- content, a different LockFile should be used. -}
|
||||||
lockExclusive :: LockFile -> IO (Maybe LockHandle)
|
lockExclusive :: LockFile -> IO (Maybe LockHandle)
|
||||||
lockExclusive = openLock fILE_SHARE_NONE
|
lockExclusive = openLock fILE_SHARE_NONE
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue