packages feed

polysemy-conc 0.4.0.1 → 0.5.0.0

raw patch · 18 files changed

+719/−34 lines, 18 filesdep +data-defaultdep +polysemy-plugindep +torsordep ~polysemydep ~polysemy-timedep ~reludePVP ok

version bump matches the API change (PVP)

Dependencies added: data-default, polysemy-plugin, torsor

Dependency ranges changed: polysemy, polysemy-time, relude, unagi-chan

API changes (from Hackage documentation)

- Polysemy.Conc: data EventToken token
- Polysemy.Conc.Effect.Events: EventToken :: token -> EventToken token
- Polysemy.Conc.Effect.Events: [$sel:unEventToken:EventToken] :: EventToken token -> token
- 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: ClockSkewConfig :: NanoSeconds -> NanoSeconds -> ClockSkewConfig
+ Polysemy.Conc: MonitorResource :: a -> MonitorResource a
+ Polysemy.Conc: clockSkewConfig :: TimeUnit u1 => TimeUnit u2 => u1 -> u2 -> ClockSkewConfig
+ Polysemy.Conc: data ClockSkewConfig
+ Polysemy.Conc: data EventResource resource
+ Polysemy.Conc: data Monitor (action :: Type) :: Effect
+ Polysemy.Conc: data Restart
+ Polysemy.Conc: data Scoped (resource :: Type) (effect :: Effect) :: Effect
+ Polysemy.Conc: interpretMaskFinal :: Member (Final IO) r => InterpreterFor (Mask Restoration) r
+ Polysemy.Conc: interpretMonitorPure :: InterpreterFor (ScopedMonitor () action) r
+ Polysemy.Conc: interpretMonitorRestart :: forall t d r. Members [Time t d, Resource, Async, Race, Final IO] r => MonitorCheck r -> InterpreterFor (RestartingMonitor CancelResource) r
+ Polysemy.Conc: interpretScoped :: forall resource effect r. (forall x. (resource -> Sem r x) -> Sem r x) -> (forall r0 x. resource -> effect (Sem r0) x -> Sem r x) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc: interpretScopedAs :: forall resource effect r. Sem r resource -> (forall r0 x. resource -> effect (Sem r0) x -> Sem r x) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc: interpretScopedH :: forall resource effect r. (forall x. (resource -> Sem r x) -> Sem r x) -> (forall r0 x. resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r x) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc: interpretUninterruptibleMaskFinal :: Member (Final IO) r => InterpreterFor (UninterruptipleMask Restoration) r
+ Polysemy.Conc: mask :: forall resource r. Member (Mask resource) r => InterpreterFor RestoreMask r
+ Polysemy.Conc: monitor :: forall action r a. Member (Monitor action) r => Sem r a -> Sem r a
+ Polysemy.Conc: monitorClockSkew :: forall t d diff r. Torsor t diff => TimeUnit diff => Members [AtomicState (Maybe t), Time t d, Embed IO] r => ClockSkewConfig -> MonitorCheck r
+ Polysemy.Conc: newtype MonitorResource a
+ Polysemy.Conc: restart :: forall resource r. Member (ScopedMonitor resource Restart) r => InterpreterFor (Monitor Restart) r
+ Polysemy.Conc: restore :: forall r a. Member RestoreMask r => Sem r a -> Sem r a
+ Polysemy.Conc: runConc :: Sem ConcStack a -> IO a
+ Polysemy.Conc: 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: runScopedAs :: forall resource effect r. Sem r resource -> (resource -> InterpreterFor effect r) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc: scoped :: forall resource effect r. Member (Scoped resource effect) r => InterpreterFor effect r
+ Polysemy.Conc: type ConcStack = [UninterruptipleMask Restoration, Mask Restoration, Race, Async, Resource, Embed IO, Final IO]
+ Polysemy.Conc: type Mask resource = Scoped (MaskResource resource) RestoreMask
+ Polysemy.Conc: type RestartingMonitor (resource :: Type) = ScopedMonitor resource Restart
+ Polysemy.Conc: type ScopedMonitor (resource :: Type) (action :: Type) = Scoped (MonitorResource resource) (Monitor action)
+ Polysemy.Conc: type UninterruptipleMask resource = Scoped (UninterruptipleMaskResource resource) RestoreMask
+ Polysemy.Conc: uninterruptibleMask :: forall resource r. Member (UninterruptipleMask resource) r => InterpreterFor RestoreMask r
+ Polysemy.Conc: withMonitor :: forall resource action r. Member (ScopedMonitor resource action) r => InterpreterFor (Monitor action) r
+ Polysemy.Conc.Effect.Events: EventResource :: resource -> EventResource resource
+ Polysemy.Conc.Effect.Events: [$sel:unEventToken:EventResource] :: EventResource resource -> resource
+ Polysemy.Conc.Effect.Events: instance GHC.Classes.Eq resource => GHC.Classes.Eq (Polysemy.Conc.Effect.Events.EventResource resource)
+ Polysemy.Conc.Effect.Events: instance GHC.Generics.Generic (Polysemy.Conc.Effect.Events.EventResource resource)
+ Polysemy.Conc.Effect.Events: instance GHC.Show.Show resource => GHC.Show.Show (Polysemy.Conc.Effect.Events.EventResource resource)
+ Polysemy.Conc.Effect.Events: newtype EventResource resource
+ Polysemy.Conc.Effect.Mask: [Restore] :: m a -> RestoreMask m a
+ Polysemy.Conc.Effect.Mask: data RestoreMask :: Effect
+ Polysemy.Conc.Effect.Mask: mask :: forall resource r. Member (Mask resource) r => InterpreterFor RestoreMask r
+ Polysemy.Conc.Effect.Mask: restore :: forall r a. Member RestoreMask r => Sem r a -> Sem r a
+ Polysemy.Conc.Effect.Mask: type Mask resource = Scoped (MaskResource resource) RestoreMask
+ Polysemy.Conc.Effect.Mask: type UninterruptipleMask resource = Scoped (UninterruptipleMaskResource resource) RestoreMask
+ Polysemy.Conc.Effect.Mask: uninterruptibleMask :: forall resource r. Member (UninterruptipleMask resource) r => InterpreterFor RestoreMask r
+ Polysemy.Conc.Effect.Monitor: MonitorCheck :: NanoSeconds -> (MVar () -> Sem r ()) -> MonitorCheck r
+ Polysemy.Conc.Effect.Monitor: MonitorResource :: a -> MonitorResource a
+ Polysemy.Conc.Effect.Monitor: Restart :: Restart
+ Polysemy.Conc.Effect.Monitor: [$sel:check:MonitorCheck] :: MonitorCheck r -> MVar () -> Sem r ()
+ Polysemy.Conc.Effect.Monitor: [$sel:interval:MonitorCheck] :: MonitorCheck r -> NanoSeconds
+ Polysemy.Conc.Effect.Monitor: [$sel:unMonitorResource:MonitorResource] :: MonitorResource a -> a
+ Polysemy.Conc.Effect.Monitor: [Monitor] :: m a -> Monitor action m a
+ Polysemy.Conc.Effect.Monitor: data Monitor (action :: Type) :: Effect
+ Polysemy.Conc.Effect.Monitor: data MonitorCheck r
+ Polysemy.Conc.Effect.Monitor: data Restart
+ Polysemy.Conc.Effect.Monitor: instance GHC.Classes.Eq Polysemy.Conc.Effect.Monitor.Restart
+ Polysemy.Conc.Effect.Monitor: instance GHC.Show.Show Polysemy.Conc.Effect.Monitor.Restart
+ Polysemy.Conc.Effect.Monitor: monitor :: forall action r a. Member (Monitor action) r => Sem r a -> Sem r a
+ Polysemy.Conc.Effect.Monitor: newtype MonitorResource a
+ Polysemy.Conc.Effect.Monitor: restart :: forall resource r. Member (ScopedMonitor resource Restart) r => InterpreterFor (Monitor Restart) r
+ Polysemy.Conc.Effect.Monitor: type RestartingMonitor (resource :: Type) = ScopedMonitor resource Restart
+ Polysemy.Conc.Effect.Monitor: type ScopedMonitor (resource :: Type) (action :: Type) = Scoped (MonitorResource resource) (Monitor action)
+ Polysemy.Conc.Effect.Monitor: withMonitor :: forall resource action r. Member (ScopedMonitor resource action) r => InterpreterFor (Monitor action) r
+ Polysemy.Conc.Interpreter.Mask: interpretMaskFinal :: Member (Final IO) r => InterpreterFor (Mask Restoration) r
+ Polysemy.Conc.Interpreter.Mask: interpretUninterruptibleMaskFinal :: Member (Final IO) r => InterpreterFor (UninterruptipleMask Restoration) r
+ Polysemy.Conc.Interpreter.Monitor: instance GHC.Classes.Eq Polysemy.Conc.Interpreter.Monitor.MonitorCancel
+ Polysemy.Conc.Interpreter.Monitor: instance GHC.Exception.Type.Exception Polysemy.Conc.Interpreter.Monitor.MonitorCancel
+ Polysemy.Conc.Interpreter.Monitor: instance GHC.Show.Show Polysemy.Conc.Interpreter.Monitor.MonitorCancel
+ Polysemy.Conc.Interpreter.Monitor: interpretMonitorPure :: InterpreterFor (ScopedMonitor () action) r
+ Polysemy.Conc.Interpreter.Monitor: interpretMonitorRestart :: forall t d r. Members [Time t d, Resource, Async, Race, Final IO] r => MonitorCheck r -> InterpreterFor (RestartingMonitor CancelResource) r
+ Polysemy.Conc.Interpreter.Scoped: interpretScoped :: forall resource effect r. (forall x. (resource -> Sem r x) -> Sem r x) -> (forall r0 x. resource -> effect (Sem r0) x -> Sem r x) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc.Interpreter.Scoped: interpretScopedAs :: forall resource effect r. Sem r resource -> (forall r0 x. resource -> effect (Sem r0) x -> Sem r x) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc.Interpreter.Scoped: interpretScopedH :: forall resource effect r. (forall x. (resource -> Sem r x) -> Sem r x) -> (forall r0 x. resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r x) -> InterpreterFor (Scoped resource effect) r
+ Polysemy.Conc.Interpreter.Stack: runConc :: Sem ConcStack a -> IO a
+ Polysemy.Conc.Interpreter.Stack: type ConcStack = [UninterruptipleMask Restoration, Mask Restoration, Race, Async, Resource, Embed IO, Final IO]
+ Polysemy.Conc.Monitor: ClockSkewConfig :: NanoSeconds -> NanoSeconds -> ClockSkewConfig
+ Polysemy.Conc.Monitor: [$sel:interval:ClockSkewConfig] :: ClockSkewConfig -> NanoSeconds
+ Polysemy.Conc.Monitor: [$sel:tolerance:ClockSkewConfig] :: ClockSkewConfig -> NanoSeconds
+ Polysemy.Conc.Monitor: clockSkewConfig :: TimeUnit u1 => TimeUnit u2 => u1 -> u2 -> ClockSkewConfig
+ Polysemy.Conc.Monitor: data ClockSkewConfig
+ Polysemy.Conc.Monitor: instance Data.Default.Class.Default Polysemy.Conc.Monitor.ClockSkewConfig
+ Polysemy.Conc.Monitor: instance GHC.Classes.Eq Polysemy.Conc.Monitor.ClockSkewConfig
+ Polysemy.Conc.Monitor: instance GHC.Show.Show Polysemy.Conc.Monitor.ClockSkewConfig
+ Polysemy.Conc.Monitor: monitorClockSkew :: forall t d diff r. Torsor t diff => TimeUnit diff => Members [AtomicState (Maybe t), Time t d, Embed IO] r => ClockSkewConfig -> MonitorCheck r
- Polysemy.Conc: data Events (token :: Type) (e :: Type) :: Effect
+ Polysemy.Conc: data Events (resource :: Type) (e :: Type) :: Effect
- Polysemy.Conc: publish :: forall e token r. Member (Events token e) r => e -> Sem r ()
+ Polysemy.Conc: publish :: forall e resource r. Member (Events resource e) r => e -> Sem r ()
- Polysemy.Conc: subscribe :: forall e token r. Member (Scoped (EventToken token) (Consume e)) r => InterpreterFor (Consume e) r
+ Polysemy.Conc: subscribe :: forall e resource r. Member (Scoped (EventResource resource) (Consume e)) r => InterpreterFor (Consume e) r
- Polysemy.Conc: type EventChan e = EventToken (OutChan e)
+ Polysemy.Conc: type EventChan e = EventResource (OutChan e)
- Polysemy.Conc: type EventConsumer token e = Scoped (EventToken token) (Consume e)
+ Polysemy.Conc: type EventConsumer token e = Scoped (EventResource token) (Consume e)
- Polysemy.Conc.Effect.Events: [Publish] :: e -> Events token e m ()
+ Polysemy.Conc.Effect.Events: [Publish] :: e -> Events resource e m ()
- Polysemy.Conc.Effect.Events: data Events (token :: Type) (e :: Type) :: Effect
+ Polysemy.Conc.Effect.Events: data Events (resource :: Type) (e :: Type) :: Effect
- Polysemy.Conc.Effect.Events: publish :: forall e token r. Member (Events token e) r => e -> Sem r ()
+ Polysemy.Conc.Effect.Events: publish :: forall e resource r. Member (Events resource e) r => e -> Sem r ()
- Polysemy.Conc.Effect.Events: subscribe :: forall e token r. Member (Scoped (EventToken token) (Consume e)) r => InterpreterFor (Consume e) r
+ Polysemy.Conc.Effect.Events: subscribe :: forall e resource r. Member (Scoped (EventResource resource) (Consume e)) r => InterpreterFor (Consume e) r
- Polysemy.Conc.Interpreter.Events: type EventChan e = EventToken (OutChan e)
+ Polysemy.Conc.Interpreter.Events: type EventChan e = EventResource (OutChan e)
- Polysemy.Conc.Interpreter.Events: type EventConsumer token e = Scoped (EventToken token) (Consume e)
+ Polysemy.Conc.Interpreter.Events: type EventConsumer token e = Scoped (EventResource token) (Consume e)

