packages feed

polysemy-conc 0.1.1.0 → 0.2.0.0

raw patch · 26 files changed

+640/−304 lines, 26 filesdep −hedgehogdep −tasty-hedgehogPVP ok

version bump matches the API change (PVP)

Dependencies removed: hedgehog, tasty-hedgehog

API changes (from Hackage documentation)

- Polysemy.Conc.Queue: interpretQueueListReadOnlyAtomic :: forall d r. Member (Embed IO) r => [d] -> InterpreterFor (Queue d) r
- Polysemy.Conc.Queue: interpretQueueListReadOnlyAtomicWith :: forall d r. Member (AtomicState [d]) r => InterpreterFor (Queue d) r
- Polysemy.Conc.Queue: interpretQueueListReadOnlyState :: forall d r. Member (Embed IO) r => [d] -> InterpreterFor (Queue d) r
- Polysemy.Conc.Queue: interpretQueueListReadOnlyStateWith :: forall d r. Member (State [d]) r => InterpreterFor (Queue d) r
- Polysemy.Conc.Queue.TB: interpretQueueTB :: forall d r. Members [Race, Embed IO] r => Natural -> InterpreterFor (Queue d) r
- Polysemy.Conc.Queue.TB: interpretQueueTBWith :: forall d r. Members [Race, Embed IO] r => TBQueue d -> InterpreterFor (Queue d) r
- Polysemy.Conc.Queue.TBM: interpretQueueTBM :: forall d r. Members [Resource, Race, Embed IO] r => Int -> InterpreterFor (Queue d) r
- Polysemy.Conc.Queue.TBM: interpretQueueTBMWith :: forall d r. Members [Race, Embed IO] r => TBMQueue d -> InterpreterFor (Queue d) r
+ Polysemy.Conc: data Critical :: Effect
+ Polysemy.Conc: data EventToken token
+ Polysemy.Conc: interpretAtomic :: forall a r. Member (Embed IO) r => a -> InterpreterFor (AtomicState a) r
+ Polysemy.Conc: interpretCritical :: Member (Final IO) r => InterpreterFor Critical r
+ Polysemy.Conc: interpretCriticalNull :: InterpreterFor Critical r
+ Polysemy.Conc: interpretScopedSync :: forall d r. Members [Resource, Race, Embed IO] r => InterpreterFor (Scoped (SyncResources (MVar d)) (Sync d)) r
+ Polysemy.Conc: interpretScopedSyncAs :: forall d r. Members [Resource, Race, Embed IO] r => d -> InterpreterFor (Scoped (SyncResources (MVar d)) (Sync d)) r
+ Polysemy.Conc: interpretSyncAs :: forall d r. Members [Race, Embed IO] r => d -> InterpreterFor (Sync d) r
+ Polysemy.Conc: loop :: Member (Queue d) r => (d -> Sem r ()) -> Sem r ()
+ Polysemy.Conc: loopOr :: Member (Queue d) r => Sem r Bool -> (d -> Sem r Bool) -> Sem r ()
+ Polysemy.Conc: retrying :: forall e w u t d r a. TimeUnit w => TimeUnit u => Members [Race, Time t d] r => w -> u -> Sem r (Either e a) -> Sem r (Maybe a)
+ Polysemy.Conc: retryingWithError :: forall e w u t d r a. TimeUnit w => TimeUnit u => Members [Race, Time t d, Embed IO] r => w -> u -> Sem r (Either e a) -> Sem r (Maybe (Either e a))
+ Polysemy.Conc: subscribe :: forall e token r. Member (Scoped (EventToken token) (Consume e)) r => InterpreterFor (Consume e) r
+ Polysemy.Conc: subscribeLoop :: forall e token r. Member (EventConsumer token e) r => (e -> Sem r ()) -> Sem r ()
+ Polysemy.Conc: subscribeWhile :: forall e token r. Member (EventConsumer token e) r => (e -> Sem r Bool) -> Sem r ()
+ Polysemy.Conc: timeoutU :: TimeUnit u => Member Race r => u -> Sem r () -> Sem r ()
+ Polysemy.Conc: type ChanConsumer e = Scoped (EventChan e) (Consume e)
+ Polysemy.Conc: type ChanEvents e = Events (OutChan e) e
+ Polysemy.Conc: type EventChan e = EventToken (OutChan e)
+ Polysemy.Conc: type EventConsumer token e = Scoped (EventToken token) (Consume e)
+ Polysemy.Conc: type ScopedSync res a = Scoped (SyncResources res) (Sync a)
+ Polysemy.Conc: withAsync :: Members [Resource, Race, Async] r => Sem r b -> (Async (Maybe b) -> Sem r a) -> Sem r a
+ Polysemy.Conc: withAsyncBlock :: Members [Resource, Async] r => Sem r b -> (Async (Maybe b) -> Sem r a) -> Sem r a
+ Polysemy.Conc: withAsync_ :: Members [Resource, Race, Async] r => Sem r b -> Sem r a -> Sem r a
+ Polysemy.Conc: withSync :: forall d res r. Member (Scoped (SyncResources res) (Sync d)) r => InterpreterFor (Sync d) r
+ Polysemy.Conc.Async: withAsync :: Members [Resource, Race, Async] r => Sem r b -> (Async (Maybe b) -> Sem r a) -> Sem r a
+ Polysemy.Conc.Async: withAsyncBlock :: Members [Resource, Async] r => Sem r b -> (Async (Maybe b) -> Sem r a) -> Sem r a
+ Polysemy.Conc.Async: withAsyncWait :: TimeUnit u => Members [Resource, Race, Async] r => u -> Sem r b -> (Async (Maybe b) -> Sem r a) -> Sem r a
+ Polysemy.Conc.Async: withAsync_ :: Members [Resource, Race, Async] r => Sem r b -> Sem r a -> Sem r a
+ Polysemy.Conc.AtomicState: interpretAtomic :: forall a r. Member (Embed IO) r => a -> InterpreterFor (AtomicState a) r
+ Polysemy.Conc.Effect.Sync: [ReadBlock] :: Sync d m d
+ Polysemy.Conc.Effect.Sync: [ReadTry] :: Sync d m (Maybe d)
+ Polysemy.Conc.Effect.Sync: [ReadWait] :: TimeUnit u => u -> Sync d m (Maybe d)
+ Polysemy.Conc.Events: subscribeLoop :: forall e token r. Member (EventConsumer token e) r => (e -> Sem r ()) -> Sem r ()
+ Polysemy.Conc.Events: subscribeWhile :: forall e token r. Member (EventConsumer token e) r => (e -> Sem r Bool) -> Sem r ()
+ Polysemy.Conc.Interpreter.Events: type ChanConsumer e = Scoped (EventChan e) (Consume e)
+ Polysemy.Conc.Interpreter.Events: type ChanEvents e = Events (OutChan e) e
+ Polysemy.Conc.Interpreter.Events: type EventChan e = EventToken (OutChan e)
+ Polysemy.Conc.Interpreter.Events: type EventConsumer token e = Scoped (EventToken token) (Consume e)
+ Polysemy.Conc.Interpreter.Queue.Pure: interpretQueueListReadOnlyAtomic :: forall d r. Member (Embed IO) r => [d] -> InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.Pure: interpretQueueListReadOnlyAtomicWith :: forall d r. Member (AtomicState [d]) r => InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.Pure: interpretQueueListReadOnlyState :: forall d r. Member (Embed IO) r => [d] -> InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.Pure: interpretQueueListReadOnlyStateWith :: forall d r. Member (State [d]) r => InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.TB: interpretQueueTB :: forall d r. Members [Race, Embed IO] r => Natural -> InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.TB: interpretQueueTBWith :: forall d r. Members [Race, Embed IO] r => TBQueue d -> InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.TBM: interpretQueueTBM :: forall d r. Members [Resource, Race, Embed IO] r => Int -> InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.TBM: interpretQueueTBMWith :: forall d r. Members [Race, Embed IO] r => TBMQueue d -> InterpreterFor (Queue d) r
+ Polysemy.Conc.Interpreter.Queue.TBM: withTBMQueue :: forall d r a. Members [Resource, Embed IO] r => Int -> (TBMQueue d -> Sem r a) -> Sem r a
+ Polysemy.Conc.Interpreter.Sync: interpretScopedSync :: forall d r. Members [Resource, Race, Embed IO] r => InterpreterFor (Scoped (SyncResources (MVar d)) (Sync d)) r
+ Polysemy.Conc.Interpreter.Sync: interpretScopedSyncAs :: forall d r. Members [Resource, Race, Embed IO] r => d -> InterpreterFor (Scoped (SyncResources (MVar d)) (Sync d)) r
+ Polysemy.Conc.Queue: loop :: Member (Queue d) r => (d -> Sem r ()) -> Sem r ()
+ Polysemy.Conc.Queue: loopOr :: Member (Queue d) r => Sem r Bool -> (d -> Sem r Bool) -> Sem r ()
+ Polysemy.Conc.Race: timeoutU :: TimeUnit u => Member Race r => u -> Sem r () -> Sem r ()
+ Polysemy.Conc.Retry: retrying :: forall e w u t d r a. TimeUnit w => TimeUnit u => Members [Race, Time t d] r => w -> u -> Sem r (Either e a) -> Sem r (Maybe a)
+ Polysemy.Conc.Retry: retryingWithError :: forall e w u t d r a. TimeUnit w => TimeUnit u => Members [Race, Time t d, Embed IO] r => w -> u -> Sem r (Either e a) -> Sem r (Maybe (Either e a))
+ Polysemy.Conc.Sync: withSync :: forall d res r. Member (Scoped (SyncResources res) (Sync d)) r => InterpreterFor (Sync d) r
- Polysemy.Conc: consume :: forall e_ajya r_ajyP. MemberWithError (Consume e_ajya) r_ajyP => Sem r_ajyP e_ajya
+ Polysemy.Conc: consume :: forall e_ajQW r_ajRB. MemberWithError (Consume e_ajQW) r_ajRB => Sem r_ajRB e_ajQW
- Polysemy.Conc: interpretEventsChan :: forall e r. Member (Embed IO) r => InterpretersFor [Events (OutChan e) e, Scoped (EventToken (OutChan e)) (Consume e)] r
+ Polysemy.Conc: interpretEventsChan :: forall e r. Members [Resource, Race, Async, Embed IO] r => InterpretersFor [Events (OutChan e) e, ChanConsumer e] r
- Polysemy.Conc: publish :: forall token_ajyZ e_ajyY r_akm1. MemberWithError (Events token_ajyZ e_ajyY) r_akm1 => e_ajyY -> Sem r_akm1 ()
+ Polysemy.Conc: publish :: forall token_ajRL e_ajRK r_akEN. MemberWithError (Events token_ajRL e_ajRK) r_akEN => e_ajRK -> Sem r_akEN ()
- Polysemy.Conc.Interpreter.Events: interpretConsumeChan :: forall e r. Member (Embed IO) r => EventToken (OutChan e) -> InterpreterFor (Consume e) r
+ Polysemy.Conc.Interpreter.Events: interpretConsumeChan :: forall e r. Member (Embed IO) r => EventChan e -> InterpreterFor (Consume e) r
- Polysemy.Conc.Interpreter.Events: interpretEventsChan :: forall e r. Member (Embed IO) r => InterpretersFor [Events (OutChan e) e, Scoped (EventToken (OutChan e)) (Consume e)] r
+ Polysemy.Conc.Interpreter.Events: interpretEventsChan :: forall e r. Members [Resource, Race, Async, Embed IO] r => InterpretersFor [Events (OutChan e) e, ChanConsumer e] r

