packages feed

polysemy-conc 0.1.0.3 → 0.1.1.0

raw patch · 13 files changed

+366/−116 lines, 13 filesdep +unagi-chandep ~polysemyPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: unagi-chan

Dependency ranges changed: polysemy

API changes (from Hackage documentation)

- Polysemy.Conc.Data.Sync: [Block] :: Sync d m d
- Polysemy.Conc.Data.Sync: [Empty] :: Sync d m Bool
- Polysemy.Conc.Data.Sync: [PutBlock] :: d -> Sync d m ()
- Polysemy.Conc.Data.Sync: [PutTry] :: d -> Sync d m Bool
- Polysemy.Conc.Data.Sync: [PutWait] :: TimeUnit u => u -> d -> Sync d m Bool
- Polysemy.Conc.Data.Sync: [TakeBlock] :: Sync d m d
- Polysemy.Conc.Data.Sync: [TakeTry] :: Sync d m (Maybe d)
- Polysemy.Conc.Data.Sync: [TakeWait] :: TimeUnit u => u -> Sync d m (Maybe d)
- Polysemy.Conc.Data.Sync: [Try] :: Sync d m (Maybe d)
- Polysemy.Conc.Data.Sync: [Wait] :: TimeUnit u => u -> Sync d m (Maybe d)
- Polysemy.Conc.Data.Sync: data Sync d :: Effect
- Polysemy.Conc.Sync: interpretSync :: forall d r. Members [Race, Embed IO] r => InterpreterFor (Sync d) r
- Polysemy.Conc.Sync: interpretSyncAs :: forall d r. Members [Race, Embed IO] r => d -> InterpreterFor (Sync d) r
- Polysemy.Conc.Sync: interpretSyncWith :: forall d r. Members [Race, Embed IO] r => MVar d -> 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: data Events (token :: Type) (e :: Type) :: Effect
+ Polysemy.Conc: interpretEventsChan :: forall e r. Member (Embed IO) r => InterpretersFor [Events (OutChan e) e, Scoped (EventToken (OutChan e)) (Consume 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.Effect.Events: EventToken :: token -> EventToken token
+ Polysemy.Conc.Effect.Events: [$sel:unEventToken:EventToken] :: EventToken token -> token
+ Polysemy.Conc.Effect.Events: [Consume] :: Consume e m e
+ Polysemy.Conc.Effect.Events: [Publish] :: e -> Events token e m ()
+ Polysemy.Conc.Effect.Events: data Consume (e :: Type) :: Effect
+ Polysemy.Conc.Effect.Events: data Events (token :: Type) (e :: Type) :: Effect
+ Polysemy.Conc.Effect.Events: instance GHC.Classes.Eq token => GHC.Classes.Eq (Polysemy.Conc.Effect.Events.EventToken token)
+ Polysemy.Conc.Effect.Events: instance GHC.Generics.Generic (Polysemy.Conc.Effect.Events.EventToken token)
+ Polysemy.Conc.Effect.Events: instance GHC.Show.Show token => GHC.Show.Show (Polysemy.Conc.Effect.Events.EventToken token)
+ Polysemy.Conc.Effect.Events: newtype EventToken token
+ Polysemy.Conc.Effect.Events: subscribe :: forall e token r. Member (Scoped (EventToken token) (Consume e)) r => InterpreterFor (Consume e) r
+ Polysemy.Conc.Effect.Scoped: [InScope] :: forall resource effect m a. (resource -> m a) -> Scoped resource effect m a
+ Polysemy.Conc.Effect.Scoped: [Run] :: forall resource effect m a. resource -> effect m a -> Scoped resource effect m a
+ Polysemy.Conc.Effect.Scoped: data Scoped (resource :: Type) (effect :: Effect) :: Effect
+ Polysemy.Conc.Effect.Scoped: runScoped :: forall resource effect r. (forall x. (resource -> Sem r x) -> Sem r x) -> (resource -> InterpreterFor effect r) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc.Effect.Scoped: runScopedAs :: forall resource effect r. Sem r resource -> (resource -> InterpreterFor effect r) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc.Effect.Scoped: scoped :: forall resource effect r. Member (Scoped resource effect) r => InterpreterFor effect r
+ Polysemy.Conc.Effect.Sync: [Block] :: Sync d m d
+ Polysemy.Conc.Effect.Sync: [Empty] :: Sync d m Bool
+ Polysemy.Conc.Effect.Sync: [PutBlock] :: d -> Sync d m ()
+ Polysemy.Conc.Effect.Sync: [PutTry] :: d -> Sync d m Bool
+ Polysemy.Conc.Effect.Sync: [PutWait] :: TimeUnit u => u -> d -> Sync d m Bool
+ Polysemy.Conc.Effect.Sync: [TakeBlock] :: Sync d m d
+ Polysemy.Conc.Effect.Sync: [TakeTry] :: Sync d m (Maybe d)
+ Polysemy.Conc.Effect.Sync: [TakeWait] :: TimeUnit u => u -> Sync d m (Maybe d)
+ Polysemy.Conc.Effect.Sync: [Try] :: Sync d m (Maybe d)
+ Polysemy.Conc.Effect.Sync: [Wait] :: TimeUnit u => u -> Sync d m (Maybe d)
+ Polysemy.Conc.Effect.Sync: data Sync d :: Effect
+ Polysemy.Conc.Interpreter.Events: interpretConsumeChan :: forall e r. Member (Embed IO) r => EventToken (OutChan 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: interpretEventsInChan :: forall e r. Member (Embed IO) r => InChan e -> InterpreterFor (Events (OutChan e) e) r
+ Polysemy.Conc.Interpreter.Sync: interpretSync :: forall d r. Members [Race, Embed IO] r => InterpreterFor (Sync d) r
+ Polysemy.Conc.Interpreter.Sync: interpretSyncAs :: forall d r. Members [Race, Embed IO] r => d -> InterpreterFor (Sync d) r
+ Polysemy.Conc.Interpreter.Sync: interpretSyncWith :: forall d r. Members [Race, Embed IO] r => MVar d -> InterpreterFor (Sync d) r
+ Polysemy.Conc.Sync: whileEmpty :: forall a r. Member (Sync a) r => Sem r () -> Sem r ()
+ Polysemy.Conc.Sync: whileEmptyInterval :: forall a u t d r. TimeUnit u => Members [Time t d, Sync a] r => u -> Sem r () -> Sem r ()

Files

lib/Polysemy/Conc.hs view
@@ -41,13 +41,24 @@    -- ** Interpreters   interpretInterrupt,++  -- * Event Channels+  Events,+  publish,+  consume,++  -- ** Interpreters+  interpretEventsChan, ) where  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.Data.Sync (Sync)+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 (   interpretQueueListReadOnlyAtomic,@@ -59,7 +70,6 @@ import Polysemy.Conc.Queue.TB (interpretQueueTB) import Polysemy.Conc.Queue.TBM (interpretQueueTBM) import Polysemy.Conc.Race (interpretRace, race_, timeout_)-import Polysemy.Conc.Sync (interpretSync)  -- $intro -- This library provides an assortment of tools for concurrency-related tasks:
− lib/Polysemy/Conc/Data/Sync.hs
@@ -1,42 +0,0 @@-{-# options_haddock prune #-}--- |Description: Sync effect-module Polysemy.Conc.Data.Sync where--import Polysemy.Time (TimeUnit)---- |Abstracts an 'MVar'.------ For documentation on the constructors, see the module "Polysemy.Conc.Data.Sync".------ @--- import Polysemy.Conc (Sync)--- import qualified Polysemy.Conc.Data.Sync as Sync------ prog :: Member (Sync Int) r => Sem r Int--- prog = do---   Sync.putTry 5---   Sync.takeBlock--- @-data Sync d :: Effect where-  -- |Read the variable, waiting until a value is available.-  Block :: Sync d m d-  -- |Read the variable, waiting until a value is available or the timeout has expired.-  Wait :: TimeUnit u => u -> Sync d m (Maybe d)-  -- |Read the variable, returning 'Nothing' immmediately if no value was available.-  Try :: Sync d m (Maybe d)-  -- |Take the variable, waiting until a value is available.-  TakeBlock :: Sync d m d-  -- |Take the variable, waiting until a value is available or the timeout has expired.-  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)-  -- |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.-  PutWait :: TimeUnit u => u -> d -> Sync d m Bool-  -- |Write the variable, returning 'False' immmediately if no value was available.-  PutTry :: d -> Sync d m Bool-  -- |Indicate whether the variable is empty.-  Empty :: Sync d m Bool--makeSem ''Sync
+ lib/Polysemy/Conc/Effect/Events.hs view
@@ -0,0 +1,31 @@+{-# options_haddock prune #-}+-- |Description: Events/Consume Effects, Internal+module Polysemy.Conc.Effect.Events where++import Polysemy.Conc.Effect.Scoped (Scoped, scoped)++-- |Consume events emitted by 'Events'.+data Consume (e :: Type) :: Effect where+  Consume :: Consume e m e++makeSem ''Consume++-- |Marker for the 'Scoped' token for 'Events'.+newtype EventToken token =+  EventToken { unEventToken :: token }+  deriving (Eq, Show, Generic)++-- |An event publisher that can be consumed from multiple threads.+data Events (token :: Type) (e :: Type) :: Effect where+  Publish :: e -> Events token e m ()++makeSem ''Events++-- |Create a new scope for 'Events', causing the nested program to get its own copy of the event stream.+-- To be used with 'Polysemy.Conc.interpretEventsChan'.+subscribe ::+  ∀ e token r .+  Member (Scoped (EventToken token) (Consume e)) r =>+  InterpreterFor (Consume e) r+subscribe =+  scoped @(EventToken token)
+ lib/Polysemy/Conc/Effect/Scoped.hs view
@@ -0,0 +1,73 @@+{-# options_haddock prune #-}+-- |Description: Scoped Effect, Internal+module Polysemy.Conc.Effect.Scoped where++import Polysemy (transform)+import Polysemy.Internal (Sem (Sem, runSem), liftSem, send)+import Polysemy.Internal.Union (Weaving (Weaving), decomp, hoist, injWeaving)++-- |@Scoped@ transforms a program so that @effect@ is associated with a @resource@ within that program.+-- This requires the interpreter for @effect@ to be parameterized by @resource@ and constructed for every program using+-- @Scoped@ separately.+--+-- An application for this is 'Polysemy.Conc.Events', in which each program using the effect 'Consume' is interpreted+-- with its own copy of the event channel; or a database transaction, in which a transaction handle is created for the+-- wrapped program and passed to the interpreter for the database effect.+--+-- Resource creation is performed by the function passed to 'runScoped'.+--+-- The constructors are not intended to be used directly; the smart constructor 'scoped' is used like a local+-- interpreter for @effect@.+data Scoped (resource :: Type) (effect :: Effect) :: Effect where+  Run :: ∀ resource effect m a . resource -> effect m a -> Scoped resource effect m a+  InScope :: ∀ resource effect m a . (resource -> m a) -> Scoped resource effect m a++interpretH' ::+  ∀ e r .+  (∀ x . Weaving e (Sem (e : r)) x -> Sem r x) ->+  InterpreterFor e r+interpretH' h sem =+  Sem \ k -> runSem sem $ decomp >>> \case+    Right wav -> runSem (h wav) k+    Left g -> k $ hoist (interpretH' h) g++-- |Constructor for 'Scoped', taking a nested program and transforming all instances of @effect@ to+-- @Scoped resource effect@.+scoped ::+  ∀ resource effect r .+  Member (Scoped resource effect) r =>+  InterpreterFor effect r+scoped main =+  send $ InScope @resource @effect \ resource ->+    transform @effect (Run resource) main++-- |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.+--+-- @scopedInterpreter@ is a regular interpreter that is called with the @resource@ argument produced by @scope@.+runScoped ::+  ∀ resource effect r .+  (∀ x . (resource -> Sem r x) -> Sem r x) ->+  (resource -> InterpreterFor effect r) ->+  InterpreterFor (Scoped resource effect) r+runScoped scope scopedInterpreter =+  run+  where+    run :: InterpreterFor (Scoped resource effect) r+    run =+      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))++-- |Variant of 'Scoped' in which the resource allocator is a plain action.+runScopedAs ::+  ∀ resource effect r .+  Sem r resource ->+  (resource -> InterpreterFor effect r) ->+  InterpreterFor (Scoped resource effect) r+runScopedAs resource =+  runScoped \ f -> f =<< resource
+ lib/Polysemy/Conc/Effect/Sync.hs view
@@ -0,0 +1,42 @@+{-# options_haddock prune #-}+-- |Description: Sync effect+module Polysemy.Conc.Effect.Sync where++import Polysemy.Time (TimeUnit)++-- |Abstracts an 'MVar'.+--+-- For documentation on the constructors, see the module "Polysemy.Conc.Effect.Sync".+--+-- @+-- import Polysemy.Conc (Sync)+-- import qualified Polysemy.Conc.Effect.Sync as Sync+--+-- prog :: Member (Sync Int) r => Sem r Int+-- prog = do+--   Sync.putTry 5+--   Sync.takeBlock+-- @+data Sync d :: Effect where+  -- |Read the variable, waiting until a value is available.+  Block :: Sync d m d+  -- |Read the variable, waiting until a value is available or the timeout has expired.+  Wait :: TimeUnit u => u -> Sync d m (Maybe d)+  -- |Read the variable, returning 'Nothing' immmediately if no value was available.+  Try :: Sync d m (Maybe d)+  -- |Take the variable, waiting until a value is available.+  TakeBlock :: Sync d m d+  -- |Take the variable, waiting until a value is available or the timeout has expired.+  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)+  -- |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.+  PutWait :: TimeUnit u => u -> d -> Sync d m Bool+  -- |Write the variable, returning 'False' immmediately if no value was available.+  PutTry :: d -> Sync d m Bool+  -- |Indicate whether the variable is empty.+  Empty :: Sync d m Bool++makeSem ''Sync
+ lib/Polysemy/Conc/Interpreter/Events.hs view
@@ -0,0 +1,58 @@+{-# options_haddock prune #-}+-- |Description: Events/Consume Interpreters, Internal+module Polysemy.Conc.Interpreter.Events where++import Control.Concurrent.Chan.Unagi.Bounded (InChan, OutChan, dupChan, newChan, readChan, tryWriteChan)+import Polysemy (InterpretersFor)++import qualified Polysemy.Conc.Effect.Events as Events+import Polysemy.Conc.Effect.Events (EventToken (EventToken), Events, Consume)+import Polysemy.Conc.Effect.Scoped (Scoped, runScopedAs)++-- |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) ->+  InterpreterFor (Consume e) r+interpretConsumeChan (EventToken chan) =+  interpret \case+    Events.Consume ->+      embed (readChan chan)++-- |Interpret 'Events' by writing to an 'InChan'.+-- Used internally by 'interpretEventsChan', not safe to use directly.+-- When the channel queue is full, this silently discards events.+interpretEventsInChan ::+  ∀ e r .+  Member (Embed IO) r =>+  InChan e ->+  InterpreterFor (Events (OutChan e) e) r+interpretEventsInChan inChan =+  interpret \case+    Events.Publish e ->+      void (embed (tryWriteChan inChan e))++-- |Interpret 'Events' and 'Consume' together by connecting them to the two ends of an unagi channel.+-- 'Consume' is only interpreted in a 'Scoped' manner, ensuring that a new duplicate of the channel is created so that+-- all consumers see all events (from the moment they are connected).+--+-- This should be used in conjunction with 'Polysemy.Conc.subscribe':+--+-- @+-- interpretEventsChan do+--   async $ subscribe do+--     putStrLn =<< consume+--   publish "hello"+-- @+--+-- Whenever 'Polysemy.Conc.subscribe' creates a new scope, this interpreter calls 'dupChan' and passes the+-- duplicate to 'interpretConsumeChan'.+interpretEventsChan ::+  ∀ e r .+  Member (Embed IO) r =>+  InterpretersFor [Events (OutChan e) e, Scoped (EventToken (OutChan e)) (Consume e)] r+interpretEventsChan sem = do+  (inChan, _) <- embed (newChan @e 64)+  runScopedAs (EventToken <$> embed (dupChan inChan)) interpretConsumeChan (interpretEventsInChan inChan sem)
+ lib/Polysemy/Conc/Interpreter/Sync.hs view
@@ -0,0 +1,58 @@+-- |Description: Sync interpreters+module Polysemy.Conc.Interpreter.Sync where++import Control.Concurrent (isEmptyMVar)++import qualified Polysemy.Conc.Data.Race as Race+import Polysemy.Conc.Data.Race (Race)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Effect.Sync (Sync)+import qualified Polysemy.Conc.Race as Race++-- |Interpret 'Sync' with the provided 'MVar'.+interpretSyncWith ::+  ∀ d r .+  Members [Race, Embed IO] r =>+  MVar d ->+  InterpreterFor (Sync d) r+interpretSyncWith var =+  interpret \case+    Sync.Block ->+      readMVar var+    Sync.Wait interval ->+      rightToMaybe <$> Race.timeout () interval (readMVar var)+    Sync.Try ->+      tryReadMVar var+    Sync.TakeBlock ->+      takeMVar var+    Sync.TakeWait interval ->+      rightToMaybe <$> Race.timeout () interval (takeMVar var)+    Sync.TakeTry ->+      tryTakeMVar var+    Sync.PutBlock d ->+      putMVar var d+    Sync.PutWait interval d ->+      Race.timeout_ False interval (True <$ putMVar var d)+    Sync.PutTry d ->+      tryPutMVar var d+    Sync.Empty ->+      embed (isEmptyMVar var)++-- |Interpret 'Sync' with an empty 'MVar'.+interpretSync ::+  ∀ d r .+  Members [Race, Embed IO] r =>+  InterpreterFor (Sync d) r+interpretSync sem = do+  var <- newEmptyMVar+  interpretSyncWith var sem++-- |Interpret 'Sync' with an 'MVar' containing the specified value.+interpretSyncAs ::+  ∀ d r .+  Members [Race, Embed IO] r =>+  d ->+  InterpreterFor (Sync d) r+interpretSyncAs d sem = do+  var <- newMVar d+  interpretSyncWith var sem
lib/Polysemy/Conc/Interrupt.hs view
@@ -10,16 +10,16 @@ import Polysemy (getInspectorT, inspect, interpretH, runT) import Polysemy.Async (Async, async, await, cancel) import Polysemy.Internal.Tactics (liftT)-import Polysemy.Time (Seconds(Seconds))-import System.Posix.Signals (Handler(CatchOnce, CatchInfoOnce), SignalInfo, installHandler, keyboardSignal)+import Polysemy.Time (Seconds (Seconds))+import System.Posix.Signals (Handler (CatchInfoOnce, CatchOnce), SignalInfo, installHandler, keyboardSignal)  import qualified Polysemy.Conc.Data.Critical as Critical import Polysemy.Conc.Data.Critical (Critical)-import Polysemy.Conc.Data.Interrupt (Interrupt(..))+import Polysemy.Conc.Data.Interrupt (Interrupt (..)) import Polysemy.Conc.Data.Race (Race)-import qualified Polysemy.Conc.Data.Sync as Sync+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Interpreter.Sync (interpretSync) import Polysemy.Conc.Race (race_)-import Polysemy.Conc.Sync (interpretSync)  putErr ::   Member (Embed IO) r =>@@ -87,10 +87,10 @@   Sem r (SignalInfo -> Sem r ()) execInterrupt = do   InterruptState quitSignal finishSignal _ orig _ <- atomicGet-  putMVar quitSignal ()-  traverse_ (uncurry processHandler) . Map.toList =<< atomicGets handlers-  checkListeners-  takeMVar finishSignal+  whenM (tryPutMVar quitSignal ()) do+    traverse_ (uncurry processHandler) . Map.toList =<< atomicGets handlers+    checkListeners+    takeMVar finishSignal   embed . orig <$ putErr "interrupt handlers finished"  registerHandler ::
lib/Polysemy/Conc/Prelude.hs view
@@ -1,7 +1,8 @@ {-# options_haddock hide #-}-{-# LANGUAGE NoImplicitPrelude #-}+{-# language NoImplicitPrelude #-}  module Polysemy.Conc.Prelude (+  module Data.Kind,   module Polysemy.Conc.Prelude,   module GHC.Err,   module Polysemy,@@ -9,6 +10,7 @@   module Relude, ) where +import Data.Kind (Type) import qualified Data.String.Interpolate as Interpolate import GHC.Err (undefined) import Language.Haskell.TH.Quote (QuasiQuoter)
lib/Polysemy/Conc/Sync.hs view
@@ -1,58 +1,34 @@--- |Description: Sync interpreters+-- |Description: Sync Combinators module Polysemy.Conc.Sync where -import Control.Concurrent (isEmptyMVar)--import qualified Polysemy.Conc.Data.Race as Race-import Polysemy.Conc.Data.Race (Race)-import qualified Polysemy.Conc.Data.Sync as Sync-import Polysemy.Conc.Data.Sync (Sync)-import qualified Polysemy.Conc.Race as Race+import qualified Polysemy.Time as Time+import Polysemy.Time (Time, TimeUnit) --- |Interpret 'Sync' with the provided 'MVar'.-interpretSyncWith ::-  ∀ d r .-  Members [Race, Embed IO] r =>-  MVar d ->-  InterpreterFor (Sync d) r-interpretSyncWith var =-  interpret \case-    Sync.Block ->-      readMVar var-    Sync.Wait interval ->-      rightToMaybe <$> Race.timeout () interval (readMVar var)-    Sync.Try ->-      tryReadMVar var-    Sync.TakeBlock ->-      takeMVar var-    Sync.TakeWait interval ->-      rightToMaybe <$> Race.timeout () interval (takeMVar var)-    Sync.TakeTry ->-      tryTakeMVar var-    Sync.PutBlock d ->-      putMVar var d-    Sync.PutWait interval d ->-      Race.timeout_ False interval (True <$ putMVar var d)-    Sync.PutTry d ->-      tryPutMVar var d-    Sync.Empty ->-      embed (isEmptyMVar var)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Effect.Sync (Sync) --- |Interpret 'Sync' with an empty 'MVar'.-interpretSync ::-  ∀ d r .-  Members [Race, Embed IO] r =>-  InterpreterFor (Sync d) r-interpretSync sem = do-  var <- newEmptyMVar-  interpretSyncWith var sem+whileEmpty ::+  ∀ a r .+  Member (Sync a) r =>+  Sem r () ->+  Sem r ()+whileEmpty action =+  spin+  where+    spin = do+      action+      whenM (isJust <$> Sync.try @a) spin --- |Interpret 'Sync' with an 'MVar' containing the specified value.-interpretSyncAs ::-  ∀ d r .-  Members [Race, Embed IO] r =>-  d ->-  InterpreterFor (Sync d) r-interpretSyncAs d sem = do-  var <- newMVar d-  interpretSyncWith var sem+whileEmptyInterval ::+  ∀ a u t d r .+  TimeUnit u =>+  Members [Time t d, Sync a] r =>+  u ->+  Sem r () ->+  Sem r ()+whileEmptyInterval interval action =+  spin+  where+    spin = do+      action+      whenM (isJust <$> Sync.try @a) (Time.sleep @t @d interval *> spin)
polysemy-conc.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           polysemy-conc-version:        0.1.0.3+version:        0.1.1.0 synopsis:       Polysemy Effects for Concurrency description:    See <https://hackage.haskell.org/package/polysemy-conc/docs/Polysemy-Conc.html> category:       Concurrency@@ -34,7 +34,11 @@       Polysemy.Conc.Data.Queue       Polysemy.Conc.Data.QueueResult       Polysemy.Conc.Data.Race-      Polysemy.Conc.Data.Sync+      Polysemy.Conc.Effect.Events+      Polysemy.Conc.Effect.Scoped+      Polysemy.Conc.Effect.Sync+      Polysemy.Conc.Interpreter.Events+      Polysemy.Conc.Interpreter.Sync       Polysemy.Conc.Interrupt       Polysemy.Conc.Prelude       Polysemy.Conc.Queue@@ -109,12 +113,12 @@       UndecidableInstances       UnicodeSyntax       ViewPatterns-  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints+  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -Wunused-packages   build-depends:       async     , base ==4.*     , containers-    , polysemy >=1.3+    , polysemy >=1.5     , polysemy-time >=0.1.2.1     , relude >=0.7     , stm@@ -123,6 +127,7 @@     , template-haskell     , text     , time+    , unagi-chan >=0.4     , unix   mixins:       base hiding (Prelude)@@ -132,6 +137,7 @@   type: exitcode-stdio-1.0   main-is: Main.hs   other-modules:+      Polysemy.Conc.Test.EventsTest       Polysemy.Conc.Test.InterruptTest       Polysemy.Conc.Test.QueueTest       Polysemy.Conc.Test.SyncTest@@ -196,13 +202,13 @@       UndecidableInstances       UnicodeSyntax       ViewPatterns-  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N+  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -Wunused-packages -threaded -rtsopts -with-rtsopts=-N   build-depends:       async     , base ==4.*     , containers     , hedgehog-    , polysemy >=1.3+    , polysemy >=1.5     , polysemy-conc     , polysemy-test     , polysemy-time >=0.1.2.1@@ -215,6 +221,7 @@     , template-haskell     , text     , time+    , unagi-chan >=0.4     , unix   mixins:       base hiding (Prelude)
+ test/Polysemy/Conc/Test/EventsTest.hs view
@@ -0,0 +1,35 @@+module Polysemy.Conc.Test.EventsTest where++import Control.Concurrent.Chan.Unagi.Bounded (OutChan)+import Polysemy.Async (async, asyncToIOFinal, await)+import Polysemy.Test (UnitTest, assertJust, runTestAuto)++import qualified Polysemy.Conc.Effect.Events as Events+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Interpreter.Events (interpretEventsChan)+import Polysemy.Conc.Interpreter.Sync (interpretSync)+import Polysemy.Conc.Race (interpretRace)++test_events :: UnitTest+test_events =+  (runTestAuto . asyncToIOFinal) do+    interpretRace $ interpretSync @() $ interpretEventsChan @Int $ interpretEventsChan @Text do+      thread1 <- async do+        Events.subscribe @Int @(OutChan Int) do+          Events.consume @Int+      thread2 <- async do+        Events.subscribe @Int @(OutChan Int) do+          Events.consume @Int+      thread3 <- async do+        Events.subscribe @Text @(OutChan Text) do+          Sync.putBlock ()+          Events.consume @Text+      Sync.takeBlock @()+      Events.publish @(OutChan Text) ("test" :: Text)+      Events.publish @(OutChan Int) (1 :: Int)+      num1 <- await thread1+      num2 <- await thread2+      text1 <- await thread3+      assertJust @_ @IO 1 num1+      assertJust @_ @IO 1 num2+      assertJust @_ @IO "test" text1
test/Polysemy/Conc/Test/SyncTest.hs view
@@ -4,10 +4,10 @@ import Polysemy.Test (UnitTest, assertEq, runTestAuto)  import Polysemy.Conc.Data.Race (Race)-import qualified Polysemy.Conc.Data.Sync as Sync-import Polysemy.Conc.Data.Sync (Sync)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Effect.Sync (Sync)+import Polysemy.Conc.Interpreter.Sync (interpretSync) import Polysemy.Conc.Race (interpretRace)-import Polysemy.Conc.Sync (interpretSync)  data Thread1 = Thread1 data Thread2 = Thread2