ngx-export-1.7.10.2: NgxExport/Internal/SafeFileLock.hsc
{-# LANGUAGE CPP, ForeignFunctionInterface, InterruptibleFFI #-}
module NgxExport.Internal.SafeFileLock (safeWaitToSetLock
,getBestLockImpl
) where
import Foreign.C
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Alloc
import System.Posix.IO
import System.Posix.Types
import System.Posix.Internals
import GHC.IO.Device
#include <HsFFI.h>
#ifndef HAVE_FCNTL_H
#error HsFFI claims that required C header file fcntl.h is missing
#endif
#include <fcntl.h>
fcntlOfdSetlkw :: CInt
#ifdef F_OFD_SETLKW
fcntlOfdSetlkw = #const F_OFD_SETLKW
#else
fcntlOfdSetlkw = 38
#endif
fcntlOfdGetlk :: CInt
#ifdef F_OFD_SETLKW
fcntlOfdGetlk = #const F_OFD_GETLK
#else
fcntlOfdGetlk = 36
#endif
fcntlSetlkw :: CInt
fcntlSetlkw = #const F_SETLKW
-- functions below were mostly adopted from System.Posix.IO.Common
mode2CShort :: SeekMode -> CShort
mode2CShort AbsoluteSeek = #const SEEK_SET
mode2CShort RelativeSeek = #const SEEK_CUR
mode2CShort SeekFromEnd = #const SEEK_END
lockReq2CShort :: LockRequest -> CShort
lockReq2CShort ReadLock = #const F_RDLCK
lockReq2CShort WriteLock = #const F_WRLCK
lockReq2CShort Unlock = #const F_UNLCK
allocaLock :: FileLock -> (Ptr CFLock -> IO a) -> IO a
allocaLock (lockreq, mode, start, len) io =
allocaBytes (#size struct flock) $ \p -> do
(#poke struct flock, l_type) p $ lockReq2CShort lockreq
(#poke struct flock, l_whence) p $ mode2CShort mode
(#poke struct flock, l_start) p start
(#poke struct flock, l_len) p len
io p
writeLock :: FileLock
writeLock = (WriteLock, AbsoluteSeek, 0, 0)
foreign import ccall interruptible "HsBase.h fcntl"
safe_c_fcntl_lock :: CInt -> CInt -> Ptr CFLock -> IO CInt
-- interruptible version of waitToSetLock as defined in System.Posix.IO
safeWaitToSetLock :: Fd -> CInt -> IO ()
safeWaitToSetLock (Fd fd) cmd = allocaLock writeLock $
throwErrnoIfMinus1_ "safeWaitToSetLock" . safe_c_fcntl_lock fd cmd
-- returns fcntlOfdSetlkw if OFD locks are available, or fcntlSetlkw otherwise
getBestLockImpl :: Fd -> IO CInt
getBestLockImpl (Fd fd) = allocaLock writeLock $ \pFlock -> do
res <- c_fcntl_lock fd fcntlOfdGetlk pFlock
if res == -1
then do
errno <- getErrno
return $ if errno == eINVAL
then fcntlSetlkw
else fcntlOfdSetlkw
else return fcntlOfdSetlkw