on master help sign in
Concurrent effect (#38)

* Renamed AsyncE to Concurrent * Renamed Concurrent to Concurrent.Async Also moved Concurrent effect to Concurrent.Internal module. * Added 'Effectful.Concurrent.Base' This module contains the low-level concurrency functions found in 'Control.Concurrent' in the base package. * Fixed benchmarks * Fixed: Arguments order in EnvironmentTests

commit871a88a40b24687e05beafb5e43f4891cd28ab85 authorMann mit Hut <mmhat@users.noreply.github.com> dateSat 27 Nov 2021, 10:57 (5 years ago) parentc21c98f
8 files changed+315−105
effectful/bench/Concurrency.hs+6−6 effectful/bench/Unlift.hs+11−11 effectful/effectful.cabal+4−1 effectful/src/Effectful/Async.hs → effectful/src/Effectful/Concurrent/Async.hs+62−76 effectful/src/Effectful/Concurrent/Base.hs+200 effectful/src/Effectful/Concurrent/Internal.hs+20 effectful/tests/AsyncTests.hs+6−6 effectful/tests/EnvironmentTests.hs+6−5
effectful/bench/Concurrency.hs+6−6
@@ -11,7 +11,7 @@ import Test.Tasty.Bench hiding (env)
1111
1212import Control.Concurrent.Async
1313import Control.Monad
14−import qualified Effectful.Async as A
14+import qualified Effectful.Concurrent.Async as A
1515
1616import Effectful
1717import Utils
@@ -25,7 +25,7 @@ concurrencyBenchmark = bgroup "concurrency"
2525shallowBench :: Int -> Benchmark
2626shallowBench n = bgroup ("unmask " ++ show n ++ "x")
2727 [ bench "async (IO)" $ nfAppIO (asyncBenchIO n) op
28− , bench "async (Eff)" $ nfAppIO (runShallow . A.runAsyncE . asyncBench n) op
28+ , bench "async (Eff)" $ nfAppIO (runShallow . A.runConcurrent . asyncBench n) op
2929 , bench "Fork (localUnliftIO/withLiftMapIO)" $
3030 nfAppIO (runShallow . runFork1 . forkBench n) op
3131 , bench "Fork (localUnlift/withLiftMap)" $
@@ -38,7 +38,7 @@ shallowBench n = bgroup ("unmask " ++ show n ++ "x")
3838
3939deepBench :: Int -> Benchmark
4040deepBench n = bgroup ("unmask " ++ show n ++ "x")
41− [ bench "async (Eff)" $ nfAppIO (runDeep . A.runAsyncE . asyncBench n) op
41+ [ bench "async (Eff)" $ nfAppIO (runDeep . A.runConcurrent . asyncBench n) op
4242 , bench "Fork (localUnliftIO/withLiftMapIO)" $
4343 nfAppIO (runDeep . runFork1 . forkBench n) op
4444 , bench "Fork (localUnlift/withLiftMap)" $
@@ -66,7 +66,7 @@ runFork1 = interpret $ \env -> \case
6666
6767-- | Uses 'localUnlift' and 'withLiftMap'.
6868runFork2 :: IOE :> es => Eff (Fork : es) a -> Eff es a
69−runFork2 = reinterpret A.runAsyncE $ \env -> \case
69+runFork2 = reinterpret A.runConcurrent $ \env -> \case
7070 ForkWithUnmask m -> withLiftMap env $ \liftMap -> do
7171 localUnlift env (ConcUnlift Ephemeral $ Limited 1) $ \unlift -> do
7272 A.asyncWithUnmask $ \unmask -> unlift $ m $ liftMap unmask
@@ -80,7 +80,7 @@ runFork3 = interpret $ \env -> \case
8080
8181-- | Uses 'localLiftUnlift'.
8282runFork4 :: IOE :> es => Eff (Fork : es) a -> Eff es a
83−runFork4 = reinterpret A.runAsyncE $ \env -> \case
83+runFork4 = reinterpret A.runConcurrent $ \env -> \case
8484 ForkWithUnmask m -> do
8585 localLiftUnlift env (ConcUnlift Persistent $ Limited 1) $ \lift unlift -> do
8686 A.asyncWithUnmask $ \unmask -> unlift $ m $ lift . unmask . unlift
@@ -94,7 +94,7 @@ asyncBenchIO n f = do
9494 wait a
9595{-# NOINLINE asyncBenchIO #-}
9696
97−asyncBench :: A.AsyncE :> es => Int -> Eff es Int -> Eff es Int
97+asyncBench :: A.Concurrent :> es => Int -> Eff es Int -> Eff es Int
9898asyncBench n f = do
9999 a <- A.asyncWithUnmask $ \unmask -> do
100100 sum <$> replicateM n (unmask f)
effectful/bench/Unlift.hs+11−11
@@ -14,7 +14,7 @@ import qualified UnliftIO as U
1414import Effectful
1515import Effectful.State.Dynamic
1616import Utils
17−import qualified Effectful.Async as A
17+import qualified Effectful.Concurrent.Async as A
1818
1919unliftBenchmark :: Benchmark
2020unliftBenchmark = bgroup "unlifting"
@@ -29,7 +29,7 @@ unliftBenchmark = bgroup "unlifting"
2929 , bench "seq" $ nfAppIO runShallow benchDummy
3030 , shallowConc "conc" 1 benchDummyE benchDummy
3131 ]
32− , shallowConc "async" 1 benchAsyncE benchAsync
32+ , shallowConc "async" 1 benchAsyncEff benchAsync
3333 , shallowConc "concurrently" 2 benchConcurrentlyE benchConcurrently
3434 ]
3535 , bgroup "deep"
@@ -38,7 +38,7 @@ unliftBenchmark = bgroup "unlifting"
3838 , bench "seq" $ nfAppIO runDeep benchDummy
3939 , deepConc "conc" 1 benchDummyE benchDummy
4040 ]
41− , deepConc "async" 1 benchAsyncE benchAsync
41+ , deepConc "async" 1 benchAsyncEff benchAsync
4242 , deepConc "concurrently" 2 benchConcurrentlyE benchConcurrently
4343 ]
4444 ]
@@ -48,11 +48,11 @@ unliftBenchmark = bgroup "unlifting"
4848shallowConc
4949 :: String
5050 -> Int
51− -> Eff '[A.AsyncE, IOE] ()
51+ -> Eff '[A.Concurrent, IOE] ()
5252 -> Eff '[IOE] ()
5353 -> Benchmark
5454shallowConc name n r f = bgroup name
55− [ bench "AsyncE" $ nfAppIO (runShallow . A.runAsyncE) r
55+ [ bench "Async" $ nfAppIO (runShallow . A.runConcurrent) r
5656 , bench "ephemeral/limited" $ nfAppIO
5757 (runShallow . withUnliftStrategy (ConcUnlift Ephemeral $ Limited n)) f
5858 , bench "ephemeral/unlimited" $ nfAppIO
@@ -66,7 +66,7 @@ shallowConc name n r f = bgroup name
6666deepConc
6767 :: String
6868 -> Int
69− -> Eff '[ A.AsyncE
69+ -> Eff '[ A.Concurrent
7070 , State (), State (), State (), State (), State ()
7171 , State (), State (), State (), State (), State ()
7272 , IOE] ()
@@ -75,7 +75,7 @@ deepConc
7575 , IOE] ()
7676 -> Benchmark
7777deepConc name n r f = bgroup name
78− [ bench "AsyncE" $ nfAppIO (runDeep . A.runAsyncE) r
78+ [ bench "Async" $ nfAppIO (runDeep . A.runConcurrent) r
7979 , bench "ephemeral/limited" $ nfAppIO
8080 (runDeep . withUnliftStrategy (ConcUnlift Ephemeral $ Limited n)) f
8181 , bench "ephemeral/unlimited" $ nfAppIO
@@ -101,10 +101,10 @@ benchDummyE :: Eff es ()
101101benchDummyE = pure ()
102102{-# NOINLINE benchDummyE #-}
103103
104−benchAsyncE :: A.AsyncE :> es => Eff es ()
105−benchAsyncE = A.withAsync (pure ()) A.wait
106−{-# NOINLINE benchAsyncE #-}
104+benchAsyncEff :: A.Concurrent :> es => Eff es ()
105+benchAsyncEff = A.withAsync (pure ()) A.wait
106+{-# NOINLINE benchAsyncEff #-}
107107
108−benchConcurrentlyE :: A.AsyncE :> es => Eff es ()
108+benchConcurrentlyE :: A.Concurrent :> es => Eff es ()
109109benchConcurrentlyE = A.concurrently_ (pure ()) (pure ())
110110{-# NOINLINE benchConcurrentlyE #-}
effectful/effectful.cabal+4−1
@@ -74,7 +74,8 @@ library
7474 hs-source-dirs: src
7575
7676 exposed-modules: Effectful
77− Effectful.Async
77+ Effectful.Concurrent.Async
78+ Effectful.Concurrent.Base
7879 Effectful.Environment
7980 Effectful.FileSystem
8081 Effectful.Process
@@ -82,6 +83,8 @@ library
8283 Effectful.Temporary
8384 Effectful.Timeout
8485
86+ other-modules: Effectful.Concurrent.Internal
87+
8588 reexported-modules: Effectful.Error
8689 , Effectful.Error.Dynamic
8790 , Effectful.Fail
effectful/src/Effectful/Async.hs → effectful/src/Effectful/Concurrent/Async.hsrenamed+62−76
@@ -1,8 +1,8 @@
11{-# LANGUAGE UndecidableInstances #-}
2−module Effectful.Async
3− ( -- * Async effect
4− AsyncE
5− , runAsyncE
2+module Effectful.Concurrent.Async
3+ ( -- * Concurrent effect
4+ Concurrent
5+ , runConcurrent
66
77 -- * Asynchronous actions
88 , Async
@@ -80,47 +80,33 @@ import qualified Control.Concurrent.Async as A
8080import qualified UnliftIO.Async as U
8181import qualified UnliftIO.Internals.Async as I
8282
83+import Effectful.Concurrent.Internal
8384import Effectful.Internal.Effect
8485import Effectful.Internal.Env
8586import Effectful.Internal.Monad
8687
87−-- | Run 'Eff' operations asynchronously via the @async@ library.
88−--
89−-- /Note:/ thread local state changes in 'Eff' operations run asynchronously
90−-- will not affect the parent thread.
91−--
92−-- /TODO:/ write about 'AsyncE' not respecting scoped operations.
93−data AsyncE :: Effect where
94− AsyncE :: AsyncE m r
95−
96−runAsyncE :: IOE :> es => Eff (AsyncE : es) a -> Eff es a
97−runAsyncE = evalEffect (IdE AsyncE)
98−
99−----------------------------------------
100−-- Operations
101−
10288-- | Lifted 'A.async'.
103−async :: AsyncE :> es => Eff es a -> Eff es (Async a)
89+async :: Concurrent :> es => Eff es a -> Eff es (Async a)
10490async = liftAsync A.async
10591
10692-- | Lifted 'A.asyncBound'.
107−asyncBound :: AsyncE :> es => Eff es a -> Eff es (Async a)
93+asyncBound :: Concurrent :> es => Eff es a -> Eff es (Async a)
10894asyncBound = liftAsync A.asyncBound
10995
11096-- | Lifted 'A.asyncOn'.
111−asyncOn :: AsyncE :> es => Int -> Eff es a -> Eff es (Async a)
97+asyncOn :: Concurrent :> es => Int -> Eff es a -> Eff es (Async a)
11298asyncOn cpu = liftAsync (A.asyncOn cpu)
11399
114100-- | Lifted 'A.asyncWithUnmask'.
115101asyncWithUnmask
116− :: AsyncE :> es
102+ :: Concurrent :> es
117103 => ((forall b. Eff es b -> Eff es b) -> Eff es a)
118104 -> Eff es (Async a)
119105asyncWithUnmask = liftAsyncWithUnmask A.asyncWithUnmask
120106
121107-- | Lifted 'A.asyncOnWithUnmask'.
122108asyncOnWithUnmask
123− :: AsyncE :> es
109+ :: Concurrent :> es
124110 => Int
125111 -> ((forall b. Eff es b -> Eff es b) -> Eff es a)
126112 -> Eff es (Async a)
@@ -128,7 +114,7 @@ asyncOnWithUnmask cpu = liftAsyncWithUnmask (A.asyncOnWithUnmask cpu)
128114
129115-- | Lifted 'A.withAsync'.
130116withAsync
131− :: AsyncE :> es
117+ :: Concurrent :> es
132118 => Eff es a
133119 -> (Async a -> Eff es b)
134120 -> Eff es b
@@ -136,7 +122,7 @@ withAsync = liftWithAsync A.withAsync
136122
137123-- | Lifted 'A.withAsyncBound'.
138124withAsyncBound
139− :: AsyncE :> es
125+ :: Concurrent :> es
140126 => Eff es a
141127 -> (Async a -> Eff es b)
142128 -> Eff es b
@@ -144,7 +130,7 @@ withAsyncBound = liftWithAsync A.withAsyncBound
144130
145131-- | Lifted 'A.withAsyncOn'.
146132withAsyncOn
147− :: AsyncE :> es
133+ :: Concurrent :> es
148134 => Int
149135 -> Eff es a
150136 -> (Async a -> Eff es b)
@@ -153,7 +139,7 @@ withAsyncOn cpu = liftWithAsync (A.withAsyncOn cpu)
153139
154140-- | Lifted 'A.withAsyncWithUnmask'.
155141withAsyncWithUnmask
156− :: AsyncE :> es
142+ :: Concurrent :> es
157143 => ((forall c. Eff es c -> Eff es c) -> Eff es a)
158144 -> (Async a -> Eff es b)
159145 -> Eff es b
@@ -161,7 +147,7 @@ withAsyncWithUnmask = liftWithAsyncWithUnmask A.withAsyncWithUnmask
161147
162148-- | Lifted 'A.withAsyncOnWithUnmask'.
163149withAsyncOnWithUnmask
164− :: AsyncE :> es
150+ :: Concurrent :> es
165151 => Int
166152 -> ((forall c. Eff es c -> Eff es c) -> Eff es a)
167153 -> (Async a -> Eff es b)
@@ -169,60 +155,60 @@ withAsyncOnWithUnmask
169155withAsyncOnWithUnmask cpu = liftWithAsyncWithUnmask (A.withAsyncOnWithUnmask cpu)
170156
171157-- | Lifted 'A.wait'.
172−wait :: AsyncE :> es => Async a -> Eff es a
158+wait :: Concurrent :> es => Async a -> Eff es a
173159wait = unsafeEff_ . A.wait
174160
175161-- | Lifted 'A.poll'.
176162poll
177− :: AsyncE :> es
163+ :: Concurrent :> es
178164 => Async a
179165 -> Eff es (Maybe (Either SomeException a))
180166poll = unsafeEff_ . A.poll
181167
182168-- | Lifted 'A.cancel'.
183−cancel :: AsyncE :> es => Async a -> Eff es ()
169+cancel :: Concurrent :> es => Async a -> Eff es ()
184170cancel = unsafeEff_ . A.cancel
185171
186172-- | Lifted 'A.cancelWith'.
187−cancelWith :: (Exception e, AsyncE :> es) => Async a -> e -> Eff es ()
173+cancelWith :: (Exception e, Concurrent :> es) => Async a -> e -> Eff es ()
188174cancelWith a = unsafeEff_ . A.cancelWith a
189175
190176-- | Lifted 'A.uninterruptibleCancel'.
191−uninterruptibleCancel :: AsyncE :> es => Async a -> Eff es ()
177+uninterruptibleCancel :: Concurrent :> es => Async a -> Eff es ()
192178uninterruptibleCancel = unsafeEff_ . A.uninterruptibleCancel
193179
194180-- | Lifted 'A.waitCatch'.
195181waitCatch
196− :: AsyncE :> es
182+ :: Concurrent :> es
197183 => Async a
198184 -> Eff es (Either SomeException a)
199185waitCatch = unsafeEff_ . A.waitCatch
200186
201187-- | Lifted 'A.waitAny'.
202−waitAny :: AsyncE :> es => [Async a] -> Eff es (Async a, a)
188+waitAny :: Concurrent :> es => [Async a] -> Eff es (Async a, a)
203189waitAny = unsafeEff_ . A.waitAny
204190
205191-- | Lifted 'A.waitAnyCatch'.
206192waitAnyCatch
207− :: AsyncE :> es
193+ :: Concurrent :> es
208194 => [Async a]
209195 -> Eff es (Async a, Either SomeException a)
210196waitAnyCatch = unsafeEff_ . A.waitAnyCatch
211197
212198-- | Lifted 'A.waitAnyCancel'.
213−waitAnyCancel :: AsyncE :> es => [Async a] -> Eff es (Async a, a)
199+waitAnyCancel :: Concurrent :> es => [Async a] -> Eff es (Async a, a)
214200waitAnyCancel = unsafeEff_ . A.waitAnyCancel
215201
216202-- | Lifted 'A.waitAnyCatchCancel'.
217203waitAnyCatchCancel
218− :: AsyncE :> es
204+ :: Concurrent :> es
219205 => [Async a]
220206 -> Eff es (Async a, Either SomeException a)
221207waitAnyCatchCancel = unsafeEff_ . A.waitAnyCatchCancel
222208
223209-- | Lifted 'A.waitEither'.
224210waitEither
225− :: AsyncE :> es
211+ :: Concurrent :> es
226212 => Async a
227213 -> Async b
228214 -> Eff es (Either a b)
@@ -230,7 +216,7 @@ waitEither a b = unsafeEff_ $ A.waitEither a b
230216
231217-- | Lifted 'A.waitEitherCatch'.
232218waitEitherCatch
233− :: AsyncE :> es
219+ :: Concurrent :> es
234220 => Async a
235221 -> Async b
236222 -> Eff es (Either (Either SomeException a) (Either SomeException b))
@@ -238,7 +224,7 @@ waitEitherCatch a b = unsafeEff_ $ A.waitEitherCatch a b
238224
239225-- | Lifted 'A.waitEitherCancel'.
240226waitEitherCancel
241− :: AsyncE :> es
227+ :: Concurrent :> es
242228 => Async a
243229 -> Async b
244230 -> Eff es (Either a b)
@@ -246,53 +232,53 @@ waitEitherCancel a b = unsafeEff_ $ A.waitEitherCancel a b
246232
247233-- | Lifted 'A.waitEitherCatchCancel'.
248234waitEitherCatchCancel
249− :: AsyncE :> es
235+ :: Concurrent :> es
250236 => Async a
251237 -> Async b
252238 -> Eff es (Either (Either SomeException a) (Either SomeException b))
253239waitEitherCatchCancel a b = unsafeEff_ $ A.waitEitherCatch a b
254240
255241-- | Lifted 'A.waitEither_'.
256−waitEither_ :: AsyncE :> es => Async a -> Async b -> Eff es ()
242+waitEither_ :: Concurrent :> es => Async a -> Async b -> Eff es ()
257243waitEither_ a b = unsafeEff_ $ A.waitEither_ a b
258244
259245-- | Lifted 'A.waitBoth'.
260−waitBoth :: AsyncE :> es => Async a -> Async b -> Eff es (a, b)
246+waitBoth :: Concurrent :> es => Async a -> Async b -> Eff es (a, b)
261247waitBoth a b = unsafeEff_ $ A.waitBoth a b
262248
263249-- | Lifted 'A.link'.
264−link :: AsyncE :> es => Async a -> Eff es ()
250+link :: Concurrent :> es => Async a -> Eff es ()
265251link = unsafeEff_ . A.link
266252
267253-- | Lifted 'A.linkOnly'.
268−linkOnly :: AsyncE :> es => (SomeException -> Bool) -> Async a -> Eff es ()
254+linkOnly :: Concurrent :> es => (SomeException -> Bool) -> Async a -> Eff es ()
269255linkOnly f = unsafeEff_ . A.linkOnly f
270256
271257-- | Lifted 'A.link2'.
272−link2 :: AsyncE :> es => Async a -> Async b -> Eff es ()
258+link2 :: Concurrent :> es => Async a -> Async b -> Eff es ()
273259link2 a b = unsafeEff_ $ A.link2 a b
274260
275261-- | Lifted 'A.link2Only'.
276−link2Only :: AsyncE :> es => (SomeException -> Bool) -> Async a -> Async b -> Eff es ()
262+link2Only :: Concurrent :> es => (SomeException -> Bool) -> Async a -> Async b -> Eff es ()
277263link2Only f a b = unsafeEff_ $ A.link2Only f a b
278264
279265-- | Lifted 'A.race'.
280−race :: AsyncE :> es => Eff es a -> Eff es b -> Eff es (Either a b)
266+race :: Concurrent :> es => Eff es a -> Eff es b -> Eff es (Either a b)
281267race ma mb = unsafeEff $ \es -> do
282268 A.race (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
283269
284270-- | Lifted 'A.race_'.
285−race_ :: AsyncE :> es => Eff es a -> Eff es b -> Eff es ()
271+race_ :: Concurrent :> es => Eff es a -> Eff es b -> Eff es ()
286272race_ ma mb = unsafeEff $ \es -> do
287273 A.race_ (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
288274
289275-- | Lifted 'A.concurrently'.
290−concurrently :: AsyncE :> es => Eff es a -> Eff es b -> Eff es (a, b)
276+concurrently :: Concurrent :> es => Eff es a -> Eff es b -> Eff es (a, b)
291277concurrently ma mb = unsafeEff $ \es -> do
292278 A.concurrently (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
293279
294280-- | Lifted 'A.concurrently_'.
295−concurrently_ :: AsyncE :> es => Eff es a -> Eff es b -> Eff es ()
281+concurrently_ :: Concurrent :> es => Eff es a -> Eff es b -> Eff es ()
296282concurrently_ ma mb = unsafeEff $ \es -> do
297283 A.concurrently_ (unEff ma =<< cloneEnv es) (unEff mb =<< cloneEnv es)
298284
@@ -302,7 +288,7 @@ concurrently_ ma mb = unsafeEff $ \es -> do
302288
303289-- | Lifted 'A.mapConcurrently'.
304290mapConcurrently
305− :: (Traversable f, AsyncE :> es)
291+ :: (Traversable f, Concurrent :> es)
306292 => (a -> Eff es b)
307293 -> f a
308294 -> Eff es (f b)
@@ -311,7 +297,7 @@ mapConcurrently f t = unsafeEff $ \es -> do
311297
312298-- | Lifted 'A.mapConcurrently_'.
313299mapConcurrently_
314− :: (Foldable f, AsyncE :> es)
300+ :: (Foldable f, Concurrent :> es)
315301 => (a -> Eff es b)
316302 -> f a
317303 -> Eff es ()
@@ -320,7 +306,7 @@ mapConcurrently_ f t = unsafeEff $ \es -> do
320306
321307-- | Lifted 'A.forConcurrently'.
322308forConcurrently
323− :: (Traversable f, AsyncE :> es)
309+ :: (Traversable f, Concurrent :> es)
324310 => f a
325311 -> (a -> Eff es b)
326312 -> Eff es (f b)
@@ -329,7 +315,7 @@ forConcurrently t f = unsafeEff $ \es -> do
329315
330316-- | Lifted 'A.forConcurrently_'.
331317forConcurrently_
332− :: (Foldable f, AsyncE :> es)
318+ :: (Foldable f, Concurrent :> es)
333319 => f a
334320 -> (a -> Eff es b)
335321 -> Eff es ()
@@ -337,12 +323,12 @@ forConcurrently_ t f = unsafeEff $ \es -> do
337323 U.forConcurrently_ t (\a -> unEff (f a) =<< cloneEnv es)
338324
339325-- | Lifted 'A.replicateConcurrently'.
340−replicateConcurrently :: AsyncE :> es => Int -> Eff es a -> Eff es [a]
326+replicateConcurrently :: Concurrent :> es => Int -> Eff es a -> Eff es [a]
341327replicateConcurrently n f = unsafeEff $ \es -> do
342328 U.replicateConcurrently n (unEff f =<< cloneEnv es)
343329
344330-- | Lifted 'A.replicateConcurrently_'.
345−replicateConcurrently_ :: AsyncE :> es => Int -> Eff es a -> Eff es ()
331+replicateConcurrently_ :: Concurrent :> es => Int -> Eff es a -> Eff es ()
346332replicateConcurrently_ n f = unsafeEff $ \es -> do
347333 U.replicateConcurrently_ n (unEff f =<< cloneEnv es)
348334
@@ -351,7 +337,7 @@ replicateConcurrently_ n f = unsafeEff $ \es -> do
351337
352338-- | Lifted 'U.pooledMapConcurrentlyN'.
353339pooledMapConcurrentlyN
354− :: (AsyncE :> es, Traversable t)
340+ :: (Concurrent :> es, Traversable t)
355341 => Int
356342 -> (a -> Eff es b)
357343 -> t a
@@ -361,7 +347,7 @@ pooledMapConcurrentlyN threads f t = unsafeEff $ \es -> do
361347
362348-- | Lifted 'U.pooledMapConcurrently'.
363349pooledMapConcurrently
364− :: (AsyncE :> es, Traversable t)
350+ :: (Concurrent :> es, Traversable t)
365351 => (a -> Eff es b)
366352 -> t a
367353 -> Eff es (t b)
@@ -370,7 +356,7 @@ pooledMapConcurrently f t = unsafeEff $ \es -> do
370356
371357-- | Lifted 'U.pooledMapConcurrentlyN'.
372358pooledMapConcurrentlyN_
373− :: (AsyncE :> es, Foldable f)
359+ :: (Concurrent :> es, Foldable f)
374360 => Int
375361 -> (a -> Eff es b)
376362 -> f a
@@ -380,7 +366,7 @@ pooledMapConcurrentlyN_ threads f t = unsafeEff $ \es -> do
380366
381367-- | Lifted 'U.pooledMapConcurrently_'.
382368pooledMapConcurrently_
383− :: (AsyncE :> es, Foldable f)
369+ :: (Concurrent :> es, Foldable f)
384370 => (a -> Eff es b)
385371 -> f a
386372 -> Eff es ()
@@ -389,7 +375,7 @@ pooledMapConcurrently_ f t = unsafeEff $ \es -> do
389375
390376-- | Lifted 'U.pooledForConcurrentlyN'.
391377pooledForConcurrentlyN
392− :: (AsyncE :> es, Traversable t)
378+ :: (Concurrent :> es, Traversable t)
393379 => Int
394380 -> t a
395381 -> (a -> Eff es b)
@@ -399,7 +385,7 @@ pooledForConcurrentlyN threads t f = unsafeEff $ \es -> do
399385
400386-- | Lifted 'U.pooledForConcurrently'.
401387pooledForConcurrently
402− :: (AsyncE :> es, Traversable t)
388+ :: (Concurrent :> es, Traversable t)
403389 => t a
404390 -> (a -> Eff es b)
405391 -> Eff es (t b)
@@ -408,7 +394,7 @@ pooledForConcurrently t f = unsafeEff $ \es -> do
408394
409395-- | Lifted 'U.pooledForConcurrentlyN'.
410396pooledForConcurrentlyN_
411− :: (AsyncE :> es, Foldable f)
397+ :: (Concurrent :> es, Foldable f)
412398 => Int
413399 -> f a
414400 -> (a -> Eff es b)
@@ -418,7 +404,7 @@ pooledForConcurrentlyN_ threads t f = unsafeEff $ \es -> do
418404
419405-- | Lifted 'U.pooledForConcurrently_'.
420406pooledForConcurrently_
421− :: (AsyncE :> es, Foldable f)
407+ :: (Concurrent :> es, Foldable f)
422408 => f a
423409 -> (a -> Eff es b)
424410 -> Eff es ()
@@ -426,22 +412,22 @@ pooledForConcurrently_ t f = unsafeEff $ \es -> do
426412 U.pooledForConcurrently_ t (\a -> unEff (f a) =<< cloneEnv es)
427413
428414-- | Lifted 'U.pooledReplicateConcurrentlyN'.
429−pooledReplicateConcurrentlyN :: AsyncE :> es => Int -> Int -> Eff es a -> Eff es [a]
415+pooledReplicateConcurrentlyN :: Concurrent :> es => Int -> Int -> Eff es a -> Eff es [a]
430416pooledReplicateConcurrentlyN threads n f = unsafeEff $ \es -> do
431417 U.pooledReplicateConcurrentlyN threads n (unEff f =<< cloneEnv es)
432418
433419-- | Lifted 'U.pooledReplicateConcurrently'.
434−pooledReplicateConcurrently :: AsyncE :> es => Int -> Eff es a -> Eff es [a]
420+pooledReplicateConcurrently :: Concurrent :> es => Int -> Eff es a -> Eff es [a]
435421pooledReplicateConcurrently n f = unsafeEff $ \es -> do
436422 U.pooledReplicateConcurrently n (unEff f =<< cloneEnv es)
437423
438424-- | Lifted 'U.pooledReplicateConcurrentlyN_'.
439−pooledReplicateConcurrentlyN_ :: AsyncE :> es => Int -> Int -> Eff es a -> Eff es ()
425+pooledReplicateConcurrentlyN_ :: Concurrent :> es => Int -> Int -> Eff es a -> Eff es ()
440426pooledReplicateConcurrentlyN_ threads n f = unsafeEff $ \es -> do
441427 U.pooledReplicateConcurrentlyN_ threads n (unEff f =<< cloneEnv es)
442428
443429-- | Lifted 'U.pooledReplicateConcurrently_'.
444−pooledReplicateConcurrently_ :: AsyncE :> es => Int -> Eff es a -> Eff es ()
430+pooledReplicateConcurrently_ :: Concurrent :> es => Int -> Eff es a -> Eff es ()
445431pooledReplicateConcurrently_ n f = unsafeEff $ \es -> do
446432 U.pooledReplicateConcurrently_ n (unEff f =<< cloneEnv es)
447433
@@ -481,7 +467,7 @@ conc :: Eff es a -> Conc es a
481467conc = Action
482468
483469-- | Lifted 'U.runConc'.
484−runConc :: AsyncE :> es => Conc es a -> Eff es a
470+runConc :: Concurrent :> es => Conc es a -> Eff es a
485471runConc m = unsafeEff $ \es -> U.runConc (unliftConc es m)
486472 where
487473 unliftConc :: Env es -> Conc es a -> U.Conc IO a
@@ -502,20 +488,20 @@ newtype Concurrently es a = Concurrently { runConcurrently :: Eff es a }
502488instance Functor (Concurrently es) where
503489 fmap f (Concurrently a) = Concurrently (fmap f a)
504490
505−instance AsyncE :> es => Applicative (Concurrently es) where
491+instance Concurrent :> es => Applicative (Concurrently es) where
506492 pure = Concurrently . pure
507493 Concurrently fs <*> Concurrently as =
508494 Concurrently ((\(f, a) -> f a) <$> concurrently fs as)
509495
510−instance AsyncE :> es => Alternative (Concurrently es) where
496+instance Concurrent :> es => Alternative (Concurrently es) where
511497 empty = Concurrently . unsafeEff_ . forever $ threadDelay maxBound
512498 Concurrently as <|> Concurrently bs =
513499 Concurrently (either id id <$> race as bs)
514500
515−instance (AsyncE :> es, Semigroup a) => Semigroup (Concurrently es a) where
501+instance (Concurrent :> es, Semigroup a) => Semigroup (Concurrently es a) where
516502 (<>) = liftA2 (<>)
517503
518−instance (AsyncE :> es, Monoid a) => Monoid (Concurrently es a) where
504+instance (Concurrent :> es, Monoid a) => Monoid (Concurrently es a) where
519505 mempty = pure mempty
520506
521507----------------------------------------
effectful/src/Effectful/Concurrent/Base.hsnew file+200
@@ -0,0 +1,200 @@
1+module Effectful.Concurrent.Base
2+ ( -- * Concurrent effect
3+ Concurrent
4+ , runConcurrent
5+
6+ -- * Basic concurrency operations
7+ , myThreadId
8+ , forkIO
9+ , forkFinally
10+ , forkIOWithUnmask
11+ , killThread
12+ , throwTo
13+
14+ -- ** Threads with affinity
15+ , forkOn
16+ , forkOnWithUnmask
17+ , getNumCapabilities
18+ , setNumCapabilities
19+ , threadCapability
20+
21+ -- * Scheduling
22+ , yield
23+
24+ -- ** Waiting
25+ , threadDelay
26+ , threadWaitRead
27+ , threadWaitWrite
28+ , threadWaitReadSTM
29+ , threadWaitWriteSTM
30+
31+ -- * Bound threads
32+ , forkOS
33+ , forkOSWithUnmask
34+ , isCurrentThreadBound
35+ , runInBoundThread
36+ , runInUnboundThread
37+
38+ -- * Weak references to ThreadIds
39+ , mkWeakThreadId
40+
41+ -- * Re-exports
42+ , C.rtsSupportsBoundThreads
43+ ) where
44+
45+import qualified Control.Concurrent as C
46+import Control.Exception (Exception, SomeException)
47+import Data.Bifunctor (second)
48+import System.Mem.Weak (Weak)
49+import System.Posix.Types (Fd)
50+import UnliftIO.STM (STM)
51+
52+import Effectful.Concurrent.Internal
53+import Effectful.Internal.Effect
54+import Effectful.Internal.Env
55+import Effectful.Internal.Monad
56+
57+----------------------------------------
58+-- Basic concurrency operations
59+
60+-- | Lifted 'C.myThreadId'.
61+myThreadId :: Concurrent :> es => Eff es C.ThreadId
62+myThreadId = unsafeEff_ C.myThreadId
63+
64+-- | Lifted 'C.forkIO'.
65+forkIO :: Concurrent :> es => Eff es () -> Eff es C.ThreadId
66+forkIO k = unsafeEff $ \es -> do
67+ esF <- cloneEnv es
68+ C.forkIO $ unEff k esF
69+
70+-- | Lifted 'C.forkFinally'.
71+forkFinally
72+ :: Concurrent :> es
73+ => Eff es a
74+ -> (Either SomeException a -> Eff es ())
75+ -> Eff es C.ThreadId
76+forkFinally k cleanup = unsafeEff $ \es -> do
77+ esF <- cloneEnv es
78+ C.forkFinally (unEff k esF) ((`unEff` esF) . cleanup)
79+
80+-- | Lifted 'C.forkIOWithUnmask'.
81+forkIOWithUnmask
82+ :: Concurrent :> es
83+ => ((forall a es'. IOE :> es' => Eff es' a -> Eff es' a)
84+ -> Eff es ())
85+ -> Eff es C.ThreadId
86+forkIOWithUnmask f = unsafeEff $ \es -> do
87+ esF <- cloneEnv es
88+ C.forkIOWithUnmask (\restore -> unEff (f (unsafeLiftMapIO restore)) esF)
89+
90+-- | Lifted 'C.killThread'.
91+killThread :: Concurrent :> es => C.ThreadId -> Eff es ()
92+killThread = unsafeEff_ . C.killThread
93+
94+-- | Lifted 'C.throwTo'.
95+throwTo :: (Concurrent :> es, Exception e) => C.ThreadId -> e -> Eff es ()
96+throwTo tid = unsafeEff_ . C.throwTo tid
97+
98+----------------------------------------
99+-- Threads with affinity
100+
101+-- | Lifted 'C.forkOn'.
102+forkOn :: Concurrent :> es => Int -> Eff es () -> Eff es C.ThreadId
103+forkOn n k = unsafeEff $ \es -> do
104+ esF <- cloneEnv es
105+ C.forkOn n (unEff k esF)
106+
107+-- | Lifted 'C.forkOnWithUnmask'.
108+forkOnWithUnmask
109+ :: Concurrent :> es
110+ => Int
111+ -> ((forall a es'. IOE :> es' => Eff es' a -> Eff es' a) -> Eff es ())
112+ -> Eff es C.ThreadId
113+forkOnWithUnmask n f = unsafeEff $ \es -> do
114+ esF <- cloneEnv es
115+ C.forkOnWithUnmask n (\restore -> unEff (f (unsafeLiftMapIO restore)) esF)
116+
117+-- | Lifted 'C.getNumCapabilities'.
118+getNumCapabilities :: Concurrent :> es => Eff es Int
119+getNumCapabilities = unsafeEff_ C.getNumCapabilities
120+
121+-- | Lifted 'C.setNumCapabilities'.
122+setNumCapabilities :: Concurrent :> es => Int -> Eff es ()
123+setNumCapabilities = unsafeEff_ . C.setNumCapabilities
124+
125+-- | Lifted 'C.threadCapability'.
126+threadCapability :: Concurrent :> es => C.ThreadId -> Eff es (Int, Bool)
127+threadCapability = unsafeEff_ . C.threadCapability
128+
129+----------------------------------------
130+-- Scheduling
131+
132+-- | Lifted 'C.yield'.
133+yield :: Concurrent :> es => Eff es ()
134+yield = unsafeEff_ C.yield
135+
136+----------------------------------------
137+-- Waiting
138+
139+-- | Lifted 'C.threadDelay'.
140+threadDelay :: Concurrent :> es => Int -> Eff es ()
141+threadDelay = unsafeEff_ . C.threadDelay
142+
143+-- | Lifted 'C.threadWaitRead'.
144+threadWaitRead :: Concurrent :> es => Fd -> Eff es ()
145+threadWaitRead = unsafeEff_ . C.threadWaitRead
146+
147+-- | Lifted 'C.threadWaitWrite'.
148+threadWaitWrite :: Concurrent :> es => Fd -> Eff es ()
149+threadWaitWrite = unsafeEff_ . C.threadWaitWrite
150+
151+-- | Lifted 'C.threadWaitReadSTM'.
152+threadWaitReadSTM :: Concurrent :> es => Fd -> Eff es (STM (), Eff es ())
153+threadWaitReadSTM fd = unsafeEff_ $ do
154+ second unsafeEff_ <$> C.threadWaitReadSTM fd
155+
156+-- | Lifted 'C.threadWaitWriteSTM'.
157+threadWaitWriteSTM :: Concurrent :> es => Fd -> Eff es (STM (), Eff es ())
158+threadWaitWriteSTM fd = unsafeEff_ $ do
159+ second unsafeEff_ <$> C.threadWaitWriteSTM fd
160+
161+----------------------------------------
162+-- Bound threads
163+
164+-- | Lifted 'C.forkOS'.
165+forkOS :: Concurrent :> es => Eff es () -> Eff es C.ThreadId
166+forkOS k = unsafeEff $ \es -> do
167+ esF <- cloneEnv es
168+ C.forkOS $ unEff k esF
169+
170+-- | Lifted 'E.forkOSWithUnmask'.
171+forkOSWithUnmask
172+ :: Concurrent :> es
173+ => ((forall a es'. IOE :> es' => Eff es' a -> Eff es' a) -> Eff es ())
174+ -> Eff es C.ThreadId
175+forkOSWithUnmask f = unsafeEff $ \es -> do
176+ esF <- cloneEnv es
177+ C.forkOSWithUnmask (\restore -> unEff (f (unsafeLiftMapIO restore)) esF)
178+
179+-- | Lifted 'C.isCurrentThreadBound'.
180+isCurrentThreadBound :: Concurrent :> es => Eff es Bool
181+isCurrentThreadBound = unsafeEff_ C.isCurrentThreadBound
182+
183+-- | Lifted 'C.runInBoundThread'.
184+runInBoundThread :: Concurrent :> es => Eff es a -> Eff es a
185+runInBoundThread k = unsafeEff $ \es -> do
186+ esF <- cloneEnv es
187+ C.runInBoundThread $ unEff k esF
188+
189+-- | Lifted 'C.runInUnboundThread'.
190+runInUnboundThread :: Concurrent :> es => Eff es a -> Eff es a
191+runInUnboundThread k = unsafeEff $ \es -> do
192+ esF <- cloneEnv es
193+ C.runInUnboundThread $ unEff k esF
194+
195+----------------------------------------
196+-- Weak references to ThreadIds
197+
198+-- | Lifted 'C.mkWeakThreadId'.
199+mkWeakThreadId :: Concurrent :> es => C.ThreadId -> Eff es (Weak C.ThreadId)
200+mkWeakThreadId = unsafeEff_ . C.mkWeakThreadId
effectful/src/Effectful/Concurrent/Internal.hsnew file+20
@@ -0,0 +1,20 @@
1+module Effectful.Concurrent.Internal
2+ ( -- * Concurrent effect
3+ Concurrent
4+ , runConcurrent
5+ ) where
6+
7+import Effectful.Internal.Effect
8+import Effectful.Internal.Monad
9+
10+-- | Run 'Eff' operations asynchronously via the @async@ library.
11+--
12+-- /Note:/ thread local state changes in 'Eff' operations run asynchronously
13+-- will not affect the parent thread.
14+--
15+-- /TODO:/ write about 'Concurrent' not respecting scoped operations.
16+data Concurrent :: Effect where
17+ Concurrent :: Concurrent m r
18+
19+runConcurrent :: IOE :> es => Eff (Concurrent : es) a -> Eff es a
20+runConcurrent = evalEffect (IdE Concurrent)
effectful/tests/AsyncTests.hs+6−6
@@ -7,7 +7,7 @@ import Test.Tasty.HUnit
77import qualified Data.Set as S
88
99import Effectful
10−import Effectful.Async
10+import Effectful.Concurrent.Async
1111import Effectful.Error
1212import Effectful.State.Dynamic
1313
@@ -23,7 +23,7 @@ asyncTests = testGroup "Async"
2323 ]
2424
2525test_localState :: Assertion
26−test_localState = runEff . runAsyncE . evalLocalState x $ do
26+test_localState = runEff . runConcurrent . evalLocalState x $ do
2727 replicateConcurrently_ 2 $ do
2828 r <- goDownward 0
2929 U.assertEqual "expected result" x r
@@ -41,7 +41,7 @@ test_localState = runEff . runAsyncE . evalLocalState x $ do
4141 else goDownward $ acc + 1
4242
4343test_sharedState :: Assertion
44−test_sharedState = runEff . runAsyncE . evalSharedState (S.empty @Int) $ do
44+test_sharedState = runEff . runConcurrent . evalSharedState (S.empty @Int) $ do
4545 concurrently_ (addWhen even x) (addWhen odd x)
4646 U.assertEqual "expected result" (S.fromList [1..x]) =<< get
4747 where
@@ -57,7 +57,7 @@ test_sharedState = runEff . runAsyncE . evalSharedState (S.empty @Int) $ do
5757 addWhen f $ n - 1
5858
5959test_errorHandling :: Assertion
60−test_errorHandling = runEff . runAsyncE . evalSharedState (0::Int) $ do
60+test_errorHandling = runEff . runConcurrent . evalSharedState (0::Int) $ do
6161 r <- runError $ concurrently_
6262 (liftIO (threadDelay 10000) >> throwError err)
6363 (modify (+x))
@@ -73,7 +73,7 @@ test_errorHandling = runEff . runAsyncE . evalSharedState (0::Int) $ do
7373 err = "thrown from async"
7474
7575test_asyncWithUnmask :: Assertion
76−test_asyncWithUnmask = runEff . runAsyncE . evalLocalState "initial" $ do
76+test_asyncWithUnmask = runEff . runConcurrent . evalLocalState "initial" $ do
7777 x <- asyncWithUnmask $ \unmask -> do
7878 liftIO $ threadDelay 10000
7979 r1 <- get @String -- 2
@@ -88,7 +88,7 @@ test_asyncWithUnmask = runEff . runAsyncE . evalLocalState "initial" $ do
8888 (inner1, inner2, outer)
8989
9090test_pooledWorkers :: Assertion
91−test_pooledWorkers = runEff . runAsyncE . evalLocalState (0::Int) $ do
91+test_pooledWorkers = runEff . runConcurrent . evalLocalState (0::Int) $ do
9292 x <- pooledForConcurrentlyN threads [1..n] $ \k -> do
9393 r <- get @Int
9494 modify @Int (+1)
effectful/tests/EnvironmentTests.hs+6−5
@@ -18,10 +18,11 @@ environmentTests = testGroup "Environment"
1818
1919test_setAndGet :: Assertion
2020test_setAndGet = runEff . runEnvironment $ do
21− let n = "EFFECTFUL_TEST1"
22− setEnv n "value"
21+ let expected = "value"
22+ n = "EFFECTFUL_TEST1"
23+ setEnv n expected
2324 v <- getEnv n
24− U.assertEqual "environment variable not set" v "value"
25+ U.assertEqual "environment variable not set" expected v
2526
2627test_unsetAndGet :: Assertion
2728test_unsetAndGet = runEff . runEnvironment $ do
@@ -33,10 +34,10 @@ test_withArgs :: Assertion
3334test_withArgs = runEff . runEnvironment $ do
3435 let expected = ["effectful-test"]
3536 args <- withArgs expected getArgs
36− U.assertEqual "correct arguments" args expected
37+ U.assertEqual "correct arguments" expected args
3738
3839test_withProg :: Assertion
3940test_withProg = runEff . runEnvironment $ do
4041 let expected = "effectful-test"
4142 n <- withProgName expected getProgName
42− U.assertEqual "correct program name" n expected
43+ U.assertEqual "correct program name" expected n