Sorry if you don't want to be bothered with my problems, but I think this
problem which I've just encountered is rather amusing.  Is there a neat solution?
I confess to finding concurrency problems difficult so there might be.

I want to implement a type Flag.  Each flag is either Up or Down.  When you
create a flag (there is a newFlag :: IO Flag operation) it is Up.  The only
operation permitted on Flags is

lowerFlags :: Flag -> Flag -> IO Bool

If both arguments are Up, lowerFlags should lower them both and return
True, otherwise it should leave them as they are and return False.
The problem is that lowerFlags should appear to be atomic, so that
if lowerFlags is called simultaneously in different threads, the result
should be the same as if one of the calls completed before the other started.

So for example the following code
   type Flag = MVar Bool -- True means Up

   newFlag = newMVar True

   lowerFlags flag1 flag2 =
      if (flag1 == flag2)
      then
         error "Illegal call to lowerFlags" -- this is not allowed
      else
         do
            val1 <- takeMVar flag1
            -- point A
            val2 <- takeMVar flag2
            let success = (val1 && val2)
            putMVar flag2 (not success && val2)
            putMVar flag1 (not success && val1)
won't work, because if lowerFlags is called simultaneously with the same arguments
but in a different order, and both calls reach point A simultanously, then both
threads will block when they attempt the takeMVar in the next line.

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?

Reply via email to