{-# LANGUAGE Safe #-}{-# LANGUAGE BangPatterns #-}------------------------------------------------------------------------------- |-- Module : Control.Concurrent.QSem-- Copyright : (c) The University of Glasgow 2001-- License : BSD-style (see the file libraries/base/LICENSE)-- -- Maintainer : libraries@haskell.org-- Stability : stable-- Portability : non-portable (concurrency)---- Simple quantity semaphores.-------------------------------------------------------------------------------moduleControl.Concurrent.QSem(-- * Simple Quantity SemaphoresQSem ,-- abstractnewQSem ,-- :: Int -> IO QSemwaitQSem ,-- :: QSem -> IO ()signalQSem -- :: QSem -> IO ())whereimportControl.Concurrent.MVar (MVar ,newEmptyMVar ,takeMVar ,tryTakeMVar ,putMVar ,newMVar ,tryPutMVar )importControl.Exception importData.Maybe -- | 'QSem' is a quantity semaphore in which the resource is acquired-- and released in units of one. It provides guaranteed FIFO ordering-- for satisfying blocked `waitQSem` calls.---- The pattern---- > bracket_ waitQSem signalQSem (...)---- is safe; it never loses a unit of the resource.--newtypeQSem =QSem (MVar (Int ,[MVar ()],[MVar ()]))-- The semaphore state (i, xs, ys):---- i is the current resource value---- (xs,ys) is the queue of blocked threads, where the queue is-- given by xs ++ reverse ys. We can enqueue new blocked threads-- by consing onto ys, and dequeue by removing from the head of xs.---- A blocked thread is represented by an empty (MVar ()). To unblock-- the thread, we put () into the MVar.---- A thread can dequeue itself by also putting () into the MVar, which-- it must do if it receives an exception while blocked in waitQSem.-- This means that when unblocking a thread in signalQSem we must-- first check whether the MVar is already full; the MVar lock on the-- semaphore itself resolves race conditions between signalQSem and a-- thread attempting to dequeue itself.-- |Build a new 'QSem' with a supplied initial quantity.-- The initial quantity must be at least 0.newQSem ::Int ->IO QSem newQSem :: Int -> IO QSem
newQSem Int
initial |Int
initial Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
0=String -> IO QSem
forall a. String -> IO a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail String
"newQSem: Initial quantity must be non-negative"|Bool
otherwise =doMVar (Int, [MVar ()], [MVar ()])
sem <-(Int, [MVar ()], [MVar ()])
-> IO (MVar (Int, [MVar ()], [MVar ()]))
forall a. a -> IO (MVar a)
newMVar (Int
initial ,[],[])QSem -> IO QSem
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (MVar (Int, [MVar ()], [MVar ()]) -> QSem
QSem MVar (Int, [MVar ()], [MVar ()])
sem )-- |Wait for a unit to become availablewaitQSem ::QSem ->IO ()waitQSem :: QSem -> IO ()
waitQSem (QSem MVar (Int, [MVar ()], [MVar ()])
m )=IO () -> IO ()
forall a. IO a -> IO a
mask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do(Int
i ,[MVar ()]
b1 ,[MVar ()]
b2 )<-MVar (Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
forall a. MVar a -> IO a
takeMVar MVar (Int, [MVar ()], [MVar ()])
m ifInt
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0thendoMVar ()
b <-IO (MVar ())
forall a. IO (MVar a)
newEmptyMVar MVar (Int, [MVar ()], [MVar ()])
-> (Int, [MVar ()], [MVar ()]) -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Int, [MVar ()], [MVar ()])
m (Int
i ,[MVar ()]
b1 ,MVar ()
b MVar () -> [MVar ()] -> [MVar ()]
forall a. a -> [a] -> [a]
: [MVar ()]
b2 )MVar () -> IO ()
wait MVar ()
b elsedolet!z :: Int
z =Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1MVar (Int, [MVar ()], [MVar ()])
-> (Int, [MVar ()], [MVar ()]) -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Int, [MVar ()], [MVar ()])
m (Int
z ,[MVar ()]
b1 ,[MVar ()]
b2 )() -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()wherewait :: MVar () -> IO ()
wait MVar ()
b =MVar () -> IO ()
forall a. MVar a -> IO a
takeMVar MVar ()
b IO () -> IO () -> IO ()
forall a b. IO a -> IO b -> IO a
`onException` (IO () -> IO ()
forall a. IO a -> IO a
uninterruptibleMask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do-- Note [signal uninterruptible](Int
i ,[MVar ()]
b1 ,[MVar ()]
b2 )<-MVar (Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
forall a. MVar a -> IO a
takeMVar MVar (Int, [MVar ()], [MVar ()])
m Maybe ()
r <-MVar () -> IO (Maybe ())
forall a. MVar a -> IO (Maybe a)
tryTakeMVar MVar ()
b (Int, [MVar ()], [MVar ()])
r' <-ifMaybe () -> Bool
forall a. Maybe a -> Bool
isJust Maybe ()
r then(Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
signal (Int
i ,[MVar ()]
b1 ,[MVar ()]
b2 )elsedoMVar () -> () -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar ()
b ();(Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
i ,[MVar ()]
b1 ,[MVar ()]
b2 )MVar (Int, [MVar ()], [MVar ()])
-> (Int, [MVar ()], [MVar ()]) -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Int, [MVar ()], [MVar ()])
m (Int, [MVar ()], [MVar ()])
r' )-- |Signal that a unit of the 'QSem' is availablesignalQSem ::QSem ->IO ()signalQSem :: QSem -> IO ()
signalQSem (QSem MVar (Int, [MVar ()], [MVar ()])
m )=IO () -> IO ()
forall a. IO a -> IO a
uninterruptibleMask_ (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do-- Note [signal uninterruptible](Int, [MVar ()], [MVar ()])
r <-MVar (Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
forall a. MVar a -> IO a
takeMVar MVar (Int, [MVar ()], [MVar ()])
m (Int, [MVar ()], [MVar ()])
r' <-(Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
signal (Int, [MVar ()], [MVar ()])
r MVar (Int, [MVar ()], [MVar ()])
-> (Int, [MVar ()], [MVar ()]) -> IO ()
forall a. MVar a -> a -> IO ()
putMVar MVar (Int, [MVar ()], [MVar ()])
m (Int, [MVar ()], [MVar ()])
r' -- Note [signal uninterruptible]-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-- If we have---- bracket waitQSem signalQSem (...)---- and an exception arrives at the signalQSem, then we must not lose-- the resource. The signalQSem is masked by bracket, but taking-- the MVar might block, and so it would be interruptible. Hence we-- need an uninterruptibleMask here.---- This isn't ideal: during high contention, some threads won't be-- interruptible. The QSemSTM implementation has better behaviour-- here, but it performs much worse than this one in some-- benchmarks.signal ::(Int ,[MVar ()],[MVar ()])->IO (Int ,[MVar ()],[MVar ()])signal :: (Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
signal (Int
i ,[MVar ()]
a1 ,[MVar ()]
a2 )=ifInt
i Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0then[MVar ()] -> [MVar ()] -> IO (Int, [MVar ()], [MVar ()])
forall {a}.
Num a =>
[MVar ()] -> [MVar ()] -> IO (a, [MVar ()], [MVar ()])
loop [MVar ()]
a1 [MVar ()]
a2 elselet!z :: Int
z =Int
i Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1in(Int, [MVar ()], [MVar ()]) -> IO (Int, [MVar ()], [MVar ()])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int
z ,[MVar ()]
a1 ,[MVar ()]
a2 )whereloop :: [MVar ()] -> [MVar ()] -> IO (a, [MVar ()], [MVar ()])
loop [][]=(a, [MVar ()], [MVar ()]) -> IO (a, [MVar ()], [MVar ()])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (a
1,[],[])loop [][MVar ()]
b2 =[MVar ()] -> [MVar ()] -> IO (a, [MVar ()], [MVar ()])
loop ([MVar ()] -> [MVar ()]
forall a. [a] -> [a]
reverse [MVar ()]
b2 )[]loop (MVar ()
b : [MVar ()]
bs )[MVar ()]
b2 =doBool
r <-MVar () -> () -> IO Bool
forall a. MVar a -> a -> IO Bool
tryPutMVar MVar ()
b ()ifBool
r then(a, [MVar ()], [MVar ()]) -> IO (a, [MVar ()], [MVar ()])
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (a
0,[MVar ()]
bs ,[MVar ()]
b2 )else[MVar ()] -> [MVar ()] -> IO (a, [MVar ()], [MVar ()])
loop [MVar ()]
bs [MVar ()]
b2 

AltStyle によって変換されたページ (->オリジナル) /