We are given the following API interface in 8th chapter of “Parallel and Concurrent Programming in Haskell” text,
data Async a = Async (MVar (Either SomeException a))
async :: IO a -> IO (Async a)
async action = do
var <- newEmptyMVar
forkIO (do r <- try action; putMVar var r)
return (Async var)
readMVar :: MVar a -> IO a
readMVar m = do
a <- takeMVar m
putMVar m a
return a
waitCatch :: Async a -> IO (Either SomeException a)
waitCatch (Async var) = readMVar var
wait :: Async a -> IO a
wait a = do
r <- waitCatch a
case r of
Left e -> throwIO e
Right a -> return a
Now if we define,
waitEither :: Async a -> Async b -> IO (Either a b)
waitEither a b = do
m <- newEmptyMVar
forkIO $ do r <- try (fmap Left (wait a)); putMVar m r
forkIO $ do r <- try (fmap Right (wait b)); putMVar m r
wait (Async m)
Couldn’t this lead to an issue where the putMVar
of line forkIO $ do r <- try (fmap Right (wait b)); putMVar m r
executes before the putMVar
present inside readMVar
as called by implementation of wait
? For simplicity, let us assume that there are no asynchronous exceptions to worry about.
Haddock of official implementation readMVar
suggest that it will receive the next call of putMVar
, do we want this assumption for our example?