simpleToggle2 :: IORef Bool -> IORef Bool -> IO (Just (Bool,Bool))
which attempts to flip the two IORefs from True to False (if they are both True); otherwise returning their actual values. Then you could code this something like this (no I'm not going to check if it passes GHC)
simpleToggle2 ioRef1 ioRef2 = do res <- atomicModifyIORef ioRef1 (\ contents1 -> unsafePerformIO (atomicModifyIORef ioRef2 (\ contents2 -> if contents1 && contents2 then (False,(False,Nothing)) else (contents2,(contents1,Just (contents1,contents2))) ) ) seq res (return res)
Then the first atomicModifyIORef replaces the contents of ioRef1 by a thunk, and returns another thunk. The seq then proceeds to evaluate this thunk, causing the unsafePerformIO to be run, which changes ioRef2.
There IS a potential problem, because if you run simpleToggle2 on the same ioRef
simpleToggle2 ioRef ioRef
or if two threads run simpleToggle2 simultaneously on the same ioRefs but in opposite order
simpleToggle2 ioRef1 ioRef2 || simpleToggle2 ioRef2 ioRef1
Don't you run into a problem even if the two threads use the same ordering? Suppose - thread 1 does the atomicModifyIORef, and gets preempted before doing the seq - thread 2 does its own atomicModifyIORef, and the seq. Thread 2 gets an inconsistent view of the IORefs. right? Simon