Haskell by Example: Stateful Goroutines
original{-# LANGUAGE GADTs #-}
import Control.Monad
import Control.Concurrent
import Control.Concurrent.STM
import System.Random
import Data.IORef
import qualified Data.Map as Map
data ReadOp = ReadOp { readKey :: Int
, readResp :: MVar Int
}
data WriteOp = WriteOp { writeKey :: Int
, writeVal :: Int
, writeResp :: MVar Bool
}
main = do
ops <- atomically $ newTVar 0
reads <- newEmptyMVar
writes <- newEmptyMVar
forkIO $ do
state <- newIORef Map.empty
forever $ do
select [ Case reads $ \read -> do
s <- readIORef state
let val = maybe 0 id $ Map.lookup (readKey read) s
putMVar (readResp read) val
, Case writes $ \write -> do
modifyIORef state (Map.insert (writeKey write) (writeVal write))
putMVar (writeResp write) True
]
forM_ [0..99] $ \_ -> do
forkIO . forever $ do
key <- randomRIO (0,4)
resp <- newEmptyMVar
let read = ReadOp { readKey = key, readResp = resp }
putMVar reads read
takeMVar resp
atomically $ do
x <- readTVar ops
writeTVar ops (x + 1)
forM_ [0..9] $ \_ -> do
forkIO . forever $ do
key <- randomRIO (0,4)
val <- randomRIO (0,99)
resp <- newEmptyMVar
let write = WriteOp { writeKey = key, writeVal = val, writeResp = resp }
putMVar writes write
takeMVar resp
atomically $ do
x <- readTVar ops
writeTVar ops (x + 1)
threadDelay 1000000
opsFinal <- atomically $ readTVar ops
putStrLn $ "ops: " ++ show opsFinal
data Select a where
Default :: IO a -> Select a
Case :: MVar b -> (b -> IO a) -> Select a
select :: [Select a] -> IO a
select [] = error "select: empty list"
select ((Default x):_) = x
select (x@(Case v f):xs) = do
var <- tryTakeMVar v
case var of
Just b -> f b
Nothing -> select (xs ++ [x])
$ runhaskell stateful-goroutines.hs
ops: 11605
back to index