{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE NamedFieldPuns #-}
module Capnp.Rpc.Promise
( Promise
, Fulfiller
, newPromise
, newPromiseWithCallback
, newCallback
, fulfill
, breakPromise
, breakOrFulfill
, ErrAlreadyResolved(..)
, wait
) where
import Control.Concurrent.STM
import Control.Monad.STM.Class
import qualified Control.Exception.Safe as HsExn
import Capnp.Gen.Capnp.Rpc.Pure
import Capnp.Rpc.Errors ()
data ErrAlreadyResolved = ErrAlreadyResolved deriving(Show)
instance HsExn.Exception ErrAlreadyResolved
newtype Fulfiller a = Fulfiller
{ callback :: Either Exception a -> STM ()
}
fulfill :: MonadSTM m => Fulfiller a -> a -> m ()
fulfill f val = breakOrFulfill f (Right val)
breakPromise :: MonadSTM m => Fulfiller a -> Exception -> m ()
breakPromise f exn = breakOrFulfill f (Left exn)
breakOrFulfill :: MonadSTM m => Fulfiller a -> Either Exception a -> m ()
breakOrFulfill Fulfiller{callback} result = liftSTM $ callback result
wait :: MonadSTM m => Promise a -> m a
wait Promise{var} = liftSTM $ do
val <- readTVar var
case val of
Nothing ->
retry
Just (Right result) ->
pure result
Just (Left exn) ->
throwSTM exn
newPromise :: MonadSTM m => m (Promise a, Fulfiller a)
newPromise = liftSTM $ do
var <- newTVar Nothing
pure
( Promise{var}
, Fulfiller
{ callback = \result -> do
val <- readTVar var
case val of
Nothing ->
writeTVar var (Just result)
Just _ ->
throwSTM ErrAlreadyResolved
}
)
newPromiseWithCallback :: MonadSTM m => (Either Exception a -> STM ()) -> m (Promise a, Fulfiller a)
newPromiseWithCallback callback = liftSTM $ do
(promise, Fulfiller{callback=oldCallback}) <- newPromise
pure
( promise
, Fulfiller
{ callback = \result -> oldCallback result >> callback result
}
)
newCallback :: MonadSTM m => (Either Exception a -> STM ()) -> m (Fulfiller a)
newCallback = liftSTM . fmap snd . newPromiseWithCallback
newtype Promise a = Promise
{ var :: TVar (Maybe (Either Exception a))
}
deriving(Eq)