@@ -98,15 +98,18 @@ module Streamly.Internal.Data.Stream.Concurrent
99 -- ** Reactive
99 -- ** Reactive
100 , fromCallback
100 , fromCallback
104#include "inline.hs"
106#include "inline.hs"
106−import Control.Concurrent (myThreadId)
108+import Control.Concurrent (myThreadId, killThread)
107−import Control.Monad (void)
109+import Control.Monad (void, when)
108import Control.Monad.IO.Class (MonadIO(liftIO))
110import Control.Monad.IO.Class (MonadIO(liftIO))
109import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
111import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
112+import Streamly.Internal.Control.ForkLifted (forkManaged)
110import Streamly.Internal.Data.Stream.Channel.Dispatcher (modifyThread)
113import Streamly.Internal.Data.Stream.Channel.Dispatcher (modifyThread)
111import Streamly.Internal.Data.Stream.Channel.Types
114import Streamly.Internal.Data.Stream.Channel.Types
112 ( ChildEvent(..)
115 ( ChildEvent(..)
@@ -114,8 +117,11 @@ import Streamly.Internal.Data.Stream.Channel.Types
115import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
118import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
116import Streamly.Internal.Data.Stream.Type (Stream)
119import Streamly.Internal.Data.Stream.Type (Stream)
120+import Streamly.Internal.Data.Stream.StreamD (Step(..))
122+import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed
118import qualified Streamly.Internal.Data.Stream as Stream
123import qualified Streamly.Internal.Data.Stream as Stream
124+import qualified Streamly.Internal.Data.Stream.StreamD as D
119import qualified Streamly.Internal.Data.Stream.StreamK as K
125import qualified Streamly.Internal.Data.Stream.StreamK as K
120import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
126import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
@@ -695,3 +701,65 @@ fromCallback setCallback = Stream.concatM $ do
695 (callback, stream) <- newCallbackStream
701 (callback, stream) <- newCallbackStream
696 setCallback callback
702 setCallback callback
697 return stream
703 return stream
705+{-# INLINE_NORMAL pollCountsD #-}
709+ -> (D.Stream m Int -> m b)
712+pollCountsD predicate fld (D.Stream step state) = D.Stream step' Nothing
715+ {-# INLINE_LATE step' #-}
716+ step' _ Nothing = do
717+ -- As long as we are using an "Int" for counts lockfree reads from
718+ -- Var should work correctly on both 32-bit and 64-bit machines.
719+ -- However, an Int on a 32-bit machine may overflow quickly.
720+ countVar <- liftIO $ Unboxed.newIORef (0 :: Int)
723+ $ Unboxed.toStreamD countVar
724+ return $ Skip (Just (countVar, tid, state))
726+ step' gst (Just (countVar, tid, st)) = do
731+ $ liftIO $ Unboxed.modifyIORef' countVar (+ 1)
732+ return $ Yield x (Just (countVar, tid, s))
733+ Skip s -> return $ Skip (Just (countVar, tid, s))
735+ liftIO $ killThread tid
738+-- | @pollCounts predicate transform fold stream@ counts those elements in the
739+-- stream that pass the @predicate@. The resulting count stream is sent to
740+-- another thread which transforms it using @transform@ and then folds it using
741+-- @fold@. The thread is automatically cleaned up if the stream stops or
742+-- aborts due to exception.
744+-- For example, to print the count of elements processed every second:
747+-- > Stream.drain $ Concur.pollCounts (const True) (Stream.rollingMap (-) . Stream.delayPost 1) (Fold.drainBy print)
748+-- $ Stream.enumerateFrom 0
751+-- Note: This may not work correctly on 32-bit machines.
755+{-# INLINE pollCounts #-}
759+ -> (Stream m Int -> m b)
762+pollCounts predicate f xs =
764+ $ pollCountsD predicate (f . Stream.fromStreamD)
765+ $ Stream.toStreamD xs