{-# LANGUAGE BangPatterns, DeriveDataTypeable, NoImplicitPrelude, UnicodeSyntax #-} -------------------------------------------------------------------------------- -- | -- Module : Control.Concurrent.RLock -- Copyright : (c) 2010 Bas van Dijk & Roel van Dijk -- License : BSD3 (see the file LICENSE) -- Maintainer : Bas van Dijk -- , Roel van Dijk -- -- This module provides the 'RLock' synchronization mechanism. It was inspired -- by the Python @RLock@ and Java @ReentrantLock@ objects and should behave in a -- similar way. See: -- -- -- -- and: -- -- -- -- All functions are /exception safe/. Throwing asynchronous exceptions will not -- compromise the internal state of a 'RLock'. -- -- This module is intended to be imported qualified. We suggest importing it like: -- -- @ -- import Control.Concurrent.RLock ( RLock ) -- import qualified Control.Concurrent.RLock as RLock ( ... ) -- @ -- -------------------------------------------------------------------------------- module Control.Concurrent.RLock ( RLock -- * Creating reentrant locks , new , newAcquired -- * Locking and unlocking , acquire , tryAcquire , release -- * Convenience functions , with , tryWith -- * Querying reentrant locks , recursionLevel ) where -------------------------------------------------------------------------------- -- Imports -------------------------------------------------------------------------------- -- from base: import Control.Applicative ( (<$>), liftA2 ) import Control.Concurrent ( ThreadId, myThreadId ) import Control.Concurrent.MVar ( MVar, newMVar, takeMVar, readMVar, putMVar ) import Control.Exception ( block, bracket_, finally ) import Control.Monad ( Monad, return, (>>=), fail, (>>), fmap ) import Data.Bool ( Bool(False, True), otherwise ) import Data.Eq ( Eq ) import Data.Function ( ($) ) import Data.Maybe ( Maybe(Nothing, Just), maybe ) import Data.Tuple ( fst, snd ) import Data.Typeable ( Typeable ) import Prelude ( Integer, fromInteger, succ, pred, error ) import System.IO ( IO ) -- from base-unicode-symbols import Data.Eq.Unicode ( (≡) ) import Data.Function.Unicode ( (∘) ) import Data.Monoid.Unicode ( (⊕) ) -- from ourselves: import Control.Concurrent.Lock ( Lock ) import qualified Control.Concurrent.Lock as Lock ( new, newAcquired, acquire, release ) -------------------------------------------------------------------------------- -- Reentrant locks -------------------------------------------------------------------------------- newtype RLock = RLock {un ∷ MVar (Maybe (ThreadId, Integer), Lock)} deriving (Eq, Typeable) new ∷ IO RLock new = do lock ← Lock.new RLock <$> newMVar (Nothing, lock) newAcquired ∷ IO RLock newAcquired = do myTID ← myThreadId lock ← Lock.newAcquired RLock <$> newMVar (Just (myTID, 1), lock) acquire ∷ RLock → IO () acquire (RLock mv) = do myTID ← myThreadId block $ let go = do t@(mb, lock) ← takeMVar mv case mb of Nothing → do Lock.acquire lock putMVar mv (Just (myTID, 1), lock) Just (tid, n) | myTID ≡ tid → let !sn = succ n in putMVar mv (Just (tid, sn), lock) | otherwise → do putMVar mv t Lock.acquire lock Lock.release lock go in go tryAcquire ∷ RLock → IO Bool tryAcquire (RLock mv) = do myTID ← myThreadId block $ do t@(mb, lock) ← takeMVar mv case mb of Nothing → do Lock.acquire lock putMVar mv (Just (myTID, 1), lock) return True Just (tid, n) | myTID ≡ tid → do let !sn = succ n putMVar mv (Just (tid, sn), lock) return True | otherwise → do putMVar mv t return False release ∷ RLock → IO () release (RLock mv) = do myTID ← myThreadId block $ do t@(mb, lock) ← takeMVar mv let err msg = do putMVar mv t error $ "Control.Concurrent.RLock.release: " ⊕ msg case mb of Nothing → err "Can't release an unacquired RLock!" Just (tid, n) | myTID ≡ tid → if n ≡ 1 then do Lock.release lock putMVar mv (Nothing, lock) else let !pn = pred n in putMVar mv (Just (tid, pn), lock) | otherwise → err "Calling thread does not own the RLock!" with ∷ RLock → IO α → IO α with = liftA2 bracket_ acquire release tryWith ∷ RLock → IO α → IO (Maybe α) tryWith l a = block $ do acquired ← tryAcquire l if acquired then fmap Just $ a `finally` release l else return Nothing recursionLevel ∷ RLock → IO Integer recursionLevel = fmap (maybe 0 snd ∘ fst) ∘ readMVar ∘ un -- The End ---------------------------------------------------------------------