on master help sign in
Separate monad-control related functionality

Streamly.Internal.Control.Concurrent now has only monad-control related routines which can possibly be replaced by equivalent functionality like unliftio if so desired. This module can be renamed but kept the original name to avoid too many changes. Fork for IO monad is in ForkIO module and fork lifted to any monad is in ForkLifted module. ForkLifted is separated because it is only required in concurrent streams which can be cleanly separated into a separate package along with this module.

commit3cba8fab46e92ca1bde6470ef00d499b8ccfbd94 authorHarendra Kumar <harendra@composewell.com> dateSun 13 Feb 2022, 08:35 (5 years ago) parentc051e84
9 files changed+123−67
src/Streamly/Internal/Control/Concurrent.hs+5−59 src/Streamly/Internal/Control/ForkIO.hs+50 src/Streamly/Internal/Control/ForkLifted.hs+55 src/Streamly/Internal/Data/SVar/Dispatch.hs+2−2 src/Streamly/Internal/Data/Stream/SVar/Eliminate.hs+2−1 src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+2−1 src/Streamly/Internal/Data/Time/Clock.hs+3−3 src/Streamly/Internal/Network/Inet/TCP.hs+2−1 streamly.cabal+2
src/Streamly/Internal/Control/Concurrent.hs+5−59
@@ -1,5 +1,3 @@
1−{-# LANGUAGE UnboxedTuples #-}
2−
31-- |
42-- Module : Streamly.Internal.Control.Concurrent
53-- Copyright : (c) 2017 Composewell Technologies
@@ -7,29 +5,22 @@
75-- Maintainer : streamly@composewell.com
86-- Stability : experimental
97-- Portability : GHC
8+--
9+-- Note: This module is primarily for abstractions related to MonadBaseControl.
10+-- Please do not add any general routines in this. It should be renamed
11+-- appropriately.
1012
1113module Streamly.Internal.Control.Concurrent
1214 (
1315 MonadAsync
1416 , RunInIO(..)
1517 , captureMonadState
16− , doFork
17− , fork
18− , forkManaged
1918 )
2019where
2120
22−import Control.Concurrent (ThreadId, forkIO, killThread)
23−import Control.Exception (SomeException(..), catch, mask)
2421import Control.Monad.Catch (MonadThrow)
2522import Control.Monad.IO.Class (MonadIO(..))
26−import Control.Monad.Trans.Control
27− (MonadBaseControl, control, StM, liftBaseDiscard)
28−import Data.Functor (void)
29−import GHC.Conc (ThreadId(..))
30−import GHC.Exts
31−import GHC.IO (IO(..))
32−import System.Mem.Weak (addFinalizer)
23+import Control.Monad.Trans.Control (MonadBaseControl, control, StM)
3324
3425-- /Since: 0.8.0 ("Streamly.Prelude")/
3526--
@@ -53,48 +44,3 @@ newtype RunInIO m = RunInIO { runInIO :: forall b. m b -> IO (StM m b) }
5344-- execution.
5445captureMonadState :: MonadBaseControl IO m => m (RunInIO m)
5546captureMonadState = control $ \run -> run (return $ RunInIO run)
56−
57−-- Stolen from the async package. The perf improvement is modest, 2% on a
58−-- thread heavy benchmark (parallel composition using noop computations).
59−-- A version of forkIO that does not include the outer exception
60−-- handler: saves a bit of time when we will be installing our own
61−-- exception handler.
62−{-# INLINE rawForkIO #-}
63−rawForkIO :: IO () -> IO ThreadId
64−rawForkIO (IO action) = IO $ \ s ->
65− case fork# action s of (# s1, tid #) -> (# s1, ThreadId tid #)
66−
67−-- | Fork a thread to run the given computation, installing the provided
68−-- exception handler. Lifted to any monad with 'MonadBaseControl IO m'
69−-- capability.
70−--
71−-- TODO: the RunInIO argument can be removed, we can directly pass the action
72−-- as "mrun action" instead.
73−{-# INLINE doFork #-}
74−doFork :: MonadBaseControl IO m
75− => m ()
76− -> RunInIO m
77− -> (SomeException -> IO ())
78− -> m ThreadId
79−doFork action (RunInIO mrun) exHandler =
80− control $ \run ->
81− mask $ \restore -> do
82− tid <- rawForkIO $ catch (restore $ void $ mrun action)
83− exHandler
84− run (return tid)
85−
86−-- | 'fork' lifted to any monad with 'MonadBaseControl IO m' capability.
87−--
88−{-# INLINABLE fork #-}
89−fork :: MonadBaseControl IO m => m () -> m ThreadId
90−fork = liftBaseDiscard forkIO
91−
92−-- | Fork a thread that is automatically killed as soon as the reference to the
93−-- returned threadId is garbage collected.
94−--
95−{-# INLINABLE forkManaged #-}
96−forkManaged :: (MonadIO m, MonadBaseControl IO m) => m () -> m ThreadId
97−forkManaged action = do
98− tid <- fork action
99− liftIO $ addFinalizer tid (killThread tid)
100− return tid
src/Streamly/Internal/Control/ForkIO.hsnew file+50
@@ -0,0 +1,50 @@
1+{-# LANGUAGE UnboxedTuples #-}
2+
3+-- |
4+-- Module : Streamly.Internal.Control.ForkIO
5+-- Copyright : (c) 2017 Composewell Technologies
6+-- License : BSD-3-Clause
7+-- Maintainer : streamly@composewell.com
8+-- Stability : experimental
9+-- Portability : GHC
10+
11+module Streamly.Internal.Control.ForkIO
12+ ( rawForkIO
13+ , forkIOManaged
14+ , forkManagedWith
15+ )
16+where
17+
18+import Control.Concurrent (ThreadId, forkIO, killThread)
19+import Control.Monad.IO.Class (MonadIO(..))
20+import GHC.Conc (ThreadId(..))
21+import GHC.Exts
22+import GHC.IO (IO(..))
23+import System.Mem.Weak (addFinalizer)
24+
25+-- | Stolen from the async package. The perf improvement is modest, 2% on a
26+-- thread heavy benchmark (parallel composition using noop computations).
27+-- A version of forkIO that does not include the outer exception
28+-- handler: saves a bit of time when we will be installing our own
29+-- exception handler.
30+{-# INLINE rawForkIO #-}
31+rawForkIO :: IO () -> IO ThreadId
32+rawForkIO (IO action) = IO $ \ s ->
33+ case fork# action s of (# s1, tid #) -> (# s1, ThreadId tid #)
34+
35+-- | Fork a thread that is automatically killed as soon as the reference to the
36+-- returned threadId is garbage collected.
37+--
38+{-# INLINABLE forkManagedWith #-}
39+forkManagedWith :: MonadIO m => (m () -> m ThreadId) -> m () -> m ThreadId
40+forkManagedWith fork action = do
41+ tid <- fork action
42+ liftIO $ addFinalizer tid (killThread tid)
43+ return tid
44+
45+-- | Fork a thread that is automatically killed as soon as the reference to the
46+-- returned threadId is garbage collected.
47+--
48+{-# INLINABLE forkIOManaged #-}
49+forkIOManaged :: IO () -> IO ThreadId
50+forkIOManaged = forkManagedWith forkIO
src/Streamly/Internal/Control/ForkLifted.hsnew file+55
@@ -0,0 +1,55 @@
1+-- |
2+-- Module : Streamly.Internal.Control.ForkLifted
3+-- Copyright : (c) 2017 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Internal.Control.ForkLifted
10+ (
11+ doFork
12+ , fork
13+ , forkManaged
14+ )
15+where
16+
17+import Control.Concurrent (ThreadId, forkIO)
18+import Control.Exception (SomeException(..), catch, mask)
19+import Control.Monad.IO.Class (MonadIO(..))
20+import Control.Monad.Trans.Control (MonadBaseControl, control, liftBaseDiscard)
21+import Data.Functor (void)
22+import Streamly.Internal.Control.Concurrent (RunInIO(..))
23+import Streamly.Internal.Control.ForkIO (rawForkIO, forkManagedWith)
24+
25+-- | Fork a thread to run the given computation, installing the provided
26+-- exception handler. Lifted to any monad with 'MonadBaseControl IO m'
27+-- capability.
28+--
29+-- TODO: the RunInIO argument can be removed, we can directly pass the action
30+-- as "mrun action" instead.
31+{-# INLINE doFork #-}
32+doFork :: MonadBaseControl IO m
33+ => m ()
34+ -> RunInIO m
35+ -> (SomeException -> IO ())
36+ -> m ThreadId
37+doFork action (RunInIO mrun) exHandler =
38+ control $ \run ->
39+ mask $ \restore -> do
40+ tid <- rawForkIO $ catch (restore $ void $ mrun action)
41+ exHandler
42+ run (return tid)
43+
44+-- | 'fork' lifted to any monad with 'MonadBaseControl IO m' capability.
45+--
46+{-# INLINABLE fork #-}
47+fork :: MonadBaseControl IO m => m () -> m ThreadId
48+fork = liftBaseDiscard forkIO
49+
50+-- | Fork a thread that is automatically killed as soon as the reference to the
51+-- returned threadId is garbage collected.
52+--
53+{-# INLINABLE forkManaged #-}
54+forkManaged :: (MonadIO m, MonadBaseControl IO m) => m () -> m ThreadId
55+forkManaged = forkManagedWith fork
src/Streamly/Internal/Data/SVar/Dispatch.hs+2−2
@@ -49,8 +49,8 @@ import Data.IORef (IORef, modifyIORef, newIORef, readIORef, writeIORef)
4949#if __GLASGOW_HASKELL__ < 804
5050import Data.Semigroup ((<>))
5151#endif
52−import Streamly.Internal.Control.Concurrent
53− (MonadAsync, captureMonadState, doFork)
52+import Streamly.Internal.Control.Concurrent (MonadAsync, captureMonadState)
53+import Streamly.Internal.Control.ForkLifted (doFork)
5454import Streamly.Internal.Data.Atomics
5555 (atomicModifyIORefCAS, atomicModifyIORefCAS_, writeBarrier,
5656 storeLoadBarrier)
src/Streamly/Internal/Data/Stream/SVar/Eliminate.hs+2−1
@@ -31,7 +31,8 @@ import Control.Monad (when, void)
3131import Control.Monad.Catch (throwM)
3232import Control.Monad.IO.Class (MonadIO(liftIO))
3333import Data.IORef (newIORef, readIORef, writeIORef)
34−import Streamly.Internal.Control.Concurrent (MonadAsync, doFork)
34+import Streamly.Internal.Control.Concurrent (MonadAsync)
35+import Streamly.Internal.Control.ForkLifted (doFork)
3536import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
3637import Streamly.Internal.Data.Fold.SVar (write, writeLimited)
3738import Streamly.Internal.Data.Fold.Type (Fold(..))
src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+2−1
@@ -133,7 +133,8 @@ import Data.Maybe (fromJust, isJust)
133133import GHC.Types (SPEC(..))
134134import qualified Control.Monad.Catch as MC
135135
136−import Streamly.Internal.Control.Concurrent (MonadAsync, fork, forkManaged)
136+import Streamly.Internal.Control.Concurrent (MonadAsync)
137+import Streamly.Internal.Control.ForkLifted (fork, forkManaged)
137138import Streamly.Internal.Data.Fold.Type (Fold(..))
138139import Streamly.Internal.Data.Pipe.Type (Pipe(..), PipeState(..))
139140import Streamly.Internal.Data.SVar.Type (defState, adaptState)
src/Streamly/Internal/Data/Time/Clock.hs+3−3
@@ -33,7 +33,7 @@ import Control.Monad (forever, when, void)
3333import Streamly.Internal.Data.Time.Clock.Type (Clock(..), getTime)
3434import Streamly.Internal.Data.Time.Units
3535 (MicroSecond64(..), fromAbsTime, addToAbsTime, toRelTime)
36−import Streamly.Internal.Control.Concurrent (forkManaged)
36+import Streamly.Internal.Control.ForkIO (forkIOManaged)
3737
3838import qualified Streamly.Internal.Data.IORef.Prim as Prim
3939
@@ -82,7 +82,7 @@ asyncClock :: Clock -> Double -> IO (ThreadId, Prim.IORef MicroSecond64)
8282asyncClock clock g = do
8383 timeVar <- Prim.newIORef 0
8484 updateTimeVar clock timeVar
85− tid <- forkManaged $ forever (updateWithDelay clock g timeVar)
85+ tid <- forkIOManaged $ forever (updateWithDelay clock g timeVar)
8686 return (tid, timeVar)
8787
8888{-# INLINE readClock #-}
@@ -145,7 +145,7 @@ timer clock g period = do
145145 reset = resetTimerExpiry clock p1 timeVar
146146 process = processTimerTick clock g timeVar mvar reset
147147 reset
148− tid <- forkManaged $ forever process
148+ tid <- forkIOManaged $ forever process
149149 return $ Timer tid mvar reset
150150
151151-- | Blocking wait for a timer tick.
src/Streamly/Internal/Network/Inet/TCP.hs+2−1
@@ -103,7 +103,8 @@ import Network.Socket
103103 socket)
104104import Prelude hiding (read)
105105
106−import Streamly.Internal.Control.Concurrent (MonadAsync, fork)
106+import Streamly.Internal.Control.Concurrent (MonadAsync)
107+import Streamly.Internal.Control.ForkLifted (fork)
107108import Streamly.Internal.Data.Array.Foreign.Type (Array(..), writeNUnsafe)
108109import Streamly.Internal.Data.Fold.Type (Fold(..))
109110import Streamly.Internal.Data.Stream.IsStream.Type (IsStream)
streamly.cabal+2
@@ -384,6 +384,8 @@ library
384384 , Streamly.Internal.BaseCompat
385385 , Streamly.Internal.Control.Exception
386386 , Streamly.Internal.Control.Monad
387+ , Streamly.Internal.Control.ForkIO
388+ , Streamly.Internal.Control.ForkLifted
387389 , Streamly.Internal.Control.Concurrent
388390 , Streamly.Internal.Data.Cont
389391 , Streamly.Internal.Data.Tuple.Strict