[Haskell] Re: [Haskell-cafe] SimonPJ and Tim Harris explain STM
tomasz.zielonka at gmail.com
Fri Nov 24 04:02:59 EST 2006
On Fri, Nov 24, 2006 at 08:22:36AM +0000, Simon Peyton-Jones wrote:
> I have also toyed with adding
> retryWith :: IO a -> STM ()
> The idea here is that the transction is undone (i.e. just like the 'retry' combinator), then the specified action is performed, and then the transaction is retried. Again no atomicity guarantee. If there's an orElse involved, both actions would get done.
> Unlike onCommit, onRetry adds new power. Suppose you have a memory
> buffer, with an STM interface:
> getLine :: Buffer -> STM STring
[Sorry for a long email, I had no time to make it short ;-)]
Another example would be my experiments with supporting time in STM, ie.
retryIfBefore :: UTCTime -> STM ()
retryIfAfter :: UTCTime -> STM ()
(Current code can be obtained with "darcs get" from
http://www.uncurry.com/haskell/stm-time/. BTW, shout if you want such a
library - it will motivate me to finally release it officially).
The naive implementation could hold UTCTime in a single TVar and update
this variable in a loop, say, 100 times a second. Of course, with many
threads using retryIfBefore/retryIfAfter this would cause too many
In my implementation I split the time TVar into a list of TVars, each
holding a bit of time representation. The retryIf* functions are written
in such a way, that they retry as soon as they can tell that it's too
early or too late. This way the number of retries for (retryIfBefore
then) is at most the length of suffix of bits that differ in
representation of "now" and "then".
But there is still a problem with accuracy. Ideally, we would like to
be as accurate as possible. One solution goes like this: if
retryIfBefore retries because the time value stored in variables is too
low, let's allow it to notify the manager thread what UTCTime it is
waiting for, so it can schedule to update the variables exactly at this
That's where retryWith would help. Right now I am using something named
autonomously :: Bool -> STM a -> STM ()
This basically forks a new thread to perform the given transaction
outside of the current transaction. To be fair, I am not sure it is
sound - as you can imagine, the implementation uses some dirty tricks
like unsafeIOToSTM. I haven't checked what would happen if I used some
variables created in the surrounding transaction.
BTW, implementing STM.Time was very instructive for me. It made me
realize that I didn't understand STM as well as I thought. Perhaps
it could be made in a nice tutorial, if it wasn't so riddled with
unsafish stuff: unsafeIOToSTM mentioned above, and unsafePerformIO
used to initialize a top-level variable *and* spawn a manager thread.
Here retryWith could also help - I fork the manager thread with it.
> I have not implemented either of these, but I think they'd be cool.
I agree especially about retryWith. But I think it's name should include
a "danger! sign", because when used wrong, it can "break" the nice
properties of STM and cause very surprising bugs. For me one good
"danger" indicator is "IO", so perhaps "retryWithIO" ?
More information about the Haskell-Cafe