summaryrefslogtreecommitdiff
path: root/Utility/LockFile
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2014-08-20 16:45:58 -0400
committerGravatar Joey Hess <joey@kitenet.net>2014-08-20 16:45:58 -0400
commit679d9c0027ac996eea9f41a6f0f39af436801e89 (patch)
treed98ae7ba049e2daa4a99e6bcf55054bde5e5ab4d /Utility/LockFile
parent86f9d7734d0c638d4c966fd87a5d7e97759e125b (diff)
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.
Diffstat (limited to 'Utility/LockFile')
-rw-r--r--Utility/LockFile/Posix.hs47
-rw-r--r--Utility/LockFile/Windows.hs75
2 files changed, 122 insertions, 0 deletions
diff --git a/Utility/LockFile/Posix.hs b/Utility/LockFile/Posix.hs
new file mode 100644
index 000000000..1538b491a
--- /dev/null
+++ b/Utility/LockFile/Posix.hs
@@ -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
diff --git a/Utility/LockFile/Windows.hs b/Utility/LockFile/Windows.hs
new file mode 100644
index 000000000..73c248b03
--- /dev/null
+++ b/Utility/LockFile/Windows.hs
@@ -0,0 +1,75 @@
+{- Windows lock files
+ -
+ - Copyright 2014 Joey Hess <joey@kitenet.net>
+ -
+ - License: BSD-2-clause
+ -}
+
+module Utility.LockFile.Windows (
+ lockShared,
+ lockExclusive,
+ dropLock,
+ waitToLock,
+ LockHandle
+) where
+
+import System.Win32.Types
+import System.Win32.File
+import Control.Concurrent
+
+type LockFile = FilePath
+
+type LockHandle = HANDLE
+
+{- Tries to lock a file with a shared lock, which allows other processes to
+ - also lock it shared. Fails is the file is exclusively locked. -}
+lockShared :: LockFile -> IO (Maybe LockHandle)
+lockShared = openLock fILE_SHARE_READ
+
+{- Tries to take an exclusive lock on a file. Fails if another process has
+ - 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 = openLock fILE_SHARE_NONE
+
+{- Windows considers just opening a file enough to lock it. This will
+ - create the LockFile if it does not already exist.
+ -
+ - Will fail if the file is already open with an incompatable ShareMode.
+ - Note that this may happen if an unrelated process, such as a virus
+ - scanner, even looks at the file. See http://support.microsoft.com/kb/316609
+ -
+ - Note that createFile busy-waits to try to avoid failing when some other
+ - process briefly has a file open. But that would make checking locks
+ - much more expensive, so is not done here. Thus, the use of c_CreateFile.
+ -
+ - Also, passing Nothing for SECURITY_ATTRIBUTES ensures that the lock file
+ - is not inheerited by any child process.
+ -}
+openLock :: ShareMode -> LockFile -> IO (Maybe LockHandle)
+openLock sharemode f = do
+ h <- withTString f $ \c_f ->
+ c_CreateFile c_f gENERIC_READ sharemode security_attributes
+ oPEN_ALWAYS fILE_ATTRIBUTE_NORMAL (maybePtr Nothing)
+ return $ if h == iNVALID_HANDLE_VALUE
+ then Nothing
+ else Just h
+ where
+ security_attributes = maybePtr Nothing
+
+dropLock :: LockHandle -> IO ()
+dropLock = closeHandle
+
+{- If the initial lock fails, this is a BUSY wait, and does not
+ - guarentee FIFO order of waiters. In other news, Windows is a POS. -}
+waitToLock :: IO (Maybe LockHandle) -> IO LockHandle
+waitToLock locker = takelock
+ where
+ takelock = go =<< locker
+ go (Just lck) = return lck
+ go Nothing = do
+ threadDelay (500000) -- half a second
+ takelock