@@ -64,11 +64,13 @@ module Streamly.Internal.Data.Stream.StreamD.Generate
6464 , fromProducer
6565 , fromSVar
6666 , toStreamD
67+
68+ , newCallbackStream
6769 )
6870where
6971
70−import Control.Concurrent (threadDelay)
71−import Control.Monad (forever)
72+import Control.Concurrent (myThreadId, threadDelay)
73+import Control.Monad (void, forever)
7274import Control.Monad.IO.Class (MonadIO(..))
7375import Data.Int (Int64)
7476import Streamly.Internal.Data.Time.Units
@@ -79,6 +81,7 @@ import Streamly.Internal.Data.Time.Units
7981import Streamly.Internal.Data.Unfold.Types (Unfold(..))
8082
8183import qualified Streamly.Internal.Data.IORef.Prim as Prim
84+import qualified Streamly.Internal.Data.Stream.StreamK as K
8285
8386import Prelude hiding (iterate, repeat, replicate, takeWhile)
8487import Streamly.Internal.Data.Stream.StreamD.Type
@@ -412,3 +415,27 @@ fromListM = Stream step
412415 {-# INLINE_LATE step #-}
413416 step _ (m:ms) = m >>= \x -> return $ Yield x ms
414417 step _ [] = return Stop
418+
419+-- Note: we can use another API with two callbacks stop and yield if we want
420+-- the callback to be able to indicate end of stream.
421+--
422+-- | Generates a callback and a stream pair. The callback returned is used to
423+-- queue values to the stream. The stream is infinite, there is no way for the
424+-- callback to indicate that it is done now.
425+--
426+-- /Internal/
427+--
428+{-# INLINE_NORMAL newCallbackStream #-}
429+newCallbackStream :: (K.IsStream t, MonadAsync m) => m ((a -> m ()), t m a)
430+newCallbackStream = do
431+ sv <- newParallelVar StopNone defState
432+
433+ -- XXX Add our own thread-id to the SVar as we can not know the callback's
434+ -- thread-id and the callback is not run in a managed worker. We need to
435+ -- handle this better.
436+ liftIO myThreadId >>= modifyThread sv
437+
438+ let callback a = liftIO $ void $ send sv (ChildYield a)
439+ -- XXX we can return an SVar and then the consumer can unfold from the
440+ -- SVar?
441+ return (callback, fromStreamD (fromSVar sv))