module Quantum.Random.Mutex where import Control.Concurrent.MVar (MVar, newMVar, takeMVar, putMVar) import Control.Concurrent (forkIO, ThreadId) -- | A data type to coordinate access to the local files, implemented with an `MVar` @()@. When the -- unit is present, access is available. IO operations that need access to the store or settings -- file remove it from the `MVar` before doing so, and then replace it when they're done. Then -- whenever two such operations might otherwise interfere, they will instead wait their turn to -- obtain the access. This functionality is implemented with 'initAccessControl' and 'withAccess'. -- -- Secondarily, it also contains another `MVar` @()@ used to prevent premature program exit when -- a forked thread is running, implemented as 'forkSafely' and 'exitSafely'. The @qrand@ -- executable uses this to ensure that a concurrent operation to add data from ANU can finish. data AccessControl = AccessControl { accessControl :: MVar (), exitControl :: MVar () } -- | Initiate the access control system. initAccessControl :: IO AccessControl initAccessControl = AccessControl <$> newMVar () <*> newMVar () -- | Perform the supplied IO action only when access is granted. withAccess :: AccessControl -> IO a -> IO a withAccess (AccessControl ac _) io = do _ <- takeMVar ac x <- io putMVar ac () pure x -- | Perform the supplied IO action while preventing premature program exit in conjunction -- with 'exitSafely'. holdExitWhile :: AccessControl -> IO a -> IO a holdExitWhile (AccessControl _ ex) io = do _ <- takeMVar ex x <- io putMVar ex () pure x -- | Perform the supplied IO action in a new thread while preventing premature program exit -- in conjunction with 'exitSafely'. forkSafely :: AccessControl -> IO () -> IO ThreadId forkSafely acc io = forkIO (holdExitWhile acc io) -- | Exit with this operation to ensure a thread forked with 'forkSafely' can finish before -- @main@ returns. exitSafely :: AccessControl -> IO () exitSafely = return . takeMVar =<< exitControl