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

Reply via email to