Files

− Changelog.md
+ changelog.md view
@@ -0,0 +1,10 @@+# Unreleased++# 0.2.0.0+* Add `read*` constructors for `Sync`+* Add `subscribeWhile`, a combinator that consumes events until a condition is met+* Add looping combinators for `Queue`+* Add `retry`, a combinator that runs an action repeatedly until it returns `Right` or a timeout is hit+* Add `Scoped`, an effect for local resource scoping+* Add `withAsync`, a bracketing combinator that runs an async action while the main action runs+* Add `interpretAtomic`, a convenience interpreter for `AtomicState` that runs `runAtomicStateTVar`
lib/Polysemy/Conc.hs view
@@ -17,12 +17,21 @@   interpretQueueListReadOnlyState,   interpretQueueListReadOnlyStateWith, +  -- ** Combinators+  loop,+  loopOr,+   -- * MVars   -- $mvar   Sync,+  ScopedSync,    -- ** Interpreters   interpretSync,+  interpretSyncAs,+  withSync,+  interpretScopedSync,+  interpretScopedSyncAs,    -- * Racing   -- $race@@ -31,6 +40,9 @@   race_,   timeout,   timeout_,+  timeoutU,+  retrying,+  retryingWithError,    -- ** Interpreters   interpretRace,@@ -46,30 +58,59 @@   Events,   publish,   consume,+  subscribe,+  subscribeWhile,+  subscribeLoop,+  EventToken,+  EventChan,+  ChanEvents,+  EventConsumer,+  ChanConsumer,    -- ** Interpreters   interpretEventsChan,++  -- * Exceptions+  Critical,++  -- ** Interpreters+  interpretCritical,+  interpretCriticalNull,++  -- * Other Combinators+  interpretAtomic,+  withAsyncBlock,+  withAsync,+  withAsync_, ) where +import Polysemy.Conc.Async (withAsync, withAsyncBlock, withAsync_)+import Polysemy.Conc.AtomicState (interpretAtomic)+import Polysemy.Conc.Critical (interpretCritical, interpretCriticalNull)+import Polysemy.Conc.Data.Critical (Critical) import Polysemy.Conc.Data.Interrupt (Interrupt) import Polysemy.Conc.Data.Queue (Queue) import Polysemy.Conc.Data.QueueResult (QueueResult) import Polysemy.Conc.Data.Race (Race, race, timeout)-import Polysemy.Conc.Effect.Events (Events, publish, consume)-import Polysemy.Conc.Effect.Sync (Sync)-import Polysemy.Conc.Interpreter.Events (interpretEventsChan)-import Polysemy.Conc.Interpreter.Sync (interpretSync)-import Polysemy.Conc.Interrupt (interpretInterrupt)-import Polysemy.Conc.Queue (+import Polysemy.Conc.Effect.Events (EventToken, Events, consume, publish, subscribe)+import Polysemy.Conc.Effect.Sync (ScopedSync, Sync)+import Polysemy.Conc.Events (subscribeLoop, subscribeWhile)+import Polysemy.Conc.Interpreter.Events (ChanConsumer, ChanEvents, EventChan, EventConsumer, interpretEventsChan)+import Polysemy.Conc.Interpreter.Queue.Pure (   interpretQueueListReadOnlyAtomic,   interpretQueueListReadOnlyAtomicWith,   interpretQueueListReadOnlyState,   interpretQueueListReadOnlyStateWith,   )+import Polysemy.Conc.Interpreter.Queue.TB (interpretQueueTB)+import Polysemy.Conc.Interpreter.Queue.TBM (interpretQueueTBM)+import Polysemy.Conc.Interpreter.Sync (interpretScopedSync, interpretScopedSyncAs, interpretSync, interpretSyncAs)+import Polysemy.Conc.Interrupt (interpretInterrupt)+import Polysemy.Conc.Queue (loop, loopOr) import Polysemy.Conc.Queue.Result (resultToMaybe)-import Polysemy.Conc.Queue.TB (interpretQueueTB)-import Polysemy.Conc.Queue.TBM (interpretQueueTBM)-import Polysemy.Conc.Race (interpretRace, race_, timeout_)+import Polysemy.Conc.Race (interpretRace, race_, timeoutU, timeout_)+import Polysemy.Conc.Retry (retrying, retryingWithError)+import Polysemy.Conc.Sync (withSync)  -- $intro -- This library provides an assortment of tools for concurrency-related tasks:
+ lib/Polysemy/Conc/Async.hs view
@@ -0,0 +1,59 @@+module Polysemy.Conc.Async where++import qualified Control.Concurrent.Async as Base+import Polysemy.Async (Async, async, cancel)+import Polysemy.Resource (Resource, bracket)+import Polysemy.Time (MilliSeconds (MilliSeconds), TimeUnit)++import Polysemy.Conc.Data.Race (Race)+import qualified Polysemy.Conc.Race as Race++-- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- Passes the handle into the action to allow it to await its result.+--+-- When cancelling, this variant will wait indefinitely for the thread to be gone.+withAsyncBlock ::+  Members [Resource, Async] r =>+  Sem r b ->+  (Base.Async (Maybe b) -> Sem r a) ->+  Sem r a+withAsyncBlock mb =+  bracket (async mb) cancel++-- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- Passes the handle into the action to allow it to await its result.+--+-- When cancelling, this variant will wait for the specified interval for the thread to be gone.+withAsyncWait ::+  TimeUnit u =>+  Members [Resource, Race, Async] r =>+  u ->+  Sem r b ->+  (Base.Async (Maybe b) -> Sem r a) ->+  Sem r a+withAsyncWait interval mb =+  bracket (async mb) (Race.timeoutU interval . cancel)++-- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- Passes the handle into the action to allow it to await its result.+--+-- When cancelling, this variant will wait for 500ms for the thread to be gone.+withAsync ::+  Members [Resource, Race, Async] r =>+  Sem r b ->+  (Base.Async (Maybe b) -> Sem r a) ->+  Sem r a+withAsync =+  withAsyncWait (MilliSeconds 500)++-- |Run the first action asynchronously while the second action executes, then cancel the first action.+-- Discards the handle, expecting the async action to either terminate or be cancelled.+--+-- When cancelling, this variant will wait for 500ms for the thread to be gone.+withAsync_ ::+  Members [Resource, Race, Async] r =>+  Sem r b ->+  Sem r a ->+  Sem r a+withAsync_ mb =+  withAsync mb . const
+ lib/Polysemy/Conc/AtomicState.hs view
@@ -0,0 +1,14 @@+module Polysemy.Conc.AtomicState where++import Polysemy.AtomicState (runAtomicStateTVar)++-- |Convenience wrapper around 'runAtomicStateTVar' that creates a new 'TVar'.+interpretAtomic ::+  ∀ a r .+  Member (Embed IO) r =>+  a ->+  InterpreterFor (AtomicState a) r+interpretAtomic initial sem = do+  tv <- newTVarIO initial+  runAtomicStateTVar tv sem+{-# inline interpretAtomic #-}
lib/Polysemy/Conc/Critical.hs view
@@ -20,7 +20,7 @@       where         run ma' s =           Exception.catch (fmap Right <$> ma') \ se -> pure (Left se <$ s)-{-# INLINE interpretCritical #-}+{-# inline interpretCritical #-}  -- |Interpret 'Critical' by doing nothing. interpretCriticalNull ::@@ -29,4 +29,4 @@   interpretH \case     Catch ma ->       fmap (fmap Right) . raise . interpretCriticalNull =<< runT ma-{-# INLINE interpretCriticalNull #-}+{-# inline interpretCriticalNull #-}
lib/Polysemy/Conc/Data/Critical.hs view
@@ -4,8 +4,8 @@  -- |An effect that catches exceptions. ----- The point of this is to catch exceptions that haven't been anticipated, as a failsafe.--- Used internally in this library.+-- Provides the exact functionality of `Polysemy.Error.fromExceptionSem`, but pushes the dependency on @Final IO@ to the+-- interpreter, and makes it optional. data Critical :: Effect where   -- |Catch all exceptions of type @e@ in this computation.   Catch :: Exception e => m a -> Critical m (Either e a)
lib/Polysemy/Conc/Effect/Scoped.hs view
@@ -44,15 +44,17 @@ -- |Interpreter for 'Scoped', taking a @resource@ allocation function and a parameterized interpreter for the plain -- @effect@. ----- @scope@ is a callback function, allowing the user to compute the resource for each program from other effects.+-- @withResource@ is a callback function, allowing the user to acquire the resource for each program from other effects. -- -- @scopedInterpreter@ is a regular interpreter that is called with the @resource@ argument produced by @scope@.+-- /Note/: This function will be called for each action in the program, so if the interpreter allocates any resources,+-- they will be scoped to a single action. Move them to @withResource@ instead. runScoped ::   ∀ resource effect r .   (∀ x . (resource -> Sem r x) -> Sem r x) ->   (resource -> InterpreterFor effect r) ->   InterpreterFor (Scoped resource effect) r-runScoped scope scopedInterpreter =+runScoped withResource scopedInterpreter =   run   where     run :: InterpreterFor (Scoped resource effect) r@@ -60,8 +62,8 @@       interpretH' \ (Weaving effect s wv ex ins) -> case effect of         Run resource act ->           scopedInterpreter resource (liftSem $ injWeaving $ Weaving act s (raise . run . wv) ex ins)-        InScope main -> do-          ex <$> scope \ resource -> run (wv (main resource <$ s))+        InScope main ->+          ex <$> withResource \ resource -> run (wv (main resource <$ s))  -- |Variant of 'Scoped' in which the resource allocator is a plain action. runScopedAs ::
lib/Polysemy/Conc/Effect/Sync.hs view
@@ -3,6 +3,7 @@ module Polysemy.Conc.Effect.Sync where  import Polysemy.Time (TimeUnit)+import Polysemy.Conc.Effect.Scoped (Scoped)  -- |Abstracts an 'MVar'. --@@ -30,6 +31,12 @@   TakeWait :: TimeUnit u => u -> Sync d m (Maybe d)   -- |Take the variable, returning 'Nothing' immmediately if no value was available.   TakeTry :: Sync d m (Maybe d)+  -- |Read the variable, waiting until a value is available.+  ReadBlock :: Sync d m d+  -- |Read the variable, waiting until a value is available or the timeout has expired.+  ReadWait :: TimeUnit u => u -> Sync d m (Maybe d)+  -- |Read the variable, returning 'Nothing' immmediately if no value was available.+  ReadTry :: Sync d m (Maybe d)   -- |Write the variable, waiting until it is writable.   PutBlock :: d -> Sync d m ()   -- |Write the variable, waiting until it is writable or the timeout has expired.@@ -40,3 +47,9 @@   Empty :: Sync d m Bool  makeSem ''Sync++newtype SyncResources a =+  SyncResources { unSyncResources :: a }++type ScopedSync res a =+  Scoped (SyncResources res) (Sync a)
+ lib/Polysemy/Conc/Events.hs view
@@ -0,0 +1,27 @@+-- |Description: Events Combinators+module Polysemy.Conc.Events where++import Polysemy.Conc.Interpreter.Events (EventConsumer)+import qualified Polysemy.Conc.Effect.Events as Events++-- |Pull repeatedly from the 'Events' channel, passing the event to the supplied callback.+-- Stop when the action returns @False@.+subscribeWhile ::+  ∀ e token r .+  Member (EventConsumer token e) r =>+  (e -> Sem r Bool) ->+  Sem r ()+subscribeWhile action =+  Events.subscribe @e @token spin+  where+    spin =+      whenM (raise . action =<< Events.consume) spin++-- |Pull repeatedly from the 'Events' channel, passing the event to the supplied callback.+subscribeLoop ::+  ∀ e token r .+  Member (EventConsumer token e) r =>+  (e -> Sem r ()) ->+  Sem r ()+subscribeLoop action =+  Events.subscribe @e @token (forever (raise . action =<< Events.consume))
lib/Polysemy/Conc/Interpreter/Events.hs view
@@ -4,17 +4,37 @@  import Control.Concurrent.Chan.Unagi.Bounded (InChan, OutChan, dupChan, newChan, readChan, tryWriteChan) import Polysemy (InterpretersFor)+import Polysemy.Async (Async)+import Polysemy.Resource (Resource) +import Polysemy.Conc.Async (withAsync_)+import Polysemy.Conc.Data.Race (Race) import qualified Polysemy.Conc.Effect.Events as Events-import Polysemy.Conc.Effect.Events (EventToken (EventToken), Events, Consume)+import Polysemy.Conc.Effect.Events (Consume, EventToken (EventToken), Events) import Polysemy.Conc.Effect.Scoped (Scoped, runScopedAs) +-- |Convenience alias for the default 'Events' that uses an 'OutChan'.+type ChanEvents e =+  Events (OutChan e) e++-- |Convenience alias for the default 'EventToken' that uses an 'OutChan'.+type EventChan e =+  EventToken (OutChan e)++-- |Convenience alias for the consumer effect.+type EventConsumer token e =+  Scoped (EventToken token) (Consume e)++-- |Convenience alias for the consumer effect using the default implementation.+type ChanConsumer e =+  Scoped (EventChan e) (Consume e)+ -- |Interpret 'Consume' by reading from an 'OutChan'. -- Used internally by 'interpretEventsChan', not safe to use directly. interpretConsumeChan ::   ∀ e r .   Member (Embed IO) r =>-  EventToken (OutChan e) ->+  EventChan e ->   InterpreterFor (Consume e) r interpretConsumeChan (EventToken chan) =   interpret \case@@ -51,8 +71,9 @@ -- duplicate to 'interpretConsumeChan'. interpretEventsChan ::   ∀ e r .-  Member (Embed IO) r =>-  InterpretersFor [Events (OutChan e) e, Scoped (EventToken (OutChan e)) (Consume e)] r+  Members [Resource, Race, Async, Embed IO] r =>+  InterpretersFor [Events (OutChan e) e, ChanConsumer e] r interpretEventsChan sem = do-  (inChan, _) <- embed (newChan @e 64)-  runScopedAs (EventToken <$> embed (dupChan inChan)) interpretConsumeChan (interpretEventsInChan inChan sem)+  (inChan, outChan) <- embed (newChan @e 64)+  withAsync_ (forever (embed (readChan outChan))) do+    runScopedAs (EventToken <$> embed (dupChan inChan)) interpretConsumeChan (interpretEventsInChan inChan sem)
+ lib/Polysemy/Conc/Interpreter/Queue/Pure.hs view
@@ -0,0 +1,113 @@+-- |Description: Pure Queue Interpreters+module Polysemy.Conc.Interpreter.Queue.Pure where++import Polysemy.AtomicState (atomicState')+import Polysemy.State (State, evalState, get, gets, put)++import Polysemy.Conc.AtomicState (interpretAtomic)+import qualified Polysemy.Conc.Data.Queue as Queue+import Polysemy.Conc.Data.Queue (Queue)+import qualified Polysemy.Conc.Data.QueueResult as QueueResult+import Polysemy.Conc.Data.QueueResult (QueueResult)++-- |Reinterpret 'Queue' as 'AtomicState' with a list that cannot be written to.+-- Useful for testing.+interpretQueueListReadOnlyAtomicWith ::+  ∀ d r .+  Member (AtomicState [d]) r =>+  InterpreterFor (Queue d) r+interpretQueueListReadOnlyAtomicWith =+  interpret \case+    Queue.Read ->+      read+    Queue.TryRead ->+      read+    Queue.ReadTimeout _ ->+      read+    Queue.Peek ->+      peek+    Queue.TryPeek ->+      peek+    Queue.Write _ ->+      pass+    Queue.TryWrite _ ->+      pure QueueResult.NotAvailable+    Queue.WriteTimeout _ _ ->+      pure QueueResult.NotAvailable+    Queue.Closed ->+      atomicGets @[d] null+    Queue.Close ->+      atomicPut @[d] []+  where+    read :: Sem r (QueueResult d)+    read =+      atomicState' @[d] \case+        [] -> ([], QueueResult.Closed)+        h : t -> (t, QueueResult.Success h)+    peek :: Sem r (QueueResult d)+    peek =+      atomicGets @[d] \case+        [] -> QueueResult.Closed+        h : _ -> QueueResult.Success h+{-# inline interpretQueueListReadOnlyAtomicWith #-}++-- |Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'AtomicState'.+interpretQueueListReadOnlyAtomic ::+  ∀ d r .+  Member (Embed IO) r =>+  [d] ->+  InterpreterFor (Queue d) r+interpretQueueListReadOnlyAtomic ds sem =+  interpretAtomic ds (interpretQueueListReadOnlyAtomicWith (raiseUnder sem))+{-# inline interpretQueueListReadOnlyAtomic #-}++-- |Reinterpret 'Queue' as 'State' with a list that cannot be written to.+-- Useful for testing.+interpretQueueListReadOnlyStateWith ::+  ∀ d r .+  Member (State [d]) r =>+  InterpreterFor (Queue d) r+interpretQueueListReadOnlyStateWith =+  interpret \case+    Queue.Read ->+      read+    Queue.TryRead ->+      read+    Queue.ReadTimeout _ ->+      read+    Queue.Peek ->+      peek+    Queue.TryPeek ->+      peek+    Queue.Write _ ->+      pass+    Queue.TryWrite _ ->+      pure QueueResult.NotAvailable+    Queue.WriteTimeout _ _ ->+      pure QueueResult.NotAvailable+    Queue.Closed ->+      gets @[d] null+    Queue.Close ->+      put @[d] []+  where+    read :: Sem r (QueueResult d)+    read =+      get @[d] >>= \case+        [] -> pure QueueResult.Closed+        h : t -> QueueResult.Success h <$ put t+    peek :: Sem r (QueueResult d)+    peek =+      gets @[d] \case+        [] -> QueueResult.Closed+        h : _ -> QueueResult.Success h+{-# inline interpretQueueListReadOnlyStateWith #-}++-- |Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'State'.+interpretQueueListReadOnlyState ::+  ∀ d r .+  Member (Embed IO) r =>+  [d] ->+  InterpreterFor (Queue d) r+interpretQueueListReadOnlyState ds sem = do+  evalState ds (interpretQueueListReadOnlyStateWith (raiseUnder sem))+{-# inline interpretQueueListReadOnlyState #-}
+ lib/Polysemy/Conc/Interpreter/Queue/TB.hs view
@@ -0,0 +1,65 @@+-- |Description: Queue Interpreters for 'TBQueue'+module Polysemy.Conc.Interpreter.Queue.TB where++import Control.Concurrent.STM (+  TBQueue,+  isFullTBQueue,+  newTBQueueIO,+  peekTBQueue,+  readTBQueue,+  tryPeekTBQueue,+  tryReadTBQueue,+  writeTBQueue,+  )++import qualified Polysemy.Conc.Data.Queue as Queue+import Polysemy.Conc.Data.Queue (Queue)+import qualified Polysemy.Conc.Data.QueueResult as QueueResult+import Polysemy.Conc.Data.Race (Race)+import Polysemy.Conc.Queue.Result (naResult)+import Polysemy.Conc.Queue.Timeout (withTimeout)++-- |Interpret 'Queue' with a 'TBQueue'.+--+-- This variant expects an allocated queue as an argument.+interpretQueueTBWith ::+  ∀ d r .+  Members [Race, Embed IO] r =>+  TBQueue d ->+  InterpreterFor (Queue d) r+interpretQueueTBWith queue =+  interpret \case+    Queue.Read ->+      atomically (QueueResult.Success <$> readTBQueue queue)+    Queue.TryRead ->+      atomically (naResult <$> tryReadTBQueue queue)+    Queue.ReadTimeout timeout ->+      withTimeout timeout (Just <$> readTBQueue queue)+    Queue.Peek ->+      atomically (QueueResult.Success <$> peekTBQueue queue)+    Queue.TryPeek ->+      atomically (naResult <$> tryPeekTBQueue queue)+    Queue.Write d ->+      atomically (writeTBQueue queue d)+    Queue.TryWrite d ->+      atomically do+        ifM (isFullTBQueue queue) (pure QueueResult.NotAvailable) (QueueResult.Success <$> writeTBQueue queue d)+    Queue.WriteTimeout timeout d ->+      withTimeout timeout (Just <$> writeTBQueue queue d)+    Queue.Closed ->+      pure False+    Queue.Close ->+      pass+{-# inline interpretQueueTBWith #-}++-- |Interpret 'Queue' with a 'TBQueue'.+interpretQueueTB ::+  ∀ d r .+  Members [Race, Embed IO] r =>+  -- |Buffer size+  Natural ->+  InterpreterFor (Queue d) r+interpretQueueTB maxQueued sem = do+  queue <- embed (newTBQueueIO @d maxQueued)+  interpretQueueTBWith queue sem+{-# inline interpretQueueTB #-}
+ lib/Polysemy/Conc/Interpreter/Queue/TBM.hs view
@@ -0,0 +1,76 @@+-- |Description: Queue Interpreters for 'TBMQueue'+module Polysemy.Conc.Interpreter.Queue.TBM where++import Control.Concurrent.STM.TBMQueue (+  TBMQueue,+  closeTBMQueue,+  isClosedTBMQueue,+  newTBMQueueIO,+  peekTBMQueue,+  readTBMQueue,+  tryPeekTBMQueue,+  tryReadTBMQueue,+  tryWriteTBMQueue,+  writeTBMQueue,+  )+import Polysemy.Resource (Resource, bracket)++import qualified Polysemy.Conc.Data.Queue as Queue+import Polysemy.Conc.Data.Queue (Queue)+import Polysemy.Conc.Data.Race (Race)+import Polysemy.Conc.Queue.Result (closedBoolResult, closedNaResult, closedResult)+import Polysemy.Conc.Queue.Timeout (withTimeout)++-- |Interpret 'Queue' with a 'TBMQueue'.+--+-- This variant expects an allocated queue as an argument.+interpretQueueTBMWith ::+  ∀ d r .+  Members [Race, Embed IO] r =>+  TBMQueue d ->+  InterpreterFor (Queue d) r+interpretQueueTBMWith queue =+  interpret \case+    Queue.Read ->+      atomically (closedResult <$> readTBMQueue queue)+    Queue.TryRead ->+      atomically (closedNaResult <$> tryReadTBMQueue queue)+    Queue.ReadTimeout timeout ->+      withTimeout timeout (readTBMQueue queue)+    Queue.Peek ->+      atomically (closedResult <$> peekTBMQueue queue)+    Queue.TryPeek ->+      atomically (closedNaResult <$> tryPeekTBMQueue queue)+    Queue.Write d ->+      atomically (writeTBMQueue queue d)+    Queue.TryWrite d ->+      atomically (closedBoolResult <$> tryWriteTBMQueue queue d)+    Queue.WriteTimeout timeout d ->+      withTimeout timeout do+        ifM (isClosedTBMQueue queue) (pure Nothing) (Just <$> writeTBMQueue queue d)+    Queue.Closed ->+      atomically (isClosedTBMQueue queue)+    Queue.Close ->+      atomically (closeTBMQueue queue)+{-# inline interpretQueueTBMWith #-}++withTBMQueue ::+  ∀ d r a .+  Members [Resource, Embed IO] r =>+  Int ->+  (TBMQueue d -> Sem r a) ->+  Sem r a+withTBMQueue maxQueued =+  bracket (embed (newTBMQueueIO maxQueued)) (atomically . closeTBMQueue)++-- |Interpret 'Queue' with a 'TBMQueue'.+interpretQueueTBM ::+  ∀ d r .+  Members [Resource, Race, Embed IO] r =>+  -- |Buffer size+  Int ->+  InterpreterFor (Queue d) r+interpretQueueTBM maxQueued sem = do+  withTBMQueue maxQueued \ queue ->+    interpretQueueTBMWith queue sem+{-# inline interpretQueueTBM #-}
lib/Polysemy/Conc/Interpreter/Sync.hs view
@@ -5,9 +5,11 @@  import qualified Polysemy.Conc.Data.Race as Race import Polysemy.Conc.Data.Race (Race)+import Polysemy.Conc.Effect.Scoped (Scoped, runScopedAs) import qualified Polysemy.Conc.Effect.Sync as Sync-import Polysemy.Conc.Effect.Sync (Sync)+import Polysemy.Conc.Effect.Sync (Sync, SyncResources (SyncResources), unSyncResources) import qualified Polysemy.Conc.Race as Race+import Polysemy.Resource (Resource)  -- |Interpret 'Sync' with the provided 'MVar'. interpretSyncWith ::@@ -29,6 +31,12 @@       rightToMaybe <$> Race.timeout () interval (takeMVar var)     Sync.TakeTry ->       tryTakeMVar var+    Sync.ReadBlock ->+      readMVar var+    Sync.ReadWait interval ->+      rightToMaybe <$> Race.timeout () interval (readMVar var)+    Sync.ReadTry ->+      tryReadMVar var     Sync.PutBlock d ->       putMVar var d     Sync.PutWait interval d ->@@ -56,3 +64,20 @@ interpretSyncAs d sem = do   var <- newMVar d   interpretSyncWith var sem++-- |Interpret 'Sync' for locally scoped use with an empty 'MVar'.+interpretScopedSync ::+  ∀ d r .+  Members [Resource, Race, Embed IO] r =>+  InterpreterFor (Scoped (SyncResources (MVar d)) (Sync d)) r+interpretScopedSync =+  runScopedAs (SyncResources <$> newEmptyMVar) \ r -> interpretSyncWith (unSyncResources r)++-- |Interpret 'Sync' for locally scoped use with an 'MVar' containing the specified value.+interpretScopedSyncAs ::+  ∀ d r .+  Members [Resource, Race, Embed IO] r =>+  d ->+  InterpreterFor (Scoped (SyncResources (MVar d)) (Sync d)) r+interpretScopedSyncAs d =+  runScopedAs (SyncResources <$> newMVar d) \ r -> interpretSyncWith (unSyncResources r)
lib/Polysemy/Conc/Interrupt.hs view
@@ -9,6 +9,7 @@ import qualified Data.Text.IO as Text import Polysemy (getInspectorT, inspect, interpretH, runT) import Polysemy.Async (Async, async, await, cancel)+import Polysemy.AtomicState (runAtomicStateTVar) import Polysemy.Internal.Tactics (liftT) import Polysemy.Time (Seconds (Seconds)) import System.Posix.Signals (Handler (CatchInfoOnce, CatchOnce), SignalInfo, installHandler, keyboardSignal)@@ -145,7 +146,7 @@       handle <- raise (interpretInterruptState (async maT))       result <- liftT (awaitOrKill desc handle)       pure (join . fmap (inspect ins) <$> result)-{-# INLINE interpretInterruptState #-}+{-# inline interpretInterruptState #-}  broadcastInterrupt ::   Members [AtomicState InterruptState, Embed IO] r =>@@ -168,7 +169,7 @@   thunk originalHandler _ =   const pass-{-# INLINE originalHandler #-}+{-# inline originalHandler #-}  installSignalHandler ::   TVar InterruptState ->@@ -191,4 +192,4 @@   runAtomicStateTVar state do     atomicModify' \ s -> s {original = originalHandler orig}     interpretInterruptState $ raiseUnder sem-{-# INLINE interpretInterrupt #-}+{-# inline interpretInterrupt #-}
lib/Polysemy/Conc/Prelude.hs view
@@ -36,7 +36,7 @@   reinterpret,   runFinal,   )-import Polysemy.AtomicState (AtomicState, atomicGet, atomicGets, atomicModify', atomicPut, runAtomicStateTVar)+import Polysemy.AtomicState (AtomicState, atomicGet, atomicGets, atomicModify', atomicPut) import Relude hiding (   Reader,   State,@@ -64,9 +64,9 @@ qt :: QuasiQuoter qt =   Interpolate.i-{-# INLINE qt #-}+{-# inline qt #-}  unify :: Either a a -> a unify =   either id id-{-# INLINE unify #-}+{-# inline unify #-}
lib/Polysemy/Conc/Queue.hs view
@@ -1,113 +1,34 @@--- |Description: Pure Queue interpreters+-- |Description: Queue Combinators module Polysemy.Conc.Queue where -import Polysemy.AtomicState (atomicState')-import Polysemy.State (State, evalState, get, gets, put)- import qualified Polysemy.Conc.Data.Queue as Queue import Polysemy.Conc.Data.Queue (Queue) import qualified Polysemy.Conc.Data.QueueResult as QueueResult-import Polysemy.Conc.Data.QueueResult (QueueResult) --- |Reinterpret 'Queue' as 'AtomicState' with a list that cannot be written to.--- Useful for testing.-interpretQueueListReadOnlyAtomicWith ::-  ∀ d r .-  Member (AtomicState [d]) r =>-  InterpreterFor (Queue d) r-interpretQueueListReadOnlyAtomicWith =-  interpret \case-    Queue.Read ->-      read-    Queue.TryRead ->-      read-    Queue.ReadTimeout _ ->-      read-    Queue.Peek ->-      peek-    Queue.TryPeek ->-      peek-    Queue.Write _ ->-      pass-    Queue.TryWrite _ ->-      pure QueueResult.NotAvailable-    Queue.WriteTimeout _ _ ->-      pure QueueResult.NotAvailable-    Queue.Closed ->-      atomicGets @[d] null-    Queue.Close ->-      atomicPut @[d] []-  where-    read :: Sem r (QueueResult d)-    read =-      atomicState' @[d] \case-        [] -> ([], QueueResult.Closed)-        h : t -> (t, QueueResult.Success h)-    peek :: Sem r (QueueResult d)-    peek =-      atomicGets @[d] \case-        [] -> QueueResult.Closed-        h : _ -> QueueResult.Success h-{-# INLINE interpretQueueListReadOnlyAtomicWith #-}---- |Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'AtomicState'.-interpretQueueListReadOnlyAtomic ::-  ∀ d r .-  Member (Embed IO) r =>-  [d] ->-  InterpreterFor (Queue d) r-interpretQueueListReadOnlyAtomic ds sem = do-  tv <- newTVarIO ds-  runAtomicStateTVar tv (interpretQueueListReadOnlyAtomicWith (raiseUnder sem))-{-# INLINE interpretQueueListReadOnlyAtomic #-}---- |Reinterpret 'Queue' as 'State' with a list that cannot be written to.--- Useful for testing.-interpretQueueListReadOnlyStateWith ::-  ∀ d r .-  Member (State [d]) r =>-  InterpreterFor (Queue d) r-interpretQueueListReadOnlyStateWith =-  interpret \case-    Queue.Read ->-      read-    Queue.TryRead ->-      read-    Queue.ReadTimeout _ ->-      read-    Queue.Peek ->-      peek-    Queue.TryPeek ->-      peek-    Queue.Write _ ->-      pass-    Queue.TryWrite _ ->-      pure QueueResult.NotAvailable-    Queue.WriteTimeout _ _ ->-      pure QueueResult.NotAvailable-    Queue.Closed ->-      gets @[d] null-    Queue.Close ->-      put @[d] []+-- |Read from a 'Queue' repeatedly until it is closed.+--+-- When an element is received, call @action@ and recurse if it returns 'True'.+-- When no element is available, evaluate @na@ and recurse if it returns 'True'.+loopOr ::+  Member (Queue d) r =>+  Sem r Bool ->+  (d -> Sem r Bool) ->+  Sem r ()+loopOr na action =+  spin   where-    read :: Sem r (QueueResult d)-    read =-      get @[d] >>= \case-        [] -> pure QueueResult.Closed-        h : t -> QueueResult.Success h <$ put t-    peek :: Sem r (QueueResult d)-    peek =-      gets @[d] \case-        [] -> QueueResult.Closed-        h : _ -> QueueResult.Success h-{-# INLINE interpretQueueListReadOnlyStateWith #-}+    spin =+      Queue.read >>= \case+        QueueResult.Success d -> whenM (action d) spin+        QueueResult.NotAvailable -> whenM na spin+        QueueResult.Closed -> pass --- |Variant of 'interpretQueueListReadOnlyAtomicWith' that interprets the 'State'.-interpretQueueListReadOnlyState ::-  ∀ d r .-  Member (Embed IO) r =>-  [d] ->-  InterpreterFor (Queue d) r-interpretQueueListReadOnlyState ds sem = do-  evalState ds (interpretQueueListReadOnlyStateWith (raiseUnder sem))-{-# INLINE interpretQueueListReadOnlyState #-}+-- |Read from a 'Queue' repeatedly until it is closed.+--+-- When an element is received, call @action@ and recurse.+loop ::+  Member (Queue d) r =>+  (d -> Sem r ()) ->+  Sem r ()+loop action =+  loopOr (pure True) \ d -> True <$ action d
lib/Polysemy/Conc/Queue/Result.hs view
@@ -10,7 +10,7 @@ closedResult = \case   Nothing -> QueueResult.Closed   Just d -> QueueResult.Success d-{-# INLINE closedResult #-}+{-# inline closedResult #-}  naResult ::   Maybe d ->@@ -18,7 +18,7 @@ naResult = \case   Nothing -> QueueResult.NotAvailable   Just d -> QueueResult.Success d-{-# INLINE naResult #-}+{-# inline naResult #-}  closedNaResult ::   Maybe (Maybe d) ->@@ -27,7 +27,7 @@   Nothing -> QueueResult.Closed   Just Nothing -> QueueResult.NotAvailable   Just (Just d) -> QueueResult.Success d-{-# INLINE closedNaResult #-}+{-# inline closedNaResult #-}  closedBoolResult ::   Maybe Bool ->@@ -36,7 +36,7 @@   Nothing -> QueueResult.Closed   Just False -> QueueResult.NotAvailable   Just True -> QueueResult.Success ()-{-# INLINE closedBoolResult #-}+{-# inline closedBoolResult #-}  -- |Turn a 'Success' into 'Just'. resultToMaybe :: QueueResult d -> Maybe d@@ -44,4 +44,4 @@   QueueResult.Success d -> Just d   QueueResult.NotAvailable -> Nothing   QueueResult.Closed -> Nothing-{-# INLINE resultToMaybe #-}+{-# inline resultToMaybe #-}
− lib/Polysemy/Conc/Queue/TB.hs
@@ -1,65 +0,0 @@--- |Description: Queue interpreters for 'TBQueue'-module Polysemy.Conc.Queue.TB where--import Control.Concurrent.STM (-  TBQueue,-  isFullTBQueue,-  newTBQueueIO,-  peekTBQueue,-  readTBQueue,-  tryPeekTBQueue,-  tryReadTBQueue,-  writeTBQueue,-  )--import qualified Polysemy.Conc.Data.Queue as Queue-import Polysemy.Conc.Data.Queue (Queue)-import qualified Polysemy.Conc.Data.QueueResult as QueueResult-import Polysemy.Conc.Data.Race (Race)-import Polysemy.Conc.Queue.Result (naResult)-import Polysemy.Conc.Queue.Timeout (withTimeout)---- |Interpret 'Queue' with a 'TBQueue'.------ This variant expects an allocated queue as an argument.-interpretQueueTBWith ::-  ∀ d r .-  Members [Race, Embed IO] r =>-  TBQueue d ->-  InterpreterFor (Queue d) r-interpretQueueTBWith queue =-  interpret \case-    Queue.Read ->-      atomically (QueueResult.Success <$> readTBQueue queue)-    Queue.TryRead ->-      atomically (naResult <$> tryReadTBQueue queue)-    Queue.ReadTimeout timeout ->-      withTimeout timeout (Just <$> readTBQueue queue)-    Queue.Peek ->-      atomically (QueueResult.Success <$> peekTBQueue queue)-    Queue.TryPeek ->-      atomically (naResult <$> tryPeekTBQueue queue)-    Queue.Write d ->-      atomically (writeTBQueue queue d)-    Queue.TryWrite d ->-      atomically do-        ifM (isFullTBQueue queue) (pure QueueResult.NotAvailable) (QueueResult.Success <$> writeTBQueue queue d)-    Queue.WriteTimeout timeout d ->-      withTimeout timeout (Just <$> writeTBQueue queue d)-    Queue.Closed ->-      pure False-    Queue.Close ->-      pass-{-# INLINE interpretQueueTBWith #-}---- |Interpret 'Queue' with a 'TBQueue'.-interpretQueueTB ::-  ∀ d r .-  Members [Race, Embed IO] r =>-  -- |Buffer size-  Natural ->-  InterpreterFor (Queue d) r-interpretQueueTB maxQueued sem = do-  queue <- embed (newTBQueueIO @d maxQueued)-  interpretQueueTBWith queue sem-{-# INLINE interpretQueueTB #-}
− lib/Polysemy/Conc/Queue/TBM.hs
@@ -1,67 +0,0 @@--- |Description: Queue interpreters for 'TBMQueue'-module Polysemy.Conc.Queue.TBM where--import Control.Concurrent.STM.TBMQueue (-  TBMQueue,-  closeTBMQueue,-  isClosedTBMQueue,-  newTBMQueueIO,-  peekTBMQueue,-  readTBMQueue,-  tryPeekTBMQueue,-  tryReadTBMQueue,-  tryWriteTBMQueue,-  writeTBMQueue,-  )--import qualified Polysemy.Conc.Data.Queue as Queue-import Polysemy.Conc.Data.Queue (Queue)-import Polysemy.Conc.Data.Race (Race)-import Polysemy.Conc.Queue.Result (closedBoolResult, closedNaResult, closedResult)-import Polysemy.Conc.Queue.Timeout (withTimeout)-import Polysemy.Resource (bracket, Resource)---- |Interpret 'Queue' with a 'TBMQueue'.------ This variant expects an allocated queue as an argument.-interpretQueueTBMWith ::-  ∀ d r .-  Members [Race, Embed IO] r =>-  TBMQueue d ->-  InterpreterFor (Queue d) r-interpretQueueTBMWith queue =-  interpret \case-    Queue.Read ->-      atomically (closedResult <$> readTBMQueue queue)-    Queue.TryRead ->-      atomically (closedNaResult <$> tryReadTBMQueue queue)-    Queue.ReadTimeout timeout ->-      withTimeout timeout (readTBMQueue queue)-    Queue.Peek ->-      atomically (closedResult <$> peekTBMQueue queue)-    Queue.TryPeek ->-      atomically (closedNaResult <$> tryPeekTBMQueue queue)-    Queue.Write d ->-      atomically (writeTBMQueue queue d)-    Queue.TryWrite d ->-      atomically (closedBoolResult <$> tryWriteTBMQueue queue d)-    Queue.WriteTimeout timeout d ->-      withTimeout timeout do-        ifM (isClosedTBMQueue queue) (pure Nothing) (Just <$> writeTBMQueue queue d)-    Queue.Closed ->-      atomically (isClosedTBMQueue queue)-    Queue.Close ->-      atomically (closeTBMQueue queue)-{-# INLINE interpretQueueTBMWith #-}---- |Interpret 'Queue' with a 'TBMQueue'.-interpretQueueTBM ::-  ∀ d r .-  Members [Resource, Race, Embed IO] r =>-  -- |Buffer size-  Int ->-  InterpreterFor (Queue d) r-interpretQueueTBM maxQueued sem = do-  bracket (embed (newTBMQueueIO @d maxQueued)) (atomically . closeTBMQueue) \ queue ->-    interpretQueueTBMWith queue sem-{-# INLINE interpretQueueTBM #-}
lib/Polysemy/Conc/Race.hs view
@@ -5,7 +5,7 @@ import qualified Control.Concurrent.Async as Async import Polysemy.Final (getInitialStateS, interpretFinal, runS) import qualified Polysemy.Time as Time-import Polysemy.Time (MicroSeconds(MicroSeconds), TimeUnit)+import Polysemy.Time (MicroSeconds (MicroSeconds), TimeUnit) import qualified System.Timeout as System  import qualified Polysemy.Conc.Data.Race as Race@@ -17,7 +17,7 @@   f (Either a b) biseqEither =   either (fmap Left) (fmap Right)-{-# INLINE biseqEither #-}+{-# inline biseqEither #-}  -- |Interpret 'Race' in terms of 'Async.race' and 'System.timeout'. -- Since this has to pass higher-order thunks as 'IO' arguments, it is interpreted in terms of 'Final IO'.@@ -32,7 +32,7 @@       mbT <- runS mb       s <- getInitialStateS       pure (maybe (Left err <$ s) (fmap Right) <$> System.timeout (fromIntegral timeout) mbT)-{-# INLINE interpretRace #-}+{-# inline interpretRace #-}  -- |Specialization of 'Race.race' for the case where both thunks return the same type, obviating the need for 'Either'. race_ ::@@ -40,9 +40,9 @@   Sem r a ->   Sem r a ->   Sem r a-race_ ma mb =-  unify <$> Race.race ma mb-{-# INLINE race_ #-}+race_ ml mr =+  unify <$> Race.race ml mr+{-# inline race_ #-}  -- |Specialization of 'Race.timeout' for the case where the thunk return the same type as the fallback, obviating the -- need for 'Either'.@@ -53,6 +53,17 @@   u ->   Sem r a ->   Sem r a-timeout_ err interval mb =-  unify <$> Race.timeout err interval mb-{-# INLINE timeout_ #-}+timeout_ err interval ma =+  unify <$> Race.timeout err interval ma+{-# inline timeout_ #-}++-- |Specialization of 'Race.timeout' for unit actions.+timeoutU ::+  TimeUnit u =>+  Member Race r =>+  u ->+  Sem r () ->+  Sem r ()+timeoutU =+  timeout_ ()+{-# inline timeoutU #-}
+ lib/Polysemy/Conc/Retry.hs view
@@ -0,0 +1,63 @@+-- |Description: Action Retrying+module Polysemy.Conc.Retry where++import qualified Polysemy.Time as Time+import Polysemy.Time (Time, TimeUnit)++import Polysemy.Conc.Data.Race (Race)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Interpreter.Sync (interpretSync)+import qualified Polysemy.Conc.Race as Race++-- |Run an action repeatedly until it returns 'Right' or the timout has been exceeded.+retrying ::+  ∀ e w u t d r a .+  TimeUnit w =>+  TimeUnit u =>+  Members [Race, Time t d] r =>+  -- |The timeout after which the attempt is abandoned.+  w ->+  -- |The waiting interval between two tries.+  u ->+  Sem r (Either e a) ->+  Sem r (Maybe a)+retrying timeout interval action =+  Race.timeout_ Nothing timeout (Just <$> spin)+  where+    spin =+      action >>= \case+        Right a ->+          pure a+        Left _ -> do+          Time.sleep @t @d interval+          spin++-- |Run an action repeatedly until it returns 'Right' or the timout has been exceeded.+--+-- If the action failed at least once, the last error will be returned in case of timeout.+retryingWithError ::+  ∀ e w u t d r a .+  TimeUnit w =>+  TimeUnit u =>+  Members [Race, Time t d, Embed IO] r =>+  -- |The timeout after which the attempt is abandoned.+  w ->+  -- |The waiting interval between two tries.+  u ->+  Sem r (Either e a) ->+  Sem r (Maybe (Either e a))+retryingWithError timeout interval action =+  interpretSync @e do+    Race.timeout_ Nothing timeout (Just <$> spin) >>= \case+      Just a -> pure (Just (Right a))+      Nothing -> fmap Left <$> Sync.takeTry+  where+    spin =+      raise action >>= \case+        Right a ->+          pure a+        Left e -> do+          void (Sync.takeTry @e)+          Sync.putTry e+          Time.sleep @t @d interval+          spin
lib/Polysemy/Conc/Sync.hs view
@@ -4,9 +4,11 @@ import qualified Polysemy.Time as Time import Polysemy.Time (Time, TimeUnit) +import Polysemy.Conc.Effect.Scoped (Scoped, scoped) import qualified Polysemy.Conc.Effect.Sync as Sync-import Polysemy.Conc.Effect.Sync (Sync)+import Polysemy.Conc.Effect.Sync (Sync, SyncResources) +-- |Run an action repeatedly until the 'Sync' variable is available. whileEmpty ::   ∀ a r .   Member (Sync a) r =>@@ -17,8 +19,9 @@   where     spin = do       action-      whenM (isJust <$> Sync.try @a) spin+      whenM (not <$> Sync.empty @a) spin +-- |Run an action repeatedly until the 'Sync' variable is available, waiting for the specified time between executions. whileEmptyInterval ::   ∀ a u t d r .   TimeUnit u =>@@ -31,4 +34,12 @@   where     spin = do       action-      whenM (isJust <$> Sync.try @a) (Time.sleep @t @d interval *> spin)+      whenM (not <$> Sync.empty @a) (Time.sleep @t @d interval *> spin)++-- |Run an action with a locally scoped 'Sync' variable.+withSync ::+  ∀ d res r .+  Member (Scoped (SyncResources res) (Sync d)) r =>+  InterpreterFor (Sync d) r+withSync =+  scoped @(SyncResources res)
polysemy-conc.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           polysemy-conc-version:        0.1.1.0+version:        0.2.0.0 synopsis:       Polysemy Effects for Concurrency description:    See <https://hackage.haskell.org/package/polysemy-conc/docs/Polysemy-Conc.html> category:       Concurrency@@ -19,7 +19,7 @@ build-type:     Simple extra-source-files:     readme.md-    Changelog.md+    changelog.md  source-repository head   type: git@@ -28,6 +28,8 @@ library   exposed-modules:       Polysemy.Conc+      Polysemy.Conc.Async+      Polysemy.Conc.AtomicState       Polysemy.Conc.Critical       Polysemy.Conc.Data.Critical       Polysemy.Conc.Data.Interrupt@@ -37,16 +39,19 @@       Polysemy.Conc.Effect.Events       Polysemy.Conc.Effect.Scoped       Polysemy.Conc.Effect.Sync+      Polysemy.Conc.Events       Polysemy.Conc.Interpreter.Events+      Polysemy.Conc.Interpreter.Queue.Pure+      Polysemy.Conc.Interpreter.Queue.TB+      Polysemy.Conc.Interpreter.Queue.TBM       Polysemy.Conc.Interpreter.Sync       Polysemy.Conc.Interrupt       Polysemy.Conc.Prelude       Polysemy.Conc.Queue       Polysemy.Conc.Queue.Result-      Polysemy.Conc.Queue.TB-      Polysemy.Conc.Queue.TBM       Polysemy.Conc.Queue.Timeout       Polysemy.Conc.Race+      Polysemy.Conc.Retry       Polysemy.Conc.Sync   other-modules:       Prelude@@ -204,23 +209,13 @@       ViewPatterns   ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -Wunused-packages -threaded -rtsopts -with-rtsopts=-N   build-depends:-      async-    , base ==4.*-    , containers-    , hedgehog+      base ==4.*     , polysemy >=1.5     , polysemy-conc     , polysemy-test     , polysemy-time >=0.1.2.1-    , relude >=0.7     , stm-    , stm-chans >=2-    , string-interpolate >=0.2     , tasty-    , tasty-hedgehog-    , template-haskell-    , text-    , time     , unagi-chan >=0.4     , unix   mixins:
test/Polysemy/Conc/Test/QueueTest.hs view
@@ -1,19 +1,19 @@ module Polysemy.Conc.Test.QueueTest where -import Polysemy.Async (asyncToIOFinal, Async)+import Polysemy.Async (Async, asyncToIOFinal) import Polysemy.Test (UnitTest, assertEq, assertJust, runTestAuto)-import Polysemy.Time (MilliSeconds(MilliSeconds), interpretTimeGhc)+import Polysemy.Time (MilliSeconds (MilliSeconds), interpretTimeGhc) import Polysemy.Time.Ghc (GhcTime)  import qualified Polysemy.Conc.Data.Queue as Queue import Polysemy.Conc.Data.Queue (Queue) import qualified Polysemy.Conc.Data.QueueResult as QueueResult+import Polysemy.Conc.Data.QueueResult (QueueResult) import Polysemy.Conc.Data.Race (Race)+import Polysemy.Conc.Interpreter.Queue.TB (interpretQueueTB)+import Polysemy.Conc.Interpreter.Queue.TBM (interpretQueueTBM) import Polysemy.Conc.Queue.Result (resultToMaybe)-import Polysemy.Conc.Queue.TB (interpretQueueTB)-import Polysemy.Conc.Queue.TBM (interpretQueueTBM) import Polysemy.Conc.Race (interpretRace)-import Polysemy.Conc.Data.QueueResult (QueueResult)  progSuccess ::   Member (Queue Int) r =>