RE: The Revenge of Finalizers
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
Simon Marlow wrote: [snip]
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.
No. At the time of the seq in thread 2, the thunk (call it Thunk 2) returned by the second atomicModifyIORef will refer to the thunk (Thunk 1) put in there by the first atomicModifyIORef. The seq will cause the unsafePerformIO from the second atomicModifyIORef to be evaluated. This will come to the "if" statement which will demand the value of Thunk 1. This will in turn provoke the evaluation of Thunk1. This will cause the action of thread 1's simpleToggle2 to be evaluated to completion. Sadly there does seem to be a problem (with GHC) if Thread 1 is asynchronously interrupted while the unsafePerformIO is being done. I'm afraid there isn't any way of avoiding this I can think of excepting putting a blockAsynchronousExceptions or whatever it's called around the seq. Still, at least that would work, because since atomicModifyIORef does not block, there is no reason for it not to. For the equivalent solution with MVars it does not work, and you need a more complicated arrangement of blocks and unblocks.
participants (2)
-
George Russell -
Simon Marlow