Haskell STM Retry
Retry restarts a transaction until a box it read changes. It lets a transaction wait for the value it needs.
A worker waits until a flag turns true
A worker retries while the flag is False. The main thread sets it to True, then takes the result.
import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)
import Control.Concurrent.STM
import Control.Monad (when)
main :: IO ()
main = do
flag <- newTVarIO False
box <- newEmptyMVar
_ <- forkIO $ do
atomically $ do
f <- readTVar flag
when (not f) retry
putMVar box "ready"
atomically $ writeTVar flag True
result <- takeMVar box
putStrLn resultA worker waits until a count reaches three
A worker retries while the count is below 3. The main thread writes 3, then takes the result.
import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)
import Control.Concurrent.STM
import Control.Monad (when)
main :: IO ()
main = do
count <- newTVarIO (0 :: Int)
box <- newEmptyMVar
_ <- forkIO $ do
result <- atomically $ do
n <- readTVar count
when (n < 3) retry
return n
putMVar box result
atomically $ writeTVar count 3
final <- takeMVar box
print finalA worker waits until a value equals seven
A worker retries while the value is not 7. The main thread writes 7, then takes the result.
import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)
import Control.Concurrent.STM
import Control.Monad (when)
main :: IO ()
main = do
value <- newTVarIO (0 :: Int)
box <- newEmptyMVar
_ <- forkIO $ do
result <- atomically $ do
n <- readTVar value
when (n /= 7) retry
return n
putMVar box result
atomically $ writeTVar value 7
final <- takeMVar box
print finalExample
import Control.Concurrent (forkIO, newEmptyMVar, putMVar, takeMVar)
import Control.Concurrent.STM
import Control.Monad (when)
main :: IO ()
main = do
flag <- newTVarIO False
count <- newTVarIO (0 :: Int)
box <- newEmptyMVar
_ <- forkIO $ do
result <- atomically $ do
f <- readTVar flag
c <- readTVar count
when (not f || c < 5) retry
return (f, c)
putMVar box result
atomically $ do
writeTVar flag True
writeTVar count 5
final <- takeMVar box
print finalKey points
- Retry: retry restarts a transaction until a box it read changes.
- Waiting transaction: a transaction that calls retry waits instead of finishing with a wrong value.
- Changed box: retry wakes only when a box the transaction read gets a new value.
- Main thread change: the main thread changes the watched box, and the waiting transaction then runs again.
- Rendezvous: the main thread takes the worker result from an MVar after the worker finishes.
In this example, a worker waits for a flag and a count together. The main thread sets the flag to True and the count to 5 in one transaction. The worker then puts the pair into an MVar. The main thread takes it and prints True and 5.
Note
retry belongs inside atomically. It never belongs in ordinary IO code.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Haskell Playground
Exercise
When does a transaction that called retry run again?