George Russell writes:
> [...]
>
> What is the neatest solution? There is an obvious solution, which
> is to crudely sequence all calls to lowerFlags by making them lock
> a single global variable (created using, sigh, unsafePerformIO) but
> this doesn't seem very elegant. If MVar's were instances of Ord as
> well as Eq, a neat solution would be to always get the least MVar
> first, but they aren't. So what should one do?
Hi.
How about introducing a third flag state, which counts as "up" but
also indicates that a thread intends to retake it and update it again?
A thread would only ever update a flag from the intermediate state, if
it was the thread which last updated that flag into the intermediate
state.
Something along these lines:
import ConcBase
data FlagState = Down | Sagging | Up deriving (Eq)
isUp = (/=) Down -- For the purposes of everything except lowerFlags
type Flag = MVar FlagState
newFlag = newMVar Up
lowerFlags flag1 flag2 =
if flag1 == flag2 then error "Illegal call to lowerFlags" else do
wasDown1 <- sag flag1
if wasDown1 then return () else do
wasDown2 <- sag flag2
_ <- takeMVar flag1 -- We know flag1 is now sagging
if wasDown2 then putMVar flag1 Up else do
_ <- takeMVar flag2 -- We know flag2 is now sagging
putMVar flag2 Down
putMVar flag1 Down
where
-- Update Up to Sagging, update Down to Down, busy wait (aargh!)
-- if already Sagging. Return whether f was down.
sag f = do
fs <- takeMVar f
case fs of
Up -> putMVar f Sagging >> return False
Down -> putMVar f Down >> return True
Sagging -> putMVar f Sagging >> sag f
Regards,
Tom