From dbd4fdbb694d866359da5cf386147c17ce4c9246 Mon Sep 17 00:00:00 2001 From: Jens Nolte <git@queezle.net> Date: Tue, 24 Aug 2021 01:14:10 +0200 Subject: [PATCH] Implement awaitAny Co-authored-by: Jan Beinke <git@janbeinke.com> --- quasar.cabal | 1 + src/Quasar/Awaitable.hs | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/quasar.cabal b/quasar.cabal index 16f256a..07e78f5 100644 --- a/quasar.cabal +++ b/quasar.cabal @@ -67,6 +67,7 @@ library build-depends: base >=4.7 && <5, binary, + containers, exceptions, ghc-prim, hashable, diff --git a/src/Quasar/Awaitable.hs b/src/Quasar/Awaitable.hs index e0f3941..e9ee836 100644 --- a/src/Quasar/Awaitable.hs +++ b/src/Quasar/Awaitable.hs @@ -14,6 +14,7 @@ module Quasar.Awaitable ( -- * Awaiting multiple awaitables cacheAwaitable, awaitEither, + awaitAny, -- * AsyncVar AsyncVar, @@ -33,6 +34,9 @@ import Control.Concurrent.STM import Control.Monad.Catch import Control.Monad.Reader import Control.Monad.Trans.Maybe +import Data.List.NonEmpty (NonEmpty(..), nonEmpty) +import Data.Foldable (toList) +import Data.Sequence import Quasar.Prelude @@ -206,10 +210,7 @@ putAsyncVarEitherSTM_ var = void . putAsyncVarEitherSTM var -- * Awaiting multiple asyncs awaitEither :: (IsAwaitable ra a , IsAwaitable rb b, MonadIO m) => a -> b -> m (Awaitable (Either ra rb)) -awaitEither x y = liftIO $ do - let startX = runAwaitable x - let startY = runAwaitable y - pure $ Awaitable $ groupLefts <$> stepBoth startX startY +awaitEither x y = pure $ Awaitable $ groupLefts <$> stepBoth (runAwaitable x) (runAwaitable y) where stepBoth :: MonadQuerySTM m => AwaitableStepM ra -> AwaitableStepM rb -> m (Either ra rb) stepBoth (AwaitableCompleted resultX) _ = pure $ Left resultX @@ -220,6 +221,26 @@ awaitEither x y = liftIO $ do Right resultY -> stepBoth stepX (nextY resultY) +awaitAny :: (IsAwaitable r a, MonadIO m) => NonEmpty a -> m (Awaitable r) +awaitAny xs = pure $ Awaitable $ stepAll Empty Empty $ runAwaitable <$> fromList (toList xs) + where + stepAll + :: MonadQuerySTM m + => Seq (STM (Maybe (Seq (AwaitableStepM r)))) + -> Seq (AwaitableStepM r) + -> Seq (AwaitableStepM r) + -> m r + stepAll _ _ (AwaitableCompleted result :<| _) = pure result + stepAll acc prevSteps (step@(AwaitableStep transaction next) :<| steps) = + stepAll + do acc |> ((\result -> (prevSteps |> next result) <> steps) <<$>> transaction) + do prevSteps |> step + steps + stepAll acc ps Empty = do + newAwaitableSteps <- querySTM $ maybe impossibleCodePathM peekAnySTM $ nonEmpty (toList acc) + stepAll Empty Empty newAwaitableSteps + + groupLefts :: Either (Either ex a) (Either ex b) -> Either ex (Either a b) groupLefts (Left x) = Left <$> x groupLefts (Right y) = Right <$> y @@ -231,3 +252,9 @@ peekEitherSTM x y = Nothing -> y >>= \case Just r -> pure (Just (Right r)) Nothing -> pure Nothing + + +peekAnySTM :: NonEmpty (STM (Maybe a)) -> STM (Maybe a) +peekAnySTM (x :| xs) = x >>= \case + r@(Just _) -> pure r + Nothing -> maybe (pure Nothing) peekAnySTM (nonEmpty xs) -- GitLab