packages feed

polysemy-conc 0.14.0.0 → 0.14.1.0

raw patch · 8 files changed

+182/−69 lines, 8 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Polysemy.Conc.Effect.Monitor: MonitorCheck :: NanoSeconds -> (MVar () -> Sem r ()) -> MonitorCheck r
+ Polysemy.Conc.Effect.Monitor: MonitorCheck :: NanoSeconds -> Sem r Bool -> MonitorCheck r
- Polysemy.Conc.Effect.Monitor: [$sel:check:MonitorCheck] :: MonitorCheck r -> MVar () -> Sem r ()
+ Polysemy.Conc.Effect.Monitor: [$sel:check:MonitorCheck] :: MonitorCheck r -> Sem r Bool
- Polysemy.Conc.Effect.Queue: close :: forall d_afNM r_afW3. Member (Queue d_afNM) r_afW3 => Sem r_afW3 ()
+ Polysemy.Conc.Effect.Queue: close :: forall d_afN2 r_afVj. Member (Queue d_afN2) r_afVj => Sem r_afVj ()
- Polysemy.Conc.Effect.Queue: closed :: forall d_afNK r_afW2. Member (Queue d_afNK) r_afW2 => Sem r_afW2 Bool
+ Polysemy.Conc.Effect.Queue: closed :: forall d_afN0 r_afVi. Member (Queue d_afN0) r_afVi => Sem r_afVi Bool
- Polysemy.Conc.Effect.Queue: peek :: forall d_afNz r_afVT. Member (Queue d_afNz) r_afVT => Sem r_afVT (QueueResult d_afNz)
+ Polysemy.Conc.Effect.Queue: peek :: forall d_afMP r_afV9. Member (Queue d_afMP) r_afV9 => Sem r_afV9 (QueueResult d_afMP)
- Polysemy.Conc.Effect.Queue: read :: forall d_afNs r_afVP. Member (Queue d_afNs) r_afVP => Sem r_afVP (QueueResult d_afNs)
+ Polysemy.Conc.Effect.Queue: read :: forall d_afMI r_afV5. Member (Queue d_afMI) r_afV5 => Sem r_afV5 (QueueResult d_afMI)
- Polysemy.Conc.Effect.Queue: readTimeout :: forall d_afNx r_afVR t_X0. (Member (Queue d_afNx) r_afVR, TimeUnit t_X0) => t_X0 -> Sem r_afVR (QueueResult d_afNx)
+ Polysemy.Conc.Effect.Queue: readTimeout :: forall d_afMN r_afV7 t_X0. (Member (Queue d_afMN) r_afV7, TimeUnit t_X0) => t_X0 -> Sem r_afV7 (QueueResult d_afMN)
- Polysemy.Conc.Effect.Queue: tryPeek :: forall d_afNB r_afVU. Member (Queue d_afNB) r_afVU => Sem r_afVU (QueueResult d_afNB)
+ Polysemy.Conc.Effect.Queue: tryPeek :: forall d_afMR r_afVa. Member (Queue d_afMR) r_afVa => Sem r_afVa (QueueResult d_afMR)
- Polysemy.Conc.Effect.Queue: tryRead :: forall d_afNu r_afVQ. Member (Queue d_afNu) r_afVQ => Sem r_afVQ (QueueResult d_afNu)
+ Polysemy.Conc.Effect.Queue: tryRead :: forall d_afMK r_afV6. Member (Queue d_afMK) r_afV6 => Sem r_afV6 (QueueResult d_afMK)
- Polysemy.Conc.Effect.Queue: tryWrite :: forall d_afNF r_afVX. Member (Queue d_afNF) r_afVX => d_afNF -> Sem r_afVX (QueueResult ())
+ Polysemy.Conc.Effect.Queue: tryWrite :: forall d_afMV r_afVd. Member (Queue d_afMV) r_afVd => d_afMV -> Sem r_afVd (QueueResult ())
- Polysemy.Conc.Effect.Queue: write :: forall d_afND r_afVV. Member (Queue d_afND) r_afVV => d_afND -> Sem r_afVV ()
+ Polysemy.Conc.Effect.Queue: write :: forall d_afMT r_afVb. Member (Queue d_afMT) r_afVb => d_afMT -> Sem r_afVb ()
- Polysemy.Conc.Effect.Queue: writeTimeout :: forall d_afNI r_afVZ t_X0. (Member (Queue d_afNI) r_afVZ, TimeUnit t_X0) => t_X0 -> d_afNI -> Sem r_afVZ (QueueResult ())
+ Polysemy.Conc.Effect.Queue: writeTimeout :: forall d_afMY r_afVf t_X0. (Member (Queue d_afMY) r_afVf, TimeUnit t_X0) => t_X0 -> d_afMY -> Sem r_afVf (QueueResult ())
- Polysemy.Conc.Effect.Sync: block :: forall d_agCU r_agLg. Member (Sync d_agCU) r_agLg => Sem r_agLg d_agCU
+ Polysemy.Conc.Effect.Sync: block :: forall d_agCa r_agKw. Member (Sync d_agCa) r_agKw => Sem r_agKw d_agCa
- Polysemy.Conc.Effect.Sync: empty :: forall d_agDf r_agLv. Member (Sync d_agDf) r_agLv => Sem r_agLv Bool
+ Polysemy.Conc.Effect.Sync: empty :: forall d_agCv r_agKL. Member (Sync d_agCv) r_agKL => Sem r_agKL Bool
- Polysemy.Conc.Effect.Sync: putBlock :: forall d_agD8 r_agLo. Member (Sync d_agD8) r_agLo => d_agD8 -> Sem r_agLo ()
+ Polysemy.Conc.Effect.Sync: putBlock :: forall d_agCo r_agKE. Member (Sync d_agCo) r_agKE => d_agCo -> Sem r_agKE ()
- Polysemy.Conc.Effect.Sync: putTry :: forall d_agDd r_agLt. Member (Sync d_agDd) r_agLt => d_agDd -> Sem r_agLt Bool
+ Polysemy.Conc.Effect.Sync: putTry :: forall d_agCt r_agKJ. Member (Sync d_agCt) r_agKJ => d_agCt -> Sem r_agKJ Bool
- Polysemy.Conc.Effect.Sync: putWait :: forall d_agDb r_agLq u_X0. (Member (Sync d_agDb) r_agLq, TimeUnit u_X0) => u_X0 -> d_agDb -> Sem r_agLq Bool
+ Polysemy.Conc.Effect.Sync: putWait :: forall d_agCr r_agKG u_X0. (Member (Sync d_agCr) r_agKG, TimeUnit u_X0) => u_X0 -> d_agCr -> Sem r_agKG Bool
- Polysemy.Conc.Effect.Sync: takeBlock :: forall d_agD1 r_agLk. Member (Sync d_agD1) r_agLk => Sem r_agLk d_agD1
+ Polysemy.Conc.Effect.Sync: takeBlock :: forall d_agCh r_agKA. Member (Sync d_agCh) r_agKA => Sem r_agKA d_agCh
- Polysemy.Conc.Effect.Sync: takeTry :: forall d_agD6 r_agLn. Member (Sync d_agD6) r_agLn => Sem r_agLn (Maybe d_agD6)
+ Polysemy.Conc.Effect.Sync: takeTry :: forall d_agCm r_agKD. Member (Sync d_agCm) r_agKD => Sem r_agKD (Maybe d_agCm)
- Polysemy.Conc.Effect.Sync: takeWait :: forall d_agD4 r_agLl u_X0. (Member (Sync d_agD4) r_agLl, TimeUnit u_X0) => u_X0 -> Sem r_agLl (Maybe d_agD4)
+ Polysemy.Conc.Effect.Sync: takeWait :: forall d_agCk r_agKB u_X0. (Member (Sync d_agCk) r_agKB, TimeUnit u_X0) => u_X0 -> Sem r_agKB (Maybe d_agCk)
- Polysemy.Conc.Effect.Sync: try :: forall d_agCZ r_agLj. Member (Sync d_agCZ) r_agLj => Sem r_agLj (Maybe d_agCZ)
+ Polysemy.Conc.Effect.Sync: try :: forall d_agCf r_agKz. Member (Sync d_agCf) r_agKz => Sem r_agKz (Maybe d_agCf)
- Polysemy.Conc.Effect.Sync: wait :: forall d_agCX r_agLh u_X0. (Member (Sync d_agCX) r_agLh, TimeUnit u_X0) => u_X0 -> Sem r_agLh (Maybe d_agCX)
+ Polysemy.Conc.Effect.Sync: wait :: forall d_agCd r_agKx u_X0. (Member (Sync d_agCd) r_agKx, TimeUnit u_X0) => u_X0 -> Sem r_agKx (Maybe d_agCd)
- Polysemy.Conc.Effect.SyncRead: block :: forall d_ahcZ r_ahfF. Member (SyncRead d_ahcZ) r_ahfF => Sem r_ahfF d_ahcZ
+ Polysemy.Conc.Effect.SyncRead: block :: forall d_ahcf r_aheV. Member (SyncRead d_ahcf) r_aheV => Sem r_aheV d_ahcf
- Polysemy.Conc.Effect.SyncRead: empty :: forall d_ahd6 r_ahfJ. Member (SyncRead d_ahd6) r_ahfJ => Sem r_ahfJ Bool
+ Polysemy.Conc.Effect.SyncRead: empty :: forall d_ahcm r_aheZ. Member (SyncRead d_ahcm) r_aheZ => Sem r_aheZ Bool
- Polysemy.Conc.Effect.SyncRead: try :: forall d_ahd4 r_ahfI. Member (SyncRead d_ahd4) r_ahfI => Sem r_ahfI (Maybe d_ahd4)
+ Polysemy.Conc.Effect.SyncRead: try :: forall d_ahck r_aheY. Member (SyncRead d_ahck) r_aheY => Sem r_aheY (Maybe d_ahck)
- Polysemy.Conc.Effect.SyncRead: wait :: forall d_ahd2 r_ahfG u_X0. (Member (SyncRead d_ahd2) r_ahfG, TimeUnit u_X0) => u_X0 -> Sem r_ahfG (Maybe d_ahd2)
+ Polysemy.Conc.Effect.SyncRead: wait :: forall d_ahci r_aheW u_X0. (Member (SyncRead d_ahci) r_aheW, TimeUnit u_X0) => u_X0 -> Sem r_aheW (Maybe d_ahci)
- Polysemy.Conc.Queue: close :: forall d_afNM r_afW3. Member (Queue d_afNM) r_afW3 => Sem r_afW3 ()
+ Polysemy.Conc.Queue: close :: forall d_afN2 r_afVj. Member (Queue d_afN2) r_afVj => Sem r_afVj ()
- Polysemy.Conc.Queue: closed :: forall d_afNK r_afW2. Member (Queue d_afNK) r_afW2 => Sem r_afW2 Bool
+ Polysemy.Conc.Queue: closed :: forall d_afN0 r_afVi. Member (Queue d_afN0) r_afVi => Sem r_afVi Bool
- Polysemy.Conc.Queue: peek :: forall d_afNz r_afVT. Member (Queue d_afNz) r_afVT => Sem r_afVT (QueueResult d_afNz)
+ Polysemy.Conc.Queue: peek :: forall d_afMP r_afV9. Member (Queue d_afMP) r_afV9 => Sem r_afV9 (QueueResult d_afMP)
- Polysemy.Conc.Queue: read :: forall d_afNs r_afVP. Member (Queue d_afNs) r_afVP => Sem r_afVP (QueueResult d_afNs)
+ Polysemy.Conc.Queue: read :: forall d_afMI r_afV5. Member (Queue d_afMI) r_afV5 => Sem r_afV5 (QueueResult d_afMI)
- Polysemy.Conc.Queue: readTimeout :: forall d_afNx r_afVR t_X0. (Member (Queue d_afNx) r_afVR, TimeUnit t_X0) => t_X0 -> Sem r_afVR (QueueResult d_afNx)
+ Polysemy.Conc.Queue: readTimeout :: forall d_afMN r_afV7 t_X0. (Member (Queue d_afMN) r_afV7, TimeUnit t_X0) => t_X0 -> Sem r_afV7 (QueueResult d_afMN)
- Polysemy.Conc.Queue: tryPeek :: forall d_afNB r_afVU. Member (Queue d_afNB) r_afVU => Sem r_afVU (QueueResult d_afNB)
+ Polysemy.Conc.Queue: tryPeek :: forall d_afMR r_afVa. Member (Queue d_afMR) r_afVa => Sem r_afVa (QueueResult d_afMR)
- Polysemy.Conc.Queue: tryRead :: forall d_afNu r_afVQ. Member (Queue d_afNu) r_afVQ => Sem r_afVQ (QueueResult d_afNu)
+ Polysemy.Conc.Queue: tryRead :: forall d_afMK r_afV6. Member (Queue d_afMK) r_afV6 => Sem r_afV6 (QueueResult d_afMK)
- Polysemy.Conc.Queue: tryWrite :: forall d_afNF r_afVX. Member (Queue d_afNF) r_afVX => d_afNF -> Sem r_afVX (QueueResult ())
+ Polysemy.Conc.Queue: tryWrite :: forall d_afMV r_afVd. Member (Queue d_afMV) r_afVd => d_afMV -> Sem r_afVd (QueueResult ())
- Polysemy.Conc.Queue: write :: forall d_afND r_afVV. Member (Queue d_afND) r_afVV => d_afND -> Sem r_afVV ()
+ Polysemy.Conc.Queue: write :: forall d_afMT r_afVb. Member (Queue d_afMT) r_afVb => d_afMT -> Sem r_afVb ()
- Polysemy.Conc.Queue: writeTimeout :: forall d_afNI r_afVZ t_X0. (Member (Queue d_afNI) r_afVZ, TimeUnit t_X0) => t_X0 -> d_afNI -> Sem r_afVZ (QueueResult ())
+ Polysemy.Conc.Queue: writeTimeout :: forall d_afMY r_afVf t_X0. (Member (Queue d_afMY) r_afVf, TimeUnit t_X0) => t_X0 -> d_afMY -> Sem r_afVf (QueueResult ())
- Polysemy.Conc.SyncRead: block :: forall d_ahcZ r_ahfF. Member (SyncRead d_ahcZ) r_ahfF => Sem r_ahfF d_ahcZ
+ Polysemy.Conc.SyncRead: block :: forall d_ahcf r_aheV. Member (SyncRead d_ahcf) r_aheV => Sem r_aheV d_ahcf
- Polysemy.Conc.SyncRead: empty :: forall d_ahd6 r_ahfJ. Member (SyncRead d_ahd6) r_ahfJ => Sem r_ahfJ Bool
+ Polysemy.Conc.SyncRead: empty :: forall d_ahcm r_aheZ. Member (SyncRead d_ahcm) r_aheZ => Sem r_aheZ Bool
- Polysemy.Conc.SyncRead: try :: forall d_ahd4 r_ahfI. Member (SyncRead d_ahd4) r_ahfI => Sem r_ahfI (Maybe d_ahd4)
+ Polysemy.Conc.SyncRead: try :: forall d_ahck r_aheY. Member (SyncRead d_ahck) r_aheY => Sem r_aheY (Maybe d_ahck)
- Polysemy.Conc.SyncRead: wait :: forall d_ahd2 r_ahfG u_X0. (Member (SyncRead d_ahd2) r_ahfG, TimeUnit u_X0) => u_X0 -> Sem r_ahfG (Maybe d_ahd2)
+ Polysemy.Conc.SyncRead: wait :: forall d_ahci r_aheW u_X0. (Member (SyncRead d_ahci) r_aheW, TimeUnit u_X0) => u_X0 -> Sem r_aheW (Maybe d_ahci)