Files

changelog.md view
@@ -1,5 +1,12 @@ # Unreleased +# 0.5.0.0++* Add `mask` effects.+* Add `Monitor`, an effect that repeatedly checks a condition and restarts a region when it is met.+* Add interpreter combinators for `Scoped`.+* Add a runner for the default `Conc` stack.+ # 0.4.0.0  * Add `lock`, a combinator for protecting a region with a mutex.
lib/Polysemy/Conc.hs view
@@ -66,7 +66,7 @@   subscribe,   subscribeWhile,   subscribeLoop,-  EventToken,+  EventResource,   EventChan,   ChanEvents,   EventConsumer,@@ -82,7 +82,48 @@   interpretCritical,   interpretCriticalNull, +  -- * Masking+  Mask,+  UninterruptipleMask,+  mask,+  uninterruptibleMask,+  restore,++  -- * Interpreters+  interpretMaskFinal,+  interpretUninterruptibleMaskFinal,++  -- * Scoped Effects+  Scoped,+  scoped,++  -- ** Interpreters+  runScoped,+  runScopedAs,+  interpretScoped,+  interpretScopedH,+  interpretScopedAs,++  -- * Monitoring+  Monitor,+  monitor,+  withMonitor,+  restart,+  Restart,+  RestartingMonitor,+  MonitorResource (MonitorResource),+  ScopedMonitor,++  -- ** Interpreters+  interpretMonitorRestart,+  interpretMonitorPure,+  monitorClockSkew,+  ClockSkewConfig (ClockSkewConfig),+  clockSkewConfig,+   -- * Other Combinators+  ConcStack,+  runConc,   interpretAtomic,   withAsyncBlock,   withAsync,@@ -101,15 +142,29 @@ import Polysemy.Conc.AtomicState (interpretAtomic) import Polysemy.Conc.Data.QueueResult (QueueResult) import Polysemy.Conc.Effect.Critical (Critical)-import Polysemy.Conc.Effect.Events (EventToken, Events, consume, publish, subscribe)+import Polysemy.Conc.Effect.Events (EventResource, Events, consume, publish, subscribe) import Polysemy.Conc.Effect.Interrupt (Interrupt)+import Polysemy.Conc.Effect.Mask (Mask, UninterruptipleMask, mask, restore, uninterruptibleMask)+import Polysemy.Conc.Effect.Monitor (+  Monitor,+  MonitorResource (MonitorResource),+  Restart,+  RestartingMonitor,+  ScopedMonitor,+  monitor,+  restart,+  withMonitor,+  ) import Polysemy.Conc.Effect.Queue (Queue) import Polysemy.Conc.Effect.Race (Race, race, timeout)+import Polysemy.Conc.Effect.Scoped (Scoped, scoped) import Polysemy.Conc.Effect.Sync (ScopedSync, Sync) import Polysemy.Conc.Events (subscribeLoop, subscribeWhile) import Polysemy.Conc.Interpreter.Critical (interpretCritical, interpretCriticalNull) import Polysemy.Conc.Interpreter.Events (ChanConsumer, ChanEvents, EventChan, EventConsumer, interpretEventsChan) import Polysemy.Conc.Interpreter.Interrupt (interpretInterrupt, interpretInterruptOnce)+import Polysemy.Conc.Interpreter.Mask (interpretMaskFinal, interpretUninterruptibleMaskFinal)+import Polysemy.Conc.Interpreter.Monitor (interpretMonitorPure, interpretMonitorRestart) import Polysemy.Conc.Interpreter.Queue.Pure (   interpretQueueListReadOnlyAtomic,   interpretQueueListReadOnlyAtomicWith,@@ -119,7 +174,10 @@ import Polysemy.Conc.Interpreter.Queue.TB (interpretQueueTB) import Polysemy.Conc.Interpreter.Queue.TBM (interpretQueueTBM) import Polysemy.Conc.Interpreter.Race (interpretRace)+import Polysemy.Conc.Interpreter.Scoped (interpretScoped, interpretScopedAs, interpretScopedH, runScoped, runScopedAs)+import Polysemy.Conc.Interpreter.Stack (ConcStack, runConc) import Polysemy.Conc.Interpreter.Sync (interpretScopedSync, interpretScopedSyncAs, interpretSync, interpretSyncAs)+import Polysemy.Conc.Monitor (ClockSkewConfig (ClockSkewConfig), clockSkewConfig, monitorClockSkew) import Polysemy.Conc.Queue (loop, loopOr) import Polysemy.Conc.Queue.Result (resultToMaybe) import Polysemy.Conc.Race (race_, timeoutAs, timeoutAs_, timeoutMaybe, timeoutU, timeout_)@@ -133,6 +191,7 @@ -- - [MVars](#mvar) -- - [Racing](#race) -- - [Signal handling](#signal)+-- - [Masking](#mask)  -- $queue -- #queue#@@ -156,3 +215,24 @@  -- $signal -- #signal#++-- $mask+-- #mask#+-- The two effects 'Mask' and 'UninterruptipleMask' use the 'Polysemy.Conc.Effect.Scoped' pattern, which allow an effect+-- with resources to be constrained to a region of code.+-- The actual effect is 'Polysemy.Conc.Effect.Mask.RestoreMask', with `mask` and 'uninterruptibleMask' only specializing+-- 'Polysemy.Conc.Effect.scoped' to the appropriate resource type.+--+-- Usage is straightforward:+--+-- @+-- prog :: Member (Mask resource) r+-- prog =+--  mask do+--    doMaskedThing+--    restore do+--      doUnmaskedThing+--    doMaskedThing+-- @+--+-- The @resource@ parameter stays polymorphic; it is used to connect the resource in the interpreter to the callsite.
lib/Polysemy/Conc/Effect/Events.hs view
@@ -5,21 +5,21 @@ import Polysemy (makeSem_) import Polysemy.Conc.Effect.Scoped (Scoped, scoped) --- |Marker for the 'Scoped' token for 'Events'.-newtype EventToken token =-  EventToken { unEventToken :: token }+-- |Marker for the 'Scoped' resource for 'Events'.+newtype EventResource resource =+  EventResource { unEventToken :: resource }   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 ()+data Events (resource :: Type) (e :: Type) :: Effect where+  Publish :: e -> Events resource e m ()  makeSem_ ''Events  -- |Publish one event. publish ::-  ∀ e token r .-  Member (Events token e) r =>+  ∀ e resource r .+  Member (Events resource e) r =>   e ->   Sem r () @@ -38,8 +38,8 @@ -- |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 =>+  ∀ e resource r .+  Member (Scoped (EventResource resource) (Consume e)) r =>   InterpreterFor (Consume e) r subscribe =-  scoped @(EventToken token)+  scoped @(EventResource resource)
+ lib/Polysemy/Conc/Effect/Mask.hs view
@@ -0,0 +1,53 @@+{-# options_haddock prune #-}+-- |Description: Mask Effect, Internal+module Polysemy.Conc.Effect.Mask where++import Polysemy (makeSem_)++import Polysemy.Conc.Effect.Scoped (Scoped, scoped)++-- |Part of an effect abstracting 'Control.Exception.mask'.+data RestoreMask :: Effect where+  Restore :: m a -> RestoreMask m a++makeSem_ ''RestoreMask++-- |Restore the previous masking state.+-- Can only be called inside of an action passed to 'mask' or 'uninterruptibleMask'.+restore ::+  ∀ r a .+  Member RestoreMask r =>+  Sem r a ->+  Sem r a++newtype MaskResource resource =+  MaskResource { unMaskResource :: resource }++newtype UninterruptipleMaskResource resource =+  UninterruptipleMaskResource { unUninterruptipleMaskResource :: resource }++-- |The scoped masking effect.+type Mask resource =+  Scoped (MaskResource resource) RestoreMask++-- |The scoped uninterruptible masking effect.+type UninterruptipleMask resource =+  Scoped (UninterruptipleMaskResource resource) RestoreMask++-- |Mark a region as masked.+-- Uses the 'Scoped' pattern.+mask ::+  ∀ resource r .+  Member (Mask resource) r =>+  InterpreterFor RestoreMask r+mask =+  scoped @(MaskResource resource)++-- |Mark a region as uninterruptibly masked.+-- Uses the 'Scoped' pattern.+uninterruptibleMask ::+  ∀ resource r .+  Member (UninterruptipleMask resource) r =>+  InterpreterFor RestoreMask r+uninterruptibleMask =+  scoped @(UninterruptipleMaskResource resource)
+ lib/Polysemy/Conc/Effect/Monitor.hs view
@@ -0,0 +1,55 @@+{-# options_haddock prune #-}+-- |Description: Monitor Effect, Internal+module Polysemy.Conc.Effect.Monitor where++import Polysemy.Time (NanoSeconds)++import Polysemy.Conc.Effect.Scoped (Scoped, scoped)++data Restart =+  Restart+  deriving (Eq, Show)++-- |Mark a region as being subject to intervention by a monitoring program.+-- This can mean that a thread is repeatedly checking a condition and cancelling this region when it is unmet.+-- A use case could be checking whether a remote service is available, or whether the system was suspended and resumed.+-- This should be used in a 'Scoped' context, like 'withMonitor'.+data Monitor (action :: Type) :: Effect where+  Monitor :: m a -> Monitor action m a++makeSem ''Monitor++-- |Marker type for a 'Scoped' 'Monitor'.+newtype MonitorResource a =+  MonitorResource { unMonitorResource :: a }++-- |Convenience alias for a 'Scoped' 'Monitor'.+type ScopedMonitor (resource :: Type) (action :: Type) =+  Scoped (MonitorResource resource) (Monitor action)++-- |'Monitor' specialized to the 'Restart' action.+type RestartingMonitor (resource :: Type) =+  ScopedMonitor resource Restart++-- |Resources for a 'Scoped' 'Monitor'.+data MonitorCheck r =+  MonitorCheck {+    interval :: NanoSeconds,+    check :: MVar () -> Sem r ()+  }++-- |Start a region that can contain monitor-intervention regions.+withMonitor ::+  ∀ resource action r .+  Member (ScopedMonitor resource action) r =>+  InterpreterFor (Monitor action) r+withMonitor =+  scoped @(MonitorResource resource)++-- |Variant of 'withMonitor' that uses the 'Restart' strategy.+restart ::+  ∀ resource r .+  Member (ScopedMonitor resource Restart) r =>+  InterpreterFor (Monitor Restart) r+restart =+  withMonitor @resource
lib/Polysemy/Conc/Interpreter/Critical.hs view
@@ -2,7 +2,7 @@ module Polysemy.Conc.Interpreter.Critical where  import qualified Control.Exception as Exception-import Polysemy (interpretH, runT)+import Polysemy (runT) import Polysemy.Final (getInitialStateS, interpretFinal, runS)  import Polysemy.Conc.Effect.Critical (Critical (..))
lib/Polysemy/Conc/Interpreter/Events.hs view
@@ -9,7 +9,7 @@  import Polysemy.Conc.Async (withAsync_) import qualified Polysemy.Conc.Effect.Events as Events-import Polysemy.Conc.Effect.Events (Consume, EventToken (EventToken), Events)+import Polysemy.Conc.Effect.Events (Consume, EventResource (EventResource), Events) import Polysemy.Conc.Effect.Race (Race) import Polysemy.Conc.Effect.Scoped (Scoped) import Polysemy.Conc.Interpreter.Scoped (runScopedAs)@@ -18,13 +18,13 @@ type ChanEvents e =   Events (OutChan e) e --- |Convenience alias for the default 'EventToken' that uses an 'OutChan'.+-- |Convenience alias for the default 'EventResource' that uses an 'OutChan'. type EventChan e =-  EventToken (OutChan e)+  EventResource (OutChan e)  -- |Convenience alias for the consumer effect. type EventConsumer token e =-  Scoped (EventToken token) (Consume e)+  Scoped (EventResource token) (Consume e)  -- |Convenience alias for the consumer effect using the default implementation. type ChanConsumer e =@@ -37,7 +37,7 @@   Member (Embed IO) r =>   EventChan e ->   InterpreterFor (Consume e) r-interpretConsumeChan (EventToken chan) =+interpretConsumeChan (EventResource chan) =   interpret \case     Events.Consume ->       embed (readChan chan)@@ -77,4 +77,4 @@ interpretEventsChan sem = do   (inChan, outChan) <- embed (newChan @e 64)   withAsync_ (forever (embed (readChan outChan))) do-    runScopedAs (EventToken <$> embed (dupChan inChan)) interpretConsumeChan (interpretEventsInChan inChan sem)+    runScopedAs (EventResource <$> embed (dupChan inChan)) interpretConsumeChan (interpretEventsInChan inChan sem)
lib/Polysemy/Conc/Interpreter/Interrupt.hs view
@@ -7,12 +7,12 @@ import qualified Data.Map.Strict as Map import qualified Data.Set as Set import qualified Data.Text.IO as Text-import Polysemy (getInspectorT, inspect, interpretH, runT)+import Polysemy (getInspectorT, inspect, 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, CatchInfo), SignalInfo, installHandler, keyboardSignal)+import System.Posix.Signals (Handler (CatchInfo, CatchInfoOnce, CatchOnce), SignalInfo, installHandler, keyboardSignal)  import qualified Polysemy.Conc.Effect.Critical as Critical import Polysemy.Conc.Effect.Critical (Critical)
+ lib/Polysemy/Conc/Interpreter/Mask.hs view
@@ -0,0 +1,63 @@+{-# options_haddock prune #-}+-- |Description: Mask Interpreters, Internal+module Polysemy.Conc.Interpreter.Mask where++import qualified Control.Exception as Base+import Polysemy (runTSimple)+import Polysemy.Final (runS, withStrategicToFinal, withWeavingToFinal)++import Polysemy.Conc.Effect.Mask (+  Mask,+  MaskResource (MaskResource),+  RestoreMask (Restore),+  UninterruptipleMask,+  UninterruptipleMaskResource (UninterruptipleMaskResource),+  )+import Polysemy.Conc.Interpreter.Scoped (runScoped)++newtype Restoration =+  Restoration { unRestoration :: ∀ a . IO a -> IO a }++mask ::+  Member (Final IO) r =>+  (MaskResource Restoration -> Sem r a) ->+  Sem r a+mask f =+  withWeavingToFinal @IO \ s lower _ ->+    Base.mask \ restore -> (lower (f (MaskResource (Restoration restore)) <$ s))++uninterruptibleMask ::+  Member (Final IO) r =>+  (UninterruptipleMaskResource Restoration -> Sem r a) ->+  Sem r a+uninterruptibleMask f =+  withWeavingToFinal @IO \ s lower _ ->+    Base.uninterruptibleMask \ restore -> (lower (f (UninterruptipleMaskResource (Restoration restore)) <$ s))++interpretRestoreMask ::+  ∀ r .+  Member (Final IO) r =>+  Restoration ->+  InterpreterFor RestoreMask r+interpretRestoreMask (Restoration restore) =+  interpretH \case+    Restore ma -> do+      let+        restoreSem m =+          withStrategicToFinal do+            (restore <$> runS m)+      restoreSem (runTSimple ma)++-- |Interpret 'Mask' in 'IO'.+interpretMaskFinal ::+  Member (Final IO) r =>+  InterpreterFor (Mask Restoration) r+interpretMaskFinal =+  runScoped mask \ (MaskResource r) -> interpretRestoreMask r++-- |Interpret 'UninterruptipleMask' in 'IO'.+interpretUninterruptibleMaskFinal ::+  Member (Final IO) r =>+  InterpreterFor (UninterruptipleMask Restoration) r+interpretUninterruptibleMaskFinal =+  runScoped uninterruptibleMask \ (UninterruptipleMaskResource r) -> interpretRestoreMask r
+ lib/Polysemy/Conc/Interpreter/Monitor.hs view
@@ -0,0 +1,76 @@+{-# options_haddock prune #-}+-- |Description: Monitor Interpreters, Internal+module Polysemy.Conc.Interpreter.Monitor where++import qualified Control.Exception as Base+import Polysemy (embedFinal, runTSimple)+import Polysemy.Async (Async)+import Polysemy.Error (errorToIOFinal, fromExceptionSem)+import Polysemy.Resource (Resource)+import qualified Polysemy.Time as Time+import Polysemy.Time (Time)++import Polysemy.Conc.Async (withAsync_)+import Polysemy.Conc.Effect.Monitor (+  Monitor (Monitor),+  MonitorCheck (MonitorCheck),+  MonitorResource (MonitorResource),+  RestartingMonitor,+  ScopedMonitor,+  )+import qualified Polysemy.Conc.Effect.Race as Race+import Polysemy.Conc.Effect.Race (Race)+import Polysemy.Conc.Interpreter.Scoped (runScoped, runScopedAs)++newtype CancelResource =+  CancelResource { signal :: MVar () }++data MonitorCancel =+  MonitorCancel+  deriving (Eq, Show, Exception)++interpretMonitorCancel ::+  Members [Race, Async, Final IO] r =>+  MonitorResource CancelResource ->+  InterpreterFor (Monitor action) r+interpretMonitorCancel (MonitorResource CancelResource {..}) =+  interpretH \case+    Monitor ma ->+      leftM (Base.throw MonitorCancel) =<< Race.race (embedFinal @IO (readMVar signal)) (runTSimple ma)++monitorRestart ::+  ∀ t d r a .+  Members [Time t d, Resource, Async, Race, Final IO] r =>+  MonitorCheck r ->+  (MonitorResource CancelResource -> Sem r a) ->+  Sem r a+monitorRestart (MonitorCheck interval check) run = do+  sig <- embedFinal @IO newEmptyMVar+  withAsync_ (Time.loop_ @t @d interval (check sig)) (spin sig)+  where+    spin sig = do+      let res = (MonitorResource (CancelResource sig))+      void (embedFinal @IO (tryTakeMVar sig))+      leftM (spin sig) =<< errorToIOFinal @MonitorCancel (fromExceptionSem @MonitorCancel (raise (run res)))++-- |Interpret @'Scoped' 'Monitor'@ with the 'Restart' strategy.+-- This takes a check action that may put an 'MVar' when the scoped region should be restarted.+-- The check is executed in a loop, with an interval given in 'MonitorCheck'.+interpretMonitorRestart ::+  ∀ t d r .+  Members [Time t d, Resource, Async, Race, Final IO] r =>+  MonitorCheck r ->+  InterpreterFor (RestartingMonitor CancelResource) r+interpretMonitorRestart check =+  runScoped (monitorRestart @t @d check) interpretMonitorCancel++interpretMonitorPure' :: MonitorResource () -> InterpreterFor (Monitor action) r+interpretMonitorPure' _ =+  interpretH \case+    Monitor ma ->+      runTSimple ma++-- |Run 'Monitor' as a no-op.+interpretMonitorPure :: InterpreterFor (ScopedMonitor () action) r+interpretMonitorPure =+  runScopedAs (pure (MonitorResource ())) interpretMonitorPure'
lib/Polysemy/Conc/Interpreter/Scoped.hs view
@@ -6,13 +6,15 @@ import Polysemy.Internal.Union (Weaving (Weaving), decomp, hoist, injWeaving)  import Polysemy.Conc.Effect.Scoped (Scoped (InScope, Run))+import Polysemy (Tactical)+import Polysemy.Internal.Tactics (runTactics)  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+interpretH' h (Sem m) =+  Sem \ k -> m $ decomp >>> \case     Right wav -> runSem (h wav) k     Left g -> k $ hoist (interpretH' h) g @@ -41,7 +43,7 @@         InScope main ->           ex <$> withResource \ resource -> run (wv (main resource <$ s)) --- |Variant of 'Scoped' in which the resource allocator is a plain action.+-- |Variant of 'runScoped' in which the resource allocator is a plain action. runScopedAs ::   ∀ resource effect r .   Sem r resource ->@@ -49,3 +51,48 @@   InterpreterFor (Scoped resource effect) r runScopedAs resource =   runScoped \ f -> f =<< resource++-- |Variant of 'runScoped' that takes a higher-order handler instead of an interpreter.+interpretScopedH ::+  ∀ resource effect r .+  (∀ x . (resource -> Sem r x) -> Sem r x) ->+  (∀ r0 x . resource -> effect (Sem r0) x -> Tactical effect (Sem r0) r x) ->+  InterpreterFor (Scoped resource effect) r+interpretScopedH withResource scopedHandler =+  run+  where+    run :: InterpreterFor (Scoped resource effect) r+    run =+      interpretH' \ (Weaving effect s wv ex ins) -> case effect of+        Run resource act ->+          ex <$> runTactics s (raise . run . wv) ins (run . wv) (scopedHandler resource act)+        InScope main ->+          ex <$> withResource \ resource -> run (wv (main resource <$ s))++-- |Variant of 'runScoped' that takes a handler instead of an interpreter.+interpretScoped ::+  ∀ resource effect r .+  (∀ x . (resource -> Sem r x) -> Sem r x) ->+  (∀ r0 x . resource -> effect (Sem r0) x -> Sem r x) ->+  InterpreterFor (Scoped resource effect) r+interpretScoped withResource scopedHandler =+  run+  where+    run :: InterpreterFor (Scoped resource effect) r+    run =+      interpretH' \ (Weaving effect s wv ex _) -> case effect of+        Run resource act -> do+          x <- scopedHandler resource act+          pure (ex (x <$ s))+        InScope main ->+          ex <$> withResource \ resource -> run (wv (main resource <$ s))+++-- |Variant of 'interpretScoped' in which the resource allocator is a plain action.+interpretScopedAs ::+  ∀ resource effect r .+  Sem r resource ->+  (∀ r0 x . resource -> effect (Sem r0) x -> Sem r x) ->+  InterpreterFor (Scoped resource effect) r+interpretScopedAs resource =+  interpretScoped \ f -> f =<< resource
+ lib/Polysemy/Conc/Interpreter/Stack.hs view
@@ -0,0 +1,37 @@+{-# options_haddock prune #-}+-- |Description: Convenience Interpreters for all Conc Effects, Internal+module Polysemy.Conc.Interpreter.Stack where++import Polysemy.Async (Async, asyncToIOFinal)+import Polysemy.Resource (Resource, resourceToIOFinal)++import Polysemy.Conc.Effect.Mask (Mask, UninterruptipleMask)+import Polysemy.Conc.Effect.Race (Race)+import Polysemy.Conc.Interpreter.Mask (Restoration, interpretMaskFinal, interpretUninterruptibleMaskFinal)+import Polysemy.Conc.Interpreter.Race (interpretRace)++-- |A default basic stack with 'Final' for _polysemy-conc_.+type ConcStack =+  [+    UninterruptipleMask Restoration,+    Mask Restoration,+    Race,+    Async,+    Resource,+    Embed IO,+    Final IO+  ]++-- |Interprets 'UninterruptipleMask', 'Mask' and 'Race' in terms of @'Final' 'IO'@ and runs the entire rest of the+-- stack.+runConc ::+  Sem ConcStack a ->+  IO a+runConc =+  runFinal .+  embedToFinal @IO .+  resourceToIOFinal .+  asyncToIOFinal .+  interpretRace .+  interpretMaskFinal .+  interpretUninterruptibleMaskFinal
+ lib/Polysemy/Conc/Monitor.hs view
@@ -0,0 +1,51 @@+module Polysemy.Conc.Monitor where++import qualified Polysemy.Time as Time+import Polysemy.Time (NanoSeconds, Time, TimeUnit, convert, Minutes (Minutes), Seconds (Seconds))+import Torsor (Torsor, difference, minus)++import Polysemy.Conc.Effect.Monitor (MonitorCheck (MonitorCheck))+import Data.Default (Default (def))++-- |Config for 'monitorClockSkew'.+data ClockSkewConfig =+  ClockSkewConfig {+    interval :: NanoSeconds,+    tolerance :: NanoSeconds+  }+  deriving (Eq, Show)++-- |Smart constructor for 'ClockSkewConfig' that takes arbitrary 'TimeUnit's.+clockSkewConfig ::+  TimeUnit u1 =>+  TimeUnit u2 =>+  u1 ->+  u2 ->+  ClockSkewConfig+clockSkewConfig i t =+  ClockSkewConfig (convert i) (convert t)++instance Default ClockSkewConfig where+  def =+    clockSkewConfig (Minutes 1) (Seconds 5)++-- |Check for 'Polysemy.Conc.Effect.Monitor' that checks every @interval@ whether the difference between the current+-- time and the time at the last check is larger than @interval@ + @tolerance@.+-- Can be used to detect that the operating system suspended and resumed.+monitorClockSkew ::+  ∀ t d diff r .+  Torsor t diff =>+  TimeUnit diff =>+  Members [AtomicState (Maybe t), Time t d, Embed IO] r =>+  ClockSkewConfig ->+  MonitorCheck r+monitorClockSkew (ClockSkewConfig interval tolerance) =+  MonitorCheck interval \ signal -> do+    atomicGet >>= \case+      Just prev -> do+        now <- Time.now @t @d+        when (minus (convert (difference now prev)) tolerance > interval) (void (embed @IO (tryPutMVar signal ())))+        atomicPut (Just now)+      Nothing -> do+        now <- Time.now @t @d+        atomicPut (Just now)
lib/Polysemy/Conc/Prelude.hs view
@@ -10,6 +10,7 @@   module Relude, ) where +import Control.Exception (try) import Data.Kind (Type) import qualified Data.String.Interpolate as Interpolate import GHC.Err (undefined)@@ -27,6 +28,7 @@   embed,   embedToFinal,   interpret,+  interpretH,   makeSem,   pureT,   raise,@@ -37,6 +39,7 @@   runFinal,   ) import Polysemy.AtomicState (AtomicState, atomicGet, atomicGets, atomicModify', atomicPut)+import Polysemy.Internal.Kind (Append) import Relude hiding (   Reader,   State,@@ -70,3 +73,23 @@ unify =   either id id {-# inline unify #-}++tryAny ::+  Member (Embed IO) r =>+  IO a ->+  Sem r (Either Text a)+tryAny =+  embed @IO . fmap (first show) . try @SomeException+{-# INLINE tryAny #-}++type a ++ b =+  Append a b++leftM ::+  Applicative m =>+  m b ->+  Either a b ->+  m b+leftM f =+  either (const f) pure+{-# inline leftM #-}
polysemy-conc.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           polysemy-conc-version:        0.4.0.1+version:        0.5.0.0 synopsis:       Polysemy Effects for Concurrency description:    See <https://hackage.haskell.org/package/polysemy-conc/docs/Polysemy-Conc.html> category:       Concurrency@@ -34,6 +34,8 @@       Polysemy.Conc.Effect.Critical       Polysemy.Conc.Effect.Events       Polysemy.Conc.Effect.Interrupt+      Polysemy.Conc.Effect.Mask+      Polysemy.Conc.Effect.Monitor       Polysemy.Conc.Effect.Queue       Polysemy.Conc.Effect.Race       Polysemy.Conc.Effect.Scoped@@ -42,12 +44,16 @@       Polysemy.Conc.Interpreter.Critical       Polysemy.Conc.Interpreter.Events       Polysemy.Conc.Interpreter.Interrupt+      Polysemy.Conc.Interpreter.Mask+      Polysemy.Conc.Interpreter.Monitor       Polysemy.Conc.Interpreter.Queue.Pure       Polysemy.Conc.Interpreter.Queue.TB       Polysemy.Conc.Interpreter.Queue.TBM       Polysemy.Conc.Interpreter.Race       Polysemy.Conc.Interpreter.Scoped+      Polysemy.Conc.Interpreter.Stack       Polysemy.Conc.Interpreter.Sync+      Polysemy.Conc.Monitor       Polysemy.Conc.Prelude       Polysemy.Conc.Queue       Polysemy.Conc.Queue.Result@@ -120,24 +126,28 @@       UndecidableInstances       UnicodeSyntax       ViewPatterns-  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -Wunused-packages+  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints   build-depends:       async     , base ==4.*     , containers-    , polysemy >=1.5-    , polysemy-time >=0.1.2.1-    , relude >=0.7+    , data-default+    , polysemy >=1.6+    , polysemy-time >=0.1.4+    , relude >=1     , stm     , stm-chans >=2     , string-interpolate >=0.2     , template-haskell     , text     , time+    , torsor     , unagi-chan >=0.4     , unix   mixins:       base hiding (Prelude)+  if impl(ghc >= 8.10)+    ghc-options: -Wunused-packages   default-language: Haskell2010  test-suite polysemy-conc-unit@@ -147,6 +157,8 @@       Polysemy.Conc.Test.EventsTest       Polysemy.Conc.Test.InterruptTest       Polysemy.Conc.Test.LockTest+      Polysemy.Conc.Test.MaskTest+      Polysemy.Conc.Test.MonitorTest       Polysemy.Conc.Test.QueueTest       Polysemy.Conc.Test.SyncTest       Paths_polysemy_conc@@ -210,20 +222,24 @@       UndecidableInstances       UnicodeSyntax       ViewPatterns-  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -Wunused-packages -threaded -rtsopts -with-rtsopts=-N+  ghc-options: -flate-specialise -fspecialise-aggressively -Wall -Wredundant-constraints -threaded -rtsopts -with-rtsopts=-N   build-depends:-      base ==4.*-    , polysemy >=1.5+      async+    , base ==4.*+    , polysemy     , polysemy-conc+    , polysemy-plugin     , polysemy-test-    , polysemy-time >=0.1.2.1+    , polysemy-time     , stm     , tasty     , time-    , unagi-chan >=0.4+    , unagi-chan     , unix   mixins:       base hiding (Prelude)     , polysemy-conc hiding (Prelude)     , polysemy-conc (Polysemy.Conc.Prelude as Prelude)+  if impl(ghc >= 8.10)+    ghc-options: -Wunused-packages   default-language: Haskell2010
test/Main.hs view
@@ -2,6 +2,8 @@  import Polysemy.Conc.Test.EventsTest (test_events) import Polysemy.Conc.Test.InterruptTest (test_interrupt)+import Polysemy.Conc.Test.MaskTest (test_mask)+import Polysemy.Conc.Test.MonitorTest (test_monitorBasic, test_monitorClockSkew) import Polysemy.Conc.Test.QueueTest (   test_queueBlockTB,   test_queueBlockTBM,@@ -33,6 +35,13 @@     ],     testGroup "interrupt" [       unitTest "interrupt" test_interrupt+    ],+    testGroup "mask" [+      unitTest "mask" test_mask+    ],+    testGroup "monitor" [+      unitTest "basic" test_monitorBasic,+      unitTest "clock skew" test_monitorClockSkew     ]   ] 
+ test/Polysemy/Conc/Test/MaskTest.hs view
@@ -0,0 +1,71 @@+{-# options_ghc -fplugin=Polysemy.Plugin #-}++module Polysemy.Conc.Test.MaskTest where++import qualified Control.Concurrent.Async as Base+import Control.Concurrent.Async (asyncThreadId)+import Control.Exception (throwTo)+import Polysemy (embedFinal)+import Polysemy.Async (Async, async, asyncToIOFinal, await)+import Polysemy.Error (fromExceptionSem, runError)+import Polysemy.Test (Hedgehog, UnitTest, assertEq, evalLeft, runTestAuto)+import System.IO.Error (userError)++import Polysemy.Conc.AtomicState (interpretAtomic)+import Polysemy.Conc.Effect.Mask (restore, uninterruptibleMask)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Interpreter.Mask (interpretUninterruptibleMaskFinal)+import Polysemy.Conc.Interpreter.Race (interpretRace)+import Polysemy.Conc.Interpreter.Sync (interpretSync)+import qualified Polysemy.Time as Time+import Polysemy.Time (interpretTimeGhc, MilliSeconds (MilliSeconds))++kill ::+  Member (Final IO) r =>+  Base.Async (Maybe ()) ->+  Sem r ()+kill t =+  embedFinal (throwTo (asyncThreadId t) (userError "stop"))++wait ::+  HasCallStack =>+  Members [Hedgehog IO, Async, Final IO] r =>+  Base.Async (Maybe ()) ->+  Sem r ()+wait t =+  withFrozenCallStack do+    void . evalLeft =<< runError @SomeException (fromExceptionSem @SomeException (await t))++test_mask :: UnitTest+test_mask =+  runTestAuto $+  asyncToIOFinal $+  interpretRace $+  interpretTimeGhc $+  interpretUninterruptibleMaskFinal $+  interpretAtomic (0 :: Int) $+  interpretSync @(Proxy 2) $+  interpretSync @(Proxy 1) do+    t1 <- async do+      uninterruptibleMask do+        Sync.putBlock (Proxy @1)+        Sync.takeBlock @(Proxy 2)+        Time.sleep (MilliSeconds 100)+        atomicPut 2+    Sync.takeBlock @(Proxy 1)+    async (kill t1)+    Sync.putBlock (Proxy @2)+    _ <- wait t1+    assertEq 2 =<< atomicGet+    t2 <- async do+      uninterruptibleMask do+        restore do+          Sync.putBlock (Proxy @1)+          void (Sync.takeBlock @(Proxy 2))+          Time.sleep (MilliSeconds 100)+          atomicPut 3+    Sync.takeBlock @(Proxy 1)+    async (kill t2)+    Sync.putBlock (Proxy @2)+    _ <- wait t2+    assertEq 2 =<< atomicGet
+ test/Polysemy/Conc/Test/MonitorTest.hs view
@@ -0,0 +1,97 @@+{-# options_ghc -fplugin=Polysemy.Plugin #-}++module Polysemy.Conc.Test.MonitorTest where++import Data.Time (UTCTime)+import Polysemy.Async (async, asyncToIOFinal, await)+import Polysemy.Test (UnitTest, assertEq, assertJust, runTestAuto)+import qualified Polysemy.Time as Time+import Polysemy.Time (+  Hours (Hours),+  MilliSeconds (MilliSeconds),+  Minutes (Minutes),+  NanoSeconds (NanoSeconds),+  Time,+  interpretTimeGhc,+  interpretTimeGhcConstantNow,+  )++import Polysemy.Conc.AtomicState (interpretAtomic)+import qualified Polysemy.Conc.Effect.Monitor as Monitor+import Polysemy.Conc.Effect.Monitor (Monitor, MonitorCheck (MonitorCheck), Restart)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Effect.Sync (Sync)+import Polysemy.Conc.Interpreter.Monitor (interpretMonitorRestart)+import Polysemy.Conc.Interpreter.Race (interpretRace)+import Polysemy.Conc.Interpreter.Sync (interpretSync)+import Polysemy.Conc.Monitor (clockSkewConfig, monitorClockSkew)++prog ::+  Members [Monitor Restart, Time t d, AtomicState Int, Sync ()] r =>+  Sem r Int+prog = do+  Monitor.monitor do+    atomicModify' (1 +)+    i <- atomicGet+    when (i <= 2) do+      Sync.putBlock ()+      Time.sleep (Hours 1)+    pure i++checker ::+  Members [Time t d, Sync (), Embed IO] r =>+  MVar () ->+  Sem r ()+checker signal =+  Sync.takeBlock *> putMVar signal ()++test_monitorBasic :: UnitTest+test_monitorBasic =+  runTestAuto $+  asyncToIOFinal $+  interpretRace $+  interpretTimeGhc $+  interpretAtomic 0 $+  interpretSync $+  interpretMonitorRestart (MonitorCheck (NanoSeconds 0) checker) do+    assertEq 3 =<< Monitor.restart prog++progSkew ::+  Member (Sync (Proxy "start")) r =>+  Members [Monitor Restart, Time t d, AtomicState Int, Sync (Proxy 1), Sync (Proxy 2), Sync (Proxy 3)] r =>+  Sem r Int+progSkew = do+  Monitor.monitor do+    atomicModify' (1 +)+    Sync.putBlock (Proxy @"start")+    void $ Sync.takeBlock @(Proxy 1)+    Sync.putBlock (Proxy @2)+    void $ Sync.takeBlock @(Proxy 3)+    atomicGet++test_monitorClockSkew :: UnitTest+test_monitorClockSkew =+  runTestAuto $+  asyncToIOFinal $+  interpretRace $+  interpretTimeGhcConstantNow $+  interpretAtomic (0 :: Int) $+  interpretAtomic @(Maybe UTCTime) Nothing $+  interpretSync @(Proxy "start") $+  interpretSync @(Proxy 1) $+  interpretSync @(Proxy 2) $+  interpretSync @(Proxy 3) $+  interpretMonitorRestart (monitorClockSkew (clockSkewConfig (MilliSeconds 1) (Minutes 30))) do+    h <- async (Monitor.restart progSkew)+    _ <- Sync.takeBlock @(Proxy "start")+    Time.adjust (Hours 1)+    _ <- Sync.takeBlock @(Proxy "start")+    Time.adjust (Minutes 10)+    Sync.putBlock (Proxy @1)+    Sync.takeBlock @(Proxy 2)+    Time.adjust (Hours 1)+    _ <- Sync.takeBlock @(Proxy "start")+    Sync.putBlock (Proxy @1)+    Sync.takeBlock @(Proxy 2)+    Sync.putBlock (Proxy @3)+    assertJust 3 =<< await h