effectful-2.5.0.0: An easy to use, performant extensible effects library.
Safe HaskellSafe-Inferred
LanguageHaskell2010

Effectful.Concurrent

Description

Lifted Control.Concurrent.

For functions that spawn threads, the order of preference for their usage is recommended as follows:

1) High level functions from Effectful.Concurrent.Async such as withAsync, concurrently or mapConcurrently.

2) Low level functions from Effectful.Concurrent.Async such as async.

3) Low level functions from Effectful.Concurrent such as forkIO.

Synopsis

Effect

data Concurrent :: Effect Source #

Provide the ability to run Eff computations concurrently in multiple threads and communicate between them.

Warning: unless you stick to high level functions from the withAsync family, the Concurrent effect makes it possible to escape the scope of any scoped effect operation. Consider the following:

>>> import Effectful.Reader.Static qualified as R
>>> printAsk msg = liftIO . putStrLn . (msg ++) . (": " ++) =<< R.ask
>>> :{
  runEff . R.runReader "GLOBAL" . runConcurrent $ do
    a <- R.local (const "LOCAL") $ do
      a <- async $ do
        printAsk "child (first)"
        threadDelay 20000
        printAsk "child (second)"
      threadDelay 10000
      printAsk "parent (inside)"
      pure a
    printAsk "parent (outside)"
    wait a
:}
child (first): LOCAL
parent (inside): LOCAL
parent (outside): GLOBAL
child (second): LOCAL

Note that the asynchronous computation doesn't respect the scope of local, i.e. the child thread still behaves like it's inside the local block, even though the parent thread already got out of it.

This is because the value provided by the Reader effect is thread local, i.e. each thread manages its own version of it. For the Reader it is the only reasonable behavior, it wouldn't be very useful if its "read only" value was affected by calls to local from its parent or child threads.

However, the cut isn't so clear if it comes to effects that provide access to a mutable state. That's why statically dispatched State and Writer effects come in two flavors, local and shared:

>>> import Effectful.State.Static.Local qualified as SL
>>> :{
  runEff . SL.execState "Hi" . runConcurrent $ do
    replicateConcurrently_ 3 $ SL.modify (++ "!")
:}
"Hi"
>>> import Effectful.State.Static.Shared qualified as SS
>>> :{
  runEff . SS.execState "Hi" . runConcurrent $ do
    replicateConcurrently_ 3 $ SS.modify (++ "!")
:}
"Hi!!!"

In the first example state updates made concurrently are not reflected in the parent thread because the value is thread local, but in the second example they are, because the value is shared.

Instances

Instances details
type DispatchOf Concurrent Source # 
Instance details

Defined in Effectful.Concurrent.Effect

data StaticRep Concurrent Source # 
Instance details

Defined in Effectful.Concurrent.Effect

Handlers

runConcurrent :: (HasCallStack, IOE :> es) => Eff (Concurrent : es) a -> Eff es a Source #

Run the Concurrent effect.

Basic concurrency operations

forkIO :: (HasCallStack, Concurrent :> es) => Eff es () -> Eff es ThreadId Source #

Lifted forkIO.

forkIOWithUnmask :: (HasCallStack, Concurrent :> es) => ((forall a. Eff es a -> Eff es a) -> Eff es ()) -> Eff es ThreadId Source #

throwTo :: (Concurrent :> es, Exception e) => ThreadId -> e -> Eff es () Source #

Lifted throwTo.

Threads with affinity

forkOn :: (HasCallStack, Concurrent :> es) => Int -> Eff es () -> Eff es ThreadId Source #

Lifted forkOn.

forkOnWithUnmask :: (HasCallStack, Concurrent :> es) => Int -> ((forall a. Eff es a -> Eff es a) -> Eff es ()) -> Eff es ThreadId Source #

Scheduling

yield :: Concurrent :> es => Eff es () Source #

Lifted yield.

Waiting

threadDelay :: Concurrent :> es => Int -> Eff es () Source #

Lifted threadDelay.

threadWaitReadSTM :: Concurrent :> es => Fd -> Eff es (STM (), Eff es ()) Source #

Bound threads

forkOS :: (HasCallStack, Concurrent :> es) => Eff es () -> Eff es ThreadId Source #

Lifted forkOS.

forkOSWithUnmask :: (HasCallStack, Concurrent :> es) => ((forall a. Eff es a -> Eff es a) -> Eff es ()) -> Eff es ThreadId Source #

Weak references to ThreadIds

Re-exports

rtsSupportsBoundThreads :: Bool #

True if bound threads are supported. If rtsSupportsBoundThreads is False, isCurrentThreadBound will always return False and both forkOS and runInBoundThread will fail.