2-- Module : Streamly.Internal.Data.Fold.Concurrent.Channel
3-- Copyright : (c) 2022 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9module Streamly.Internal.Data.Fold.Concurrent.Channel
11 module Streamly.Internal.Data.Fold.Concurrent.Channel.Type
23import Control.Concurrent (takeMVar)
24import Control.Monad (void, when)
25import Control.Monad.IO.Class (MonadIO(liftIO))
26import Data.IORef (writeIORef)
27import Streamly.Internal.Control.Concurrent (MonadAsync)
28import Streamly.Internal.Data.Fold (Fold(..), Step (..))
29import Streamly.Internal.Data.Channel.Worker (sendWithDoorBell)
30import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
32import Streamly.Internal.Data.Fold.Concurrent.Channel.Type
33import Streamly.Internal.Data.Channel.Types
35-------------------------------------------------------------------------------
37-------------------------------------------------------------------------------
39-- XXX Cleanup the fold if the stream is interrupted. Add a GC hook.
41-- | Evaluate a fold asynchronously using a concurrent channel. The driver just
42-- queues the input stream values to the fold channel buffer and returns. The
43-- fold evaluates the queued values asynchronously. On finalization, 'parEval'
44-- waits for the asynchronous fold to complete before it returns.
46{-# INLINABLE parEval #-}
47parEval :: MonadAsync m => (Config -> Config) -> Fold m a b -> Fold m a b
49 Fold step initial extract final
53 initial = Partial <$> newChannel modifier f
55 -- XXX This is not truly asynchronous. If the fold is done we only get to
56 -- know when we send the next input unless the stream ends. We could
57 -- potentially throw an async exception to the driver to inform it
58 -- asynchronously. Alternatively, the stream should not block forever, it
59 -- should keep polling the fold status. We can insert a timer tick in the
60 -- input stream to do that.
62 -- A polled stream abstraction may be useful, it would consist of normal
63 -- events and tick events, latter are guaranteed to arrive.
65 -- XXX We can use the config to indicate if the fold is a scanning type or
66 -- one-shot, or use a separate parEvalScan for scanning. For a scanning
67 -- type fold the worker would always send the intermediate values back to
68 -- the driver. An intermediate value can be returned on an input, or the
69 -- driver can poll even without input, if we have the Skip input support.
70 -- When the buffer is full we can return "Skip" and then the next step
71 -- without input can wait for an output to arrive. Similarly, when "final"
72 -- is called it can return "Skip" to continue or "Done" to indicate
75 status <- sendToWorker chan a
76 return $ case status of
77 Nothing -> Partial chan
80 -- XXX We can use a separate type for non-scanning folds that will
81 -- introduce a lot of complexity. Are there combinators that rely on the
82 -- "extract" function even in non-scanning use cases?
83 -- Instead of making such folds partial we can also make them return a
85 extract _ = error "Concurrent folds do not support scanning"
87 -- XXX depending on the use case we may want to either wait for the result
88 -- or cancel the ongoing work. We can use the config to control that?
89 -- Currently it waits for the work to complete.
96 status <- checkFoldStatus chan
101 (svarInspectMode chan)
103 "parEval: waiting to drain"
104 $ takeMVar (outputDoorBellFromConsumer chan)
105 -- XXX remove recursion
108 when (svarInspectMode chan) $ liftIO $ do
109 t <- getTime Monotonic
110 writeIORef (svarStopTime (svarStats chan)) (Just t)
111 printSVar (dumpSVar chan) "SVar Done"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list