[Haskell-cafe] STM atomic blocks in IO functions

wren ng thornton wren at freegeek.org
Sat Jan 14 23:12:02 CET 2012


On 1/14/12 2:24 PM, Rob Stewart wrote:
> Are IO functions permissible in STM atomically blocks?

They are not. The semantics of STM are that each transaction is retried 
until it succeeds, and that the number of times it is retried does not 
affect the program output. Thus, you can only do things in STM which can 
be reverted, since you may have to undo the side-effects whenever the 
transaction is retried.

However, if you're interested in pursuing this approach, you should take 
a look at TwilightSTM which expands the interaction possibilities 
between IO and STM.

> If so how? If
> not, how would one get around a problem of having to use an IO
> function to retrieve a value that is to be written to a TVar ?

If you truly must do IO in the middle of a transaction, the typical 
solution is to use a locking mechanism. For example, you use a TMVar() 
as the lock: taking the () token in order to prevent other threads from 
doing the IO; doing the IO; and then putting the () token back. Thus, 
something like:

     do  ...
         atomically $ do
             ...
             () <- takeTMVar lockRunFoo
         x <- runFoo
         atomically $ do
             putTMVar lockRunFoo ()
             ...x...
         ...

However, it's often possible to factor the IO out of the original 
transaction, so you should do so whenever you can. An unfortunate 
downside of the above locking hack is that the STM state is not 
guaranteed to be consistent across the two transactions. You can fake 
read-consistency by moving reads into the first transaction in order to 
bind the values to local variables, as in:

     do  ...
         (a,b,c) <- atomically $ do
             ...
             a <- ...
             ...
             b <- ...
             ...
             c <- ...
             ...
             () <- takeTMVar lockRunFoo
             return (a,b,c)
         x <- runFoo
         atomically $ do
             putTMVar lockRunFoo ()
             ...x...a...b...c...
         ...

And you can fake write-consistency by moving writes into the second 
transaction to ensure that they all are committed at once. However, you 
can't use those tricks if you have a complicated back and forth with 
reading and writing.

-- 
Live well,
~wren



More information about the Haskell-Cafe mailing list