Software Transactional Memory
Run an STM transaction in IO Block until a read TVar changes
Try an alternative if the first transaction retries TMVar, TChan, TQueue, TBQueue
Transactional MVar, channels, queues (stm package) check b = if b then pure () else retry
STM transactions are composable (unlike locks), deadlock-free, and automatically retried on conflict. You can't do IO inside STM; the type system prevents it.
import Control.Concurrent.STM
import Control.Concurrent (forkIO)
import Control.Monad
type Account = TVar Int
transfer :: Account -> Account -> Int -> STM ()
transfer from to amount = do
balance <- readTVar from
when (balance < amount) retry -- block until the balance changes, then retry
modifyTVar' from (subtract amount)
modifyTVar' to (+ amount)
-- Composable alternatives
transferOrFail :: Account -> Account -> Int -> STM Bool
transferOrFail from to amount =
(transfer from to amount >> pure True) `orElse` pure False
main :: IO ()
main = do
alice <- newTVarIO 100
bob <- newTVarIO 0
atomically (transfer alice bob 30) -- runs atomically: all or nothing
ok <- atomically (transferOrFail alice bob 500)
print ok -- False
balances <- atomically ((,) <$> readTVar alice <*> readTVar bob)
print balances -- (70,30)
-- A blocking transfer that completes when funds arrive
_ <- forkIO $ atomically (transfer bob alice 50) -- waits: bob has only 30
atomically (modifyTVar' bob (+ 100)) -- now the waiting transfer can proceed