@@ -1,3 +1,4 @@
1{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE ConstraintKinds #-}
2{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleContexts #-}
3{-# LANGUAGE FlexibleInstances #-}
4{-# LANGUAGE FlexibleInstances #-}
@@ -25,13 +26,19 @@ module Streamly.Core
25 -- * Streams
26 -- * Streams
26 , Stream (..)
27 , Stream (..)
28− -- * Construction
29+ -- * Construction (pure)
29 , singleton
32 , singleton
36+ -- * Construction (monadic)
36 -- * Semigroup Style Composition
43 -- * Semigroup Style Composition
@@ -41,13 +48,11 @@ module Streamly.Core
42 , parallel
49 , parallel
49 , zipAsyncWith
53 , zipAsyncWith
55+ -- XXX move to Streams.hs
51 -- * Transformers
56 -- * Transformers
52 , withLocal
57 , withLocal
53 , withCatchError
58 , withCatchError
@@ -55,18 +60,24 @@ module Streamly.Core
55 -- * Concurrent Stream Vars (SVars)
60 -- * Concurrent Stream Vars (SVars)
57 , SVarStyle (..)
62 , SVarStyle (..)
59 , newStreamVar1
63 , newStreamVar1
61 , fromStreamVar
64 , fromStreamVar
62 , toStreamVar
65 , toStreamVar
69+-- MVar diagnostics has some overhead - around 5% on asyncly null benchmark,
70+-- but keep it on for now to debug problems quickly if and when they happen.
66import Control.Concurrent (ThreadId, myThreadId, threadDelay)
73import Control.Concurrent (ThreadId, myThreadId, threadDelay)
67import Control.Concurrent.MVar (MVar, newEmptyMVar, tryTakeMVar,
74import Control.Concurrent.MVar (MVar, newEmptyMVar, tryTakeMVar,
68 tryPutMVar, takeMVar, putMVar)
75 tryPutMVar, takeMVar, putMVar)
69import Control.Exception (SomeException (..))
76import Control.Exception (SomeException (..))
78+import Control.Exception (catch,
79+ BlockedIndefinitelyOnMVar(..))
70import qualified Control.Exception.Lifted as EL
81import qualified Control.Exception.Lifted as EL
71import Control.Monad (when)
82import Control.Monad (when)
72import Control.Monad.Catch (MonadThrow, throwM)
83import Control.Monad.Catch (MonadThrow, throwM)
@@ -120,7 +131,7 @@ data SVarStyle =
120 | WAsyncVar -- breadth first concurrent
131 | WAsyncVar -- breadth first concurrent
121 | ParallelVar -- all parallel
132 | ParallelVar -- all parallel
122 | AheadVar -- Concurrent look ahead
133 | AheadVar -- Concurrent look ahead
123− deriving Eq
134+ deriving (Eq, Show)
125-- | An SVar or a Stream Var is a conduit to the output from multiple streams
136-- | An SVar or a Stream Var is a conduit to the output from multiple streams
126-- running concurrently and asynchronously. An SVar can be thought of as an
137-- running concurrently and asynchronously. An SVar can be thought of as an
@@ -170,6 +181,7 @@ data SVar m a =
170 , workQueue :: IORef ([Stream m a], Int)
181 , workQueue :: IORef ([Stream m a], Int)
171 , enqueue :: Stream m a -> IO ()
182 , enqueue :: Stream m a -> IO ()
172 , queueEmpty :: m Bool
183 , queueEmpty :: m Bool
184+ , waitingForWork :: IORef Bool
173 , runqueue :: m ()
185 , runqueue :: m ()
175 -- Shared, thread tracking
187 -- Shared, thread tracking
@@ -177,6 +189,50 @@ data SVar m a =
177 , activeWorkers :: IORef Int
189 , activeWorkers :: IORef Int
193+{-# NOINLINE dumpSVar #-}
194+dumpSVar :: SVar m a -> IO String
196+ (oqList, oqLen) <- readIORef $ outputQueue sv
197+ db <- tryTakeMVar $ doorBell sv
198+ srn <- tryTakeMVar $ siren sv
199+ (oheap, oheapSize) <- readIORef $ outputHeap sv
200+ hsrn <- tryTakeMVar $ heapSiren sv
201+ (wq, wqSize) <- readIORef $ workQueue sv
202+ waiting <- readIORef $ waitingForWork sv
203+ rthread <- readIORef $ runningThreads sv
204+ workers <- readIORef $ activeWorkers sv
205+ -- XXX queueEmpty should be made IO return type
208+ [ "style = " ++ show (svarStyle sv)
209+ , "outputQueue length computed = " ++ show (length oqList)
210+ , "outputQueue length maintained = " ++ show oqLen
211+ , "output doorBell = " ++ show db
212+ , "worker siren = " ++ show srn
213+ , "heap length computed = " ++ show (H.size oheap)
214+ , "heap length maintained = " ++ show oheapSize
215+ , "heap siren = " ++ show hsrn
216+ , "work queue length computed = " ++ show (length wq)
217+ , "work queue length maintained = " ++ show wqSize
218+ , "waitingForWork = " ++ show waiting
219+ , "running threads = " ++ show rthread
220+ , "running thread count = " ++ show workers
223+{-# NOINLINE mvarExcHandler #-}
224+mvarExcHandler :: SVar m a -> String -> BlockedIndefinitelyOnMVar -> IO ()
225+mvarExcHandler sv label BlockedIndefinitelyOnMVar = do
226+ svInfo <- dumpSVar sv
227+ error $ label ++ " " ++ "BlockedIndefinitelyOnMVar\n" ++ svInfo
229+withDBGMVar :: SVar m a -> String -> IO () -> IO ()
230+withDBGMVar sv label action = action `catch` mvarExcHandler sv label
232+withDBGMVar :: SVar m a -> String -> IO () -> IO ()
233+withDBGMVar _ _ action = action
180-- Slightly faster version of CAS. Gained some improvement by avoiding the use
236-- Slightly faster version of CAS. Gained some improvement by avoiding the use
181-- of "evaluate" because we know we do not have exceptions in fn.
237-- of "evaluate" because we know we do not have exceptions in fn.
182{-# INLINE atomicModifyIORefCAS #-}
238{-# INLINE atomicModifyIORefCAS #-}
@@ -350,7 +406,7 @@ send sv msg = do
350 -- we will be woken up by the consumer blowing the siren.
406 -- we will be woken up by the consumer blowing the siren.
351 -- Note: current length after adding our output is len + 1
407 -- Note: current length after adding our output is len + 1
352 else when (len + 1 >= 1500) $ do
408 else when (len + 1 >= 1500) $ do
353− takeMVar (siren sv)
409+ withDBGMVar sv "send: wait for siren" $ takeMVar (siren sv)
354 (_, n) <- readIORef (outputQueue sv)
410 (_, n) <- readIORef (outputQueue sv)
355 when (n <= 1500) $ void $ tryPutMVar (siren sv) ()
411 when (n <= 1500) $ void $ tryPutMVar (siren sv) ()
@@ -367,8 +423,22 @@ sendStop sv = myThreadId >>= \tid -> send sv (ChildStop tid Nothing)
367-- expressions. Large left associated compositions can grow this to a
423-- expressions. Large left associated compositions can grow this to a
368-- large size
424-- large size
369{-# INLINE enqueueLIFO #-}
425{-# INLINE enqueueLIFO #-}
370−enqueueLIFO :: IORef [Stream m a] -> Stream m a -> IO ()
426+enqueueLIFO :: SVar m a -> IORef [Stream m a] -> Stream m a -> IO ()
427+enqueueLIFO sv q m = do
428+ w <- readIORef $ waitingForWork sv
371−enqueueLIFO q m = atomicModifyIORefCAS_ q $ \ ms -> m : ms
431+ else atomicModifyIORefCAS_ q $ \ms -> m : ms
436+ v <- atomicModifyIORefCAS q $ \ ms ->
438+ [] -> (m : ms, True)
439+ _ -> (m : ms, False)
440+ when v $ void $ tryPutMVar (doorBell sv) ()
441+ atomicModifyIORefCAS_ (waitingForWork sv) (const False)
373runqueueLIFO :: MonadIO m => SVar m a -> IORef [Stream m a] -> m ()
443runqueueLIFO :: MonadIO m => SVar m a -> IORef [Stream m a] -> m ()
374runqueueLIFO sv q = run
444runqueueLIFO sv q = run
@@ -394,8 +464,19 @@ runqueueLIFO sv q = run
394-------------------------------------------------------------------------------
464-------------------------------------------------------------------------------
396{-# INLINE enqueueFIFO #-}
466{-# INLINE enqueueFIFO #-}
397−enqueueFIFO :: LinkedQueue (Stream m a) -> Stream m a -> IO ()
467+enqueueFIFO :: SVar m a -> LinkedQueue (Stream m a) -> Stream m a -> IO ()
398−enqueueFIFO = pushL
468+enqueueFIFO sv q m = do
469+ w <- readIORef $ waitingForWork sv
479+ when emp $ void $ tryPutMVar (doorBell sv) ()
400runqueueFIFO :: MonadIO m => SVar m a -> LinkedQueue (Stream m a) -> m ()
481runqueueFIFO :: MonadIO m => SVar m a -> LinkedQueue (Stream m a) -> m ()
401runqueueFIFO sv q = run
482runqueueFIFO sv q = run
@@ -411,7 +492,7 @@ runqueueFIFO sv q = run
411 dequeue = liftIO $ tryPopR q
492 dequeue = liftIO $ tryPopR q
412 sendit a = liftIO $ send sv (ChildYield a)
493 sendit a = liftIO $ send sv (ChildYield a)
413 single a = sendit a >> run
494 single a = sendit a >> run
414− yield a r = sendit a >> liftIO (enqueueFIFO q r) >> run
495+ yield a r = sendit a >> liftIO (enqueueFIFO sv q r) >> run
416-------------------------------------------------------------------------------
497-------------------------------------------------------------------------------
417-- Parallel
498-- Parallel
@@ -499,7 +580,8 @@ sendToHeap sv msg seqNo = do
501 when (len + 1 >= maxHeapSize) $ do
582 when (len + 1 >= maxHeapSize) $ do
583+ withDBGMVar sv "sendToHeap: wait for heapSiren"
502− takeMVar (heapSiren sv)
584+ $ takeMVar (heapSiren sv)
503 (h, _) <- readIORef (outputHeap sv)
585 (h, _) <- readIORef (outputHeap sv)
504 when (H.size h <= maxHeapSize) $ void $ tryPutMVar (siren sv) ()
586 when (H.size h <= maxHeapSize) $ void $ tryPutMVar (siren sv) ()
505 return False
587 return False
@@ -508,7 +590,7 @@ sendToHeap sv msg seqNo = do
508 AheadYieldSingle a -> send sv (ChildYield a)
590 AheadYieldSingle a -> send sv (ChildYield a)
509 AheadYieldMany a mv -> do
591 AheadYieldMany a mv -> do
510 send sv (ChildYield a)
592 send sv (ChildYield a)
511− putMVar mv ()
593+ withDBGMVar sv "sendToHeap: wakeup stream" $ putMVar mv ()
512 return True
594 return True
514popFromHeap
596popFromHeap
@@ -547,7 +629,7 @@ drainHeap sv nextSeqNo = do
547 drainHeap sv nextSeqNo
629 drainHeap sv nextSeqNo
548 AheadYieldMany a mv -> do
630 AheadYieldMany a mv -> do
549 send sv (ChildYield a)
631 send sv (ChildYield a)
550− putMVar mv ()
632+ withDBGMVar sv "drainHeap: wakeup stream" $ putMVar mv ()
552-- | The thread that has the token has the responsibility to move any pending
634-- | The thread that has the token has the responsibility to move any pending
553-- entries in the heap to the outputQueue. When the token is assigned to a
635-- entries in the heap to the outputQueue. When the token is assigned to a
@@ -585,11 +667,15 @@ handleOutput sv haveToken prevSeqNo seqNo a ev = do
585-- associated expression, we queue the right expression and execute the left.
667-- associated expression, we queue the right expression and execute the left.
586-- Thererefore the queue never has more than on item in it.
668-- Thererefore the queue never has more than on item in it.
587{-# INLINE enqueueAhead #-}
669{-# INLINE enqueueAhead #-}
588−enqueueAhead :: IORef ([Stream m a], Int) -> Stream m a -> IO ()
670+enqueueAhead :: SVar m a -> IORef ([Stream m a], Int) -> Stream m a -> IO ()
589−enqueueAhead q m = do
671+enqueueAhead sv q m = do
672+ w <- readIORef $ waitingForWork sv
590 atomicModifyIORefCAS_ q $ \ case
673 atomicModifyIORefCAS_ q $ \ case
591 ([], n) -> ([m], n + 1) -- increment sequence
674 ([], n) -> ([m], n + 1) -- increment sequence
592 _ -> error "not empty"
675 _ -> error "not empty"
677+ void $ tryPutMVar (doorBell sv) ()
678+ atomicModifyIORefCAS_ (waitingForWork sv) (const False)
594runqueueAhead :: MonadIO m => SVar m a -> IORef ([Stream m a], Int) -> m ()
680runqueueAhead :: MonadIO m => SVar m a -> IORef ([Stream m a], Int) -> m ()
595runqueueAhead sv q = run False (-1)
681runqueueAhead sv q = run False (-1)
@@ -637,7 +723,9 @@ runqueueAhead sv q = run False (-1)
637 gotToken <-
723 gotToken <-
638 handleOutput sv tStatus prevSeqNo seqNo a
724 handleOutput sv tStatus prevSeqNo seqNo a
639 (AheadYieldMany a mv)
725 (AheadYieldMany a mv)
640− when (not gotToken) $ void $ takeMVar mv
726+ when (not gotToken) $ void $
727+ withDBGMVar sv "runqueueAhead: stream waiting"
641 (runStream r) (Just sv) stopNext singleNext yieldNext
729 (runStream r) (Just sv) stopNext singleNext yieldNext
643 (runStream m) (Just sv) stop single yield
731 (runStream m) (Just sv) stop single yield
@@ -725,32 +813,76 @@ sendWorkerWait sv = do
725 -- If there is no output pending to process and there is no worker to be
813 -- If there is no output pending to process and there is no worker to be
726 -- sent then we block, so that we do not keep looping fruitlessly.
814 -- sent then we block, so that we do not keep looping fruitlessly.
816+ -- Note that we are guaranteed to have at least one outstanding worker when
817+ -- we enter this function. So if we sleep we are guaranteed to be woken up.
818+ -- Note that the worker count is only decremented during event processing
819+ -- in fromStreamVar and therefore it is safe to read and use it without a
728 -- XXX we should wait in such a way so that we can be immediately
822 -- XXX we should wait in such a way so that we can be immediately
729− -- interrupted by a doorBell.
823+ -- interrupted by a doorBell. We can sleep on the doorbell and use a
824+ -- monitor thread to send us a doorbell.
730 liftIO $ threadDelay 200
825 liftIO $ threadDelay 200
731 (_, n) <- liftIO $ readIORef (outputQueue sv)
826 (_, n) <- liftIO $ readIORef (outputQueue sv)
732 when (n <= 0) $ do
827 when (n <= 0) $ do
733− -- XXX the queue may be empty temporarily if the worker has dequeued
828+ -- The queue may be empty temporarily if the worker has dequeued the
734− -- the work item but has not enqueued the remaining part yet. For the
829+ -- work item but has not enqueued the remaining part yet. For the same
735− -- same reason, a worker may come back if it tries to dequeue when the
830+ -- reason, a worker may come back if it tries to dequeue and finds the
736− -- queue is empty, even though the whole work has not finished yet.
831+ -- queue empty, even though the whole work has not finished yet.
737 done <- queueEmpty sv
832 done <- queueEmpty sv
738 if not done
833 if not done
740 cnt <- liftIO $ readIORef $ activeWorkers sv
835 cnt <- liftIO $ readIORef $ activeWorkers sv
836+ -- Note that we may deadlock if the previous workers (tasks in the
837+ -- stream) wait/depend on the future workers (tasks in the stream)
838+ -- executing. In that case we should either configure the maxWorker
839+ -- count to higher or use parallel style instead of ahead or async
741 if (cnt < 1500)
841 if (cnt < 1500)
743 pushWorker sv
843 pushWorker sv
744 sendWorkerWait sv
844 sendWorkerWait sv
845+ else liftIO $ withDBGMVar sv "sendWorkerWait: wait for workers"
745− else liftIO $ takeMVar (doorBell sv)
846+ $ takeMVar (doorBell sv)
848+ -- We found that the queue was empty, but it may be empty
849+ -- temporarily, when we checked it. If that's the case we might
850+ -- sleep indefinitely unless the active workers produce some
851+ -- output. We may deadlock specially if the otuput from the other
852+ -- workers depends on the future workers that we may never send.
853+ -- So in case the queue was temporarily empty set a flag to inform
854+ -- the enqueue to send us a doorbell.
856+ liftIO $ atomicModifyIORefCAS_ (waitingForWork sv) $ const True
858+ -- check again, this time we have set the waitingForWork flag so we
859+ -- are guaranteed to get a doorbell in case the status changed
860+ -- before we could sleep.
862+ -- Note that this is just a best effort mechanism to avoid a
863+ -- deadlock. Deadlocks may still happen if for some weird reason
864+ -- the consuming computation shares an MVar or some other resource
865+ -- with the producing computation and gets blocked on that resource
866+ -- and therefore cannot do any pushworker to add more threads to
867+ -- the producer. In such cases the programmer should use a parallel
868+ -- style so that all the producers are scheduled immediately and
869+ -- unconditionally. We can also use a separate monitor thread to
870+ -- push workers instead of pushing them from the consumer, but then
871+ -- we are no longer using pull based concurrency rate adaptation.
873+ -- XXX update this in the tutorial.
874+ emp <- queueEmpty sv
876+ liftIO $ withDBGMVar sv "sendWorkerWait: nothing to do"
746− else liftIO $ takeMVar (doorBell sv)
877+ $ takeMVar (doorBell sv)
748-- | Pull a stream from an SVar.
879-- | Pull a stream from an SVar.
749{-# NOINLINE fromStreamVar #-}
880{-# NOINLINE fromStreamVar #-}
750fromStreamVar :: MonadAsync m => SVar m a -> Stream m a
881fromStreamVar :: MonadAsync m => SVar m a -> Stream m a
751fromStreamVar sv = Stream $ \_ stp sng yld -> do
882fromStreamVar sv = Stream $ \_ stp sng yld -> do
752 if svarStyle sv == ParallelVar
883 if svarStyle sv == ParallelVar
884+ then liftIO $ withDBGMVar sv "fromStreamVar: doorbell"
753− then liftIO $ takeMVar (doorBell sv)
885+ $ takeMVar (doorBell sv)
755 res <- liftIO $ tryTakeMVar (doorBell sv)
887 res <- liftIO $ tryTakeMVar (doorBell sv)
756 when (isNothing res) $ sendWorkerWait sv
888 when (isNothing res) $ sendWorkerWait sv
@@ -798,6 +930,7 @@ getFifoSVar ctype = do
798 outQMv <- newEmptyMVar
930 outQMv <- newEmptyMVar
799 hooter <- newEmptyMVar
931 hooter <- newEmptyMVar
800 active <- newIORef 0
932 active <- newIORef 0
933+ wfw <- newIORef False
801 running <- newIORef S.empty
934 running <- newIORef S.empty
802 q <- newQ
935 q <- newQ
@@ -809,8 +942,9 @@ getFifoSVar ctype = do
809 , runningThreads = running
942 , runningThreads = running
810 , workQueue = undefined
943 , workQueue = undefined
811 , runqueue = runqueueFIFO sv q
944 , runqueue = runqueueFIFO sv q
812− , enqueue = pushL q
945+ , enqueue = enqueueFIFO sv q
813 , queueEmpty = liftIO $ nullQ q
946 , queueEmpty = liftIO $ nullQ q
947+ , waitingForWork = wfw
814 , svarStyle = ctype
948 , svarStyle = ctype
815 , activeWorkers = active
949 , activeWorkers = active
@@ -822,6 +956,7 @@ getLifoSVar ctype = do
822 outQMv <- newEmptyMVar
956 outQMv <- newEmptyMVar
823 hooter <- newEmptyMVar
957 hooter <- newEmptyMVar
824 active <- newIORef 0
958 active <- newIORef 0
959+ wfw <- newIORef False
825 running <- newIORef S.empty
960 running <- newIORef S.empty
826 q <- newIORef []
961 q <- newIORef []
827 let checkEmpty = null <$> liftIO (readIORef q)
962 let checkEmpty = null <$> liftIO (readIORef q)
@@ -834,8 +969,9 @@ getLifoSVar ctype = do
834 , runningThreads = running
969 , runningThreads = running
835 , workQueue = undefined
970 , workQueue = undefined
836 , runqueue = runqueueLIFO sv q
971 , runqueue = runqueueLIFO sv q
837− , enqueue = enqueueLIFO q
972+ , enqueue = enqueueLIFO sv q
838 , queueEmpty = checkEmpty
973 , queueEmpty = checkEmpty
974+ , waitingForWork = wfw
839 , svarStyle = ctype
975 , svarStyle = ctype
840 , activeWorkers = active
976 , activeWorkers = active
@@ -847,6 +983,7 @@ getParSVar style = do
847 outQMv <- newEmptyMVar
983 outQMv <- newEmptyMVar
848 hooter <- newEmptyMVar
984 hooter <- newEmptyMVar
849 active <- newIORef 0
985 active <- newIORef 0
986+ wfw <- newIORef False
850 running <- newIORef S.empty
987 running <- newIORef S.empty
852 SVar { outputQueue = outQ
989 SVar { outputQueue = outQ
@@ -859,6 +996,7 @@ getParSVar style = do
859 , runqueue = undefined
996 , runqueue = undefined
860 , enqueue = undefined
997 , enqueue = undefined
861 , queueEmpty = undefined
998 , queueEmpty = undefined
999+ , waitingForWork = wfw
862 , svarStyle = style
1000 , svarStyle = style
863 , activeWorkers = active
1001 , activeWorkers = active
@@ -872,6 +1010,7 @@ getAheadSVar style = do
872 hooter <- newEmptyMVar
1010 hooter <- newEmptyMVar
873 hsiren <- newEmptyMVar
1011 hsiren <- newEmptyMVar
874 active <- newIORef 0
1012 active <- newIORef 0
1013+ wfw <- newIORef False
875 running <- newIORef S.empty
1014 running <- newIORef S.empty
876 q <- newIORef ([], -1)
1015 q <- newIORef ([], -1)
877 let checkEmpty = liftIO $ do
1016 let checkEmpty = liftIO $ do
@@ -888,6 +1027,7 @@ getAheadSVar style = do
888 , runqueue = runqueueAhead sv q
1027 , runqueue = runqueueAhead sv q
889 , enqueue = undefined
1028 , enqueue = undefined
890 , queueEmpty = checkEmpty
1029 , queueEmpty = checkEmpty
1030+ , waitingForWork = wfw
891 , svarStyle = style
1031 , svarStyle = style
892 , activeWorkers = active
1032 , activeWorkers = active
@@ -921,7 +1061,7 @@ newStreamVarAhead m = do
921 -- Note: We must have all the work on the queue before sending the
1061 -- Note: We must have all the work on the queue before sending the
922 -- pushworker, otherwise the pushworker may exit before we even get a
1062 -- pushworker, otherwise the pushworker may exit before we even get a
923 -- chance to push.
1063 -- chance to push.
924− liftIO $ enqueueAhead (workQueue sv) m
1064+ liftIO $ enqueueAhead sv (workQueue sv) m
925 pushWorker sv
1065 pushWorker sv
926 return sv
1066 return sv
@@ -1033,7 +1173,7 @@ ahead :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1033ahead m1 m2 = Stream $ \svr stp sng yld -> do
1173ahead m1 m2 = Stream $ \svr stp sng yld -> do
1034 case svr of
1174 case svr of
1035 Just sv | svarStyle sv == AheadVar -> do
1175 Just sv | svarStyle sv == AheadVar -> do
1036− liftIO $ enqueueAhead (workQueue sv) m2
1176+ liftIO $ enqueueAhead sv (workQueue sv) m2
1037 -- Always run the left side on a new SVar to avoid complexity in
1177 -- Always run the left side on a new SVar to avoid complexity in
1038 -- sequencing results. This means the left side cannot further
1178 -- sequencing results. This means the left side cannot further
1039 -- split into more ahead computations on the same SVar.
1179 -- split into more ahead computations on the same SVar.
@@ -1043,9 +1183,15 @@ ahead m1 m2 = Stream $ \svr stp sng yld -> do
1043 (runStream (fromStreamVar sv)) Nothing stp sng yld
1183 (runStream (fromStreamVar sv)) Nothing stp sng yld
1045 concurrently ma mb = Stream $ \svr stp sng yld -> do
1185 concurrently ma mb = Stream $ \svr stp sng yld -> do
1046− liftIO $ enqueueAhead (workQueue (fromJust svr)) mb
1186+ liftIO $ enqueueAhead (fromJust svr) (workQueue (fromJust svr)) mb
1047 (runStream ma) Nothing stp sng yld
1187 (runStream ma) Nothing stp sng yld
1189+-- | XXX we can implement it more efficienty by directly implementing instead
1190+-- of combining streams using ahead.
1191+{-# INLINE consMAhead #-}
1192+consMAhead :: MonadAsync m => m a -> Stream m a -> Stream m a
1193+consMAhead m r = once m `ahead` r
1049------------------------------------------------------------------------------
1195------------------------------------------------------------------------------
1050-- Semigroup and Monoid style compositions for parallel actions
1196-- Semigroup and Monoid style compositions for parallel actions
1051------------------------------------------------------------------------------
1197------------------------------------------------------------------------------
@@ -1054,14 +1200,32 @@ ahead m1 m2 = Stream $ \svr stp sng yld -> do
1054async :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1200async :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1055async = joinStreamVarAsync AsyncVar
1201async = joinStreamVarAsync AsyncVar
1203+-- | XXX we can implement it more efficienty by directly implementing instead
1204+-- of combining streams using async.
1205+{-# INLINE consMAsync #-}
1206+consMAsync :: MonadAsync m => m a -> Stream m a -> Stream m a
1207+consMAsync m r = once m `async` r
1057{-# INLINE wAsync #-}
1209{-# INLINE wAsync #-}
1058wAsync :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1210wAsync :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1059wAsync = joinStreamVarAsync WAsyncVar
1211wAsync = joinStreamVarAsync WAsyncVar
1213+-- | XXX we can implement it more efficienty by directly implementing instead
1214+-- of combining streams using wAsync.
1215+{-# INLINE consMWAsync #-}
1216+consMWAsync :: MonadAsync m => m a -> Stream m a -> Stream m a
1217+consMWAsync m r = once m `wAsync` r
1061{-# INLINE parallel #-}
1219{-# INLINE parallel #-}
1062parallel :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1220parallel :: MonadAsync m => Stream m a -> Stream m a -> Stream m a
1063parallel = joinStreamVarPar ParallelVar
1221parallel = joinStreamVarPar ParallelVar
1223+-- | XXX we can implement it more efficienty by directly implementing instead
1224+-- of combining streams using parallel.
1225+{-# INLINE consMParallel #-}
1226+consMParallel :: MonadAsync m => m a -> Stream m a -> Stream m a
1227+consMParallel m r = once m `parallel` r
1065-------------------------------------------------------------------------------
1229-------------------------------------------------------------------------------
1066-- Functor instace is the same for all types
1230-- Functor instace is the same for all types
1067-------------------------------------------------------------------------------
1231-------------------------------------------------------------------------------
@@ -1076,8 +1240,8 @@ instance Monad m => Functor (Stream m) where
1076-- Alternative & MonadPlus
1240-- Alternative & MonadPlus
1077------------------------------------------------------------------------------
1241------------------------------------------------------------------------------
1079−alt :: Stream m a -> Stream m a -> Stream m a
1243+_alt :: Stream m a -> Stream m a -> Stream m a
1080−alt m1 m2 = Stream $ \_ stp sng yld ->
1244+_alt m1 m2 = Stream $ \_ stp sng yld ->
1081 let stop = runStream m2 Nothing stp sng yld
1245 let stop = runStream m2 Nothing stp sng yld
1082 in runStream m1 Nothing stop sng yld
1246 in runStream m1 Nothing stop sng yld