Files

lib/Polysemy/Conc/Effect/Monitor.hs view
@@ -38,7 +38,7 @@ data MonitorCheck r =   MonitorCheck {     interval :: NanoSeconds,-    check :: MVar () -> Sem r ()+    check :: Sem r Bool   }  -- | Transform the stack of the check in a 'MonitorCheck'.@@ -47,7 +47,7 @@   MonitorCheck r ->   MonitorCheck r' hoistMonitorCheck f MonitorCheck {..} =-  MonitorCheck {check = f . check, ..}+  MonitorCheck {check = f check, ..}  -- | Start a region that can contain monitor-intervention regions. withMonitor ::
lib/Polysemy/Conc/Interpreter/Monitor.hs view
@@ -18,8 +18,8 @@ import qualified Polysemy.Conc.Effect.Race as Race import Polysemy.Conc.Effect.Race (Race) -newtype CancelResource =-  CancelResource { signal :: MVar () }+data CancelResource =+  CancelResource { kill :: MVar () }  data MonitorCancel =   MonitorCancel@@ -33,14 +33,23 @@   (CancelResource -> Sem r a) ->   Sem r a monitorRestart (MonitorCheck interval check) use = do-  sig <- embedFinal @IO newEmptyMVar-  withAsync_ (Time.loop_ @t @d interval (check sig)) (spin sig)-  where-    spin sig = do-      let res = (CancelResource sig)-      void (embedFinal @IO (tryTakeMVar sig))-      either (const (spin sig)) pure =<< errorToIOFinal @MonitorCancel (fromExceptionSem @MonitorCancel (raise (use res)))+  kill <- embedFinal @IO newEmptyMVar+  start <- embedFinal @IO newEmptyMVar+  let +    runCheck = do+      embedFinal (readMVar start)+      whenM check $ embedFinal @IO do+        takeMVar start+        void (tryPutMVar kill ())++    spin = do+      let res = CancelResource {..}+      embedFinal (putMVar start ())+      leftA (const spin) =<< errorToIOFinal @MonitorCancel (fromExceptionSem @MonitorCancel (raise (use res)))++  withAsync_ (Time.loop_ @t @d interval runCheck) spin+ -- | Interpret @'Polysemy.Conc.Scoped' 'Monitor'@ with the 'Polysemy.Conc.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'.@@ -51,8 +60,9 @@   InterpreterFor RestartingMonitor r interpretMonitorRestart check =   interpretScopedH (const (monitorRestart @t @d (hoistMonitorCheck raise check))) \ CancelResource {..} -> \case-    Monitor ma ->-      either (const (Base.throw MonitorCancel)) pure =<< Race.race (embedFinal @IO (readMVar signal)) (runTSimple ma)+    Monitor ma -> do+      void (embedFinal @IO (tryTakeMVar kill))+      leftA (const (Base.throw MonitorCancel)) =<< Race.race (embedFinal @IO (readMVar kill)) (runTSimple ma)  interpretMonitorPure' :: () -> InterpreterFor (Monitor action) r interpretMonitorPure' _ =
lib/Polysemy/Conc/Monitor.hs view
@@ -42,12 +42,8 @@   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)+  MonitorCheck interval do+    now <- Time.now @t @d+    atomicState \ s -> (Just now, maybe False (skewed now) s)+  where+    skewed now prev = minus (convert (difference now prev)) tolerance > interval
polysemy-conc.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           polysemy-conc-version:        0.14.0.0+version:        0.14.1.0 synopsis:       Polysemy effects for concurrency description:    See https://hackage.haskell.org/package/polysemy-conc/docs/Polysemy-Conc.html category:       Concurrency@@ -132,6 +132,7 @@       Polysemy.Conc.Test.MaskTest       Polysemy.Conc.Test.MonitorTest       Polysemy.Conc.Test.QueueTest+      Polysemy.Conc.Test.Run       Polysemy.Conc.Test.SyncTest   hs-source-dirs:       test@@ -185,6 +186,7 @@     , tasty >=1.4.2 && <1.5     , tasty-hedgehog >=1.3.0.0 && <1.5     , time >=1.12.2 && <1.13+    , torsor ==0.1.*   mixins:       base hiding (Prelude)     , incipit-core (IncipitCore as Prelude)
test/Main.hs view
@@ -1,6 +1,5 @@ module Main where -import Hedgehog (property, test, withTests) import Polysemy.Conc.Test.EventsTest (test_events) import Polysemy.Conc.Test.LockTest (test_lock) import Polysemy.Conc.Test.MaskTest (test_mask)@@ -13,39 +12,43 @@   test_queueTBM,   test_queueTimeoutTBM,   )+import Polysemy.Conc.Test.Run (unitTestTimes) import Polysemy.Conc.Test.SyncTest (test_sync, test_syncLock)-import Polysemy.Test (unitTest)-import Test.Tasty (TestTree, defaultMain, testGroup)-import Test.Tasty.Hedgehog (testProperty)+import Test.Tasty (TestTree, Timeout (NoTimeout), adjustOption, defaultMain, mkTimeout, testGroup) +defaultTimeout :: Timeout -> Timeout+defaultTimeout = \case+  NoTimeout -> mkTimeout 60_000_000+  t -> t+ tests :: TestTree tests =+  adjustOption defaultTimeout $   testGroup "main" [     testGroup "queue" [-      unitTest "TBM success" test_queueTBM,-      unitTest "TBM timeout" test_queueTimeoutTBM,-      unitTest "TBM peek" test_queuePeekTBM,-      unitTest "TBM block" test_queueBlockTBM,-      unitTest "TB success" test_queueTB,-      unitTest "TB block" test_queueBlockTB+      unitTestTimes 100 "TBM success" test_queueTBM,+      unitTestTimes 100 "TBM timeout" test_queueTimeoutTBM,+      unitTestTimes 100 "TBM peek" test_queuePeekTBM,+      unitTestTimes 10 "TBM block" test_queueBlockTBM,+      unitTestTimes 100 "TB success" test_queueTB,+      unitTestTimes 10 "TB block" test_queueBlockTB     ],     testGroup "events" [-      testProperty "events" (withTests 100 (property (test test_events)))+      unitTestTimes 100 "events" test_events     ],     testGroup "sync" [-      unitTest "sync" test_sync,-      unitTest "lock" test_syncLock+      unitTestTimes 100 "sync" test_sync,+      unitTestTimes 10 "lock" test_syncLock     ],     test_lock,     testGroup "mask" [-      unitTest "mask" test_mask+      unitTestTimes 10 "mask" test_mask     ],     testGroup "monitor" [-      unitTest "basic" test_monitorBasic,-      unitTest "clock skew" test_monitorClockSkew+      unitTestTimes 100 "basic" test_monitorBasic,+      unitTestTimes 1000 "clock skew" test_monitorClockSkew     ]   ]  main :: IO ()-main =-  defaultMain tests+main = defaultMain tests
test/Polysemy/Conc/Test/LockTest.hs view
@@ -2,7 +2,7 @@  module Polysemy.Conc.Test.LockTest where -import Polysemy.Test (UnitTest, assert, assertJust, runTestAuto, unitTest)+import Polysemy.Test (UnitTest, assert, assertJust, runTestAuto) import Polysemy.Time (GhcTime, MilliSeconds (MilliSeconds), Seconds (Seconds), interpretTimeGhc) import Test.Tasty (TestTree, testGroup) @@ -16,6 +16,7 @@ import Polysemy.Conc.Interpreter.Race (interpretRace) import Polysemy.Conc.Interpreter.Sync (interpretSync) import Polysemy.Conc.Race (timeout_)+import Polysemy.Conc.Test.Run (unitTestTimes)  interpretLockTest ::   Members [Resource, Embed IO, Final IO] r =>@@ -52,6 +53,6 @@ test_lock :: TestTree test_lock =   testGroup "lock" [-    unitTest "basic" test_lockBasic,-    unitTest "reentry" test_lockReentry+    unitTestTimes 10 "basic" test_lockBasic,+    unitTestTimes 10 "reentry" test_lockReentry   ]
test/Polysemy/Conc/Test/MonitorTest.hs view
@@ -2,18 +2,25 @@  module Polysemy.Conc.Test.MonitorTest where +import Control.Concurrent (threadDelay)+import qualified Data.Text as Text import Data.Time (UTCTime)-import Polysemy.Test (UnitTest, assertEq, assertJust, runTestAuto)+import Polysemy.Test (TestError (TestError), UnitTest, assertEq, assertJust, runTestAuto) import qualified Polysemy.Time as Time import Polysemy.Time (+  GhcTime,   Hours (Hours),   MilliSeconds (MilliSeconds),   Minutes (Minutes),   NanoSeconds (NanoSeconds),+  Seconds (Seconds),   Time,+  TimeUnit,+  convert,   interpretTimeGhc,   interpretTimeGhcConstantNow,   )+import Torsor (difference)  import Polysemy.Conc.AtomicState (interpretAtomic) import qualified Polysemy.Conc.Effect.Monitor as Monitor@@ -39,10 +46,9 @@  checker ::   Members [Time t d, Sync (), Embed IO] r =>-  MVar () ->-  Sem r ()-checker signal =-  Sync.takeBlock *> embed (putMVar signal ())+  Sem r Bool+checker =+  True <$ Sync.takeBlock  test_monitorBasic :: UnitTest test_monitorBasic =@@ -55,42 +61,123 @@   interpretMonitorRestart (MonitorCheck (NanoSeconds 0) checker) do     assertEq 3 =<< Monitor.restart prog +locked ::+  Members [AtomicState [Text], Error TestError, Embed IO] r =>+  Sem r a+locked = do+  embed (threadDelay 1_000_000)+  msgs <- atomicGet+  throw (TestError (Text.unlines ("Locked after:" : reverse msgs)))++takeBlock ::+  ∀ label r .+  HasCallStack =>+  Members [AtomicState [Text], Sync (Proxy label), Error TestError, Embed IO] r =>+  Text ->+  Sem r ()+takeBlock desc =+  withFrozenCallStack do+    atomicModify' (("taking '" <> desc <> "'") :)+    whenM (isNothing <$> Sync.takeWait (Seconds 1)) locked++putBlock ::+  ∀ label r .+  HasCallStack =>+  Members [AtomicState [Text], Sync (Proxy label), Error TestError, Embed IO] r =>+  Text ->+  Sem r ()+putBlock desc =+  withFrozenCallStack do+    atomicModify' (("putting '" <> desc <> "'") :)+    unlessM (Sync.putWait (Seconds 1) Proxy) locked+ progSkew ::-  Member (Sync (Proxy "start")) r =>-  Members [Monitor Restart, Time t d, AtomicState Int, Sync (Proxy 1), Sync (Proxy 2), Sync (Proxy 3)] r =>+  Members [Sync (Proxy "start"), Sync (Proxy "1"), Sync (Proxy "2"), Sync (Proxy "3"), Embed IO] r =>+  Members [Monitor Restart, AtomicState [Text], AtomicState Int, Error TestError, Time t d] r =>   Sem r Int-progSkew = do+progSkew =   Monitor.monitor do     atomicModify' (1 +)-    Sync.putBlock (Proxy @"start")-    void $ Sync.takeBlock @(Proxy 1)-    Sync.putBlock (Proxy @2)-    void $ Sync.takeBlock @(Proxy 3)+    putBlock @"start" "start"+    takeBlock @"1" "1"+    putBlock @"2" "2"+    takeBlock @"3" "3"     atomicGet +sleepPoll ::+  Members [GhcTime, Embed IO] r =>+  TimeUnit u =>+  u ->+  UTCTime ->+  Sem r ()+sleepPoll duration start =+  spin+  where+    spin = do+      embed (threadDelay 10_000)+      unlessM (later <$> Time.now) spin+    later now =+      difference now start >= diff+    diff =+      convert duration++interceptTime ::+  ∀ r a .+  Members [AtomicState [Text], GhcTime, Sync (Proxy "sleep"), Error TestError, Embed IO] r =>+  Sem r a ->+  Sem r a+interceptTime =+  intercept \case+    Time.Now -> do+      atomicModify' ("now" :)+      Time.now+    Time.Today ->+      Time.today+    Time.Sleep t -> do+      atomicModify' ("sleep" :)+      now <- Time.now+      putBlock @"sleep" "sleep"+      sleepPoll t now+      atomicModify' ("slept" :)+    Time.SetTime now ->+      Time.setTime now+    Time.Adjust (diff :: u) -> do+      atomicModify' ("adjust" :)+      send (Time.Adjust diff)+    Time.SetDate startAt ->+      Time.setDate startAt+ test_monitorClockSkew :: UnitTest test_monitorClockSkew =   runTestAuto $   asyncToIOFinal $   interpretRace $-  interpretTimeGhcConstantNow $   interpretAtomic (0 :: Int) $+  interpretAtomic ([] :: [Text]) $   interpretAtomic @(Maybe UTCTime) Nothing $   interpretSync @(Proxy "start") $-  interpretSync @(Proxy 1) $-  interpretSync @(Proxy 2) $-  interpretSync @(Proxy 3) $-  interpretMonitorRestart (monitorClockSkew (clockSkewConfig (MilliSeconds 1) (Minutes 30))) do+  interpretSync @(Proxy "sleep") $+  interpretSync @(Proxy "1") $+  interpretSync @(Proxy "2") $+  interpretSync @(Proxy "3") $+  interpretTimeGhcConstantNow $+  interceptTime $+  interpretMonitorRestart monitor do     h <- async (Monitor.restart progSkew)-    _ <- Sync.takeBlock @(Proxy "start")+    takeBlock @"start" "start 1"+    takeBlock @"sleep" "sleep"     Time.adjust (Hours 1)-    _ <- Sync.takeBlock @(Proxy "start")+    _ <- takeBlock @"start" "start 2"+    takeBlock @"sleep" "sleep"     Time.adjust (Minutes 10)-    Sync.putBlock (Proxy @1)-    Sync.takeBlock @(Proxy 2)+    putBlock @"1" "1 1"+    takeBlock @"2" "2 1"+    takeBlock @"sleep" "sleep"     Time.adjust (Hours 1)-    _ <- Sync.takeBlock @(Proxy "start")-    Sync.putBlock (Proxy @1)-    Sync.takeBlock @(Proxy 2)-    Sync.putBlock (Proxy @3)+    _ <- takeBlock @"start" "start 3"+    putBlock @"1" "1 2"+    takeBlock @"2" "2 2"+    putBlock @"3" "3"     assertJust 3 =<< await h+  where+    monitor = monitorClockSkew (clockSkewConfig (MilliSeconds 1) (Minutes 30))
+ test/Polysemy/Conc/Test/Run.hs view
@@ -0,0 +1,14 @@+module Polysemy.Conc.Test.Run where++import Hedgehog (TestLimit, property, test, withTests)+import Polysemy.Test (UnitTest)+import Test.Tasty (TestName, TestTree)+import Test.Tasty.Hedgehog (testProperty)++unitTestTimes ::+  TestLimit ->+  TestName ->+  UnitTest ->+  TestTree+unitTestTimes n desc =+  testProperty desc . withTests n . property . test