
Eureka, I claim to have written an implementation which agrees with all the semantics that Simon Peyton-Jones wants for onCommit/onRetry/retryWith. See below: Simon Peyton-Jones wrote:
| In many useful cases, such as the getLine example, the Y action will have its | own atomic {} block. In which case the semantics of when it is allowed to | re-attempt X are what is important. If you require (Y) to complete before | re-attempting (X) then you get an infinite regression where every (atomic block) | fails with (retryWith (next atomic block)), and nothing is ever re-attempted. | This is why "retryWith Y" meaning rollback X and do "Y >> atomic X" is the wrong | implementation.
I don't agree. I think it's quite reasonable. Not many atomic blocks will finish with retryWith. Of course there is a possibility of an infinite loop, but we already have that: f x = f x. Of course, Y can always choose to do a forkIO, but it shouldn't hav to.
For me the only difficulty is the implementation. We'd like to block X on the TVars it read (as usual), *unless* executing Y wrote to any of them. That requires a bit more cleverness in the commit code, but not a great deal I think.
Simon
It is the Helper Thread code version on the wiki at http://haskell.org/haskellwiki/New_monads/MonadAdvSTM#Helper_Thread_Code Quick explanation of the code for runAdvSTM (usually called with atomicAdv): When the action X in (atomicAdv X) ends with (retryWith Y) the job Y is put into an MVar. Then a retry causes the orElse in wrappedAction to perform check'retry. This sees the job Y and then *) if this is the first retry job: creates and cache a channel and spawn the helper thread *) push the retry job Y into the channel *) call retry to cause action X to cause the current GHC runtime to block on whatever STM-variables it used The wrappedAction commits if and only if the action X commits. In which case the commit action stored in the TVar is read and performed. Then a check is performed to see if the helper thread was spawned, and if so tell the helper thread to quit and block until the helper thread is done. Note that the action X can be re-attempted by the runtime before the retry job Y is run or before it has finished running. But this will only happen in the usual cases where there was an STM update, instead of the possible busy wait in the Single Thread code example on the wiki page. Does this meet your specifications, Simon? -- Chris