@@ -93,6 +93,9 @@ module Streamly.Internal.Data.Stream.StreamD
9393 , enumerateFromToFractional
9494 , enumerateFromThenToFractional
9595
96+ -- ** Time
97+ , currentTime
98+
9699 -- ** Conversions
97100 -- | Transform an input structure into a stream.
98101 -- | Direct style stream does not support @fromFoldable@.
@@ -245,9 +248,11 @@ module Streamly.Internal.Data.Stream.StreamD
245248 , filterM
246249 , uniq
247250 , take
251+ , takeByTime
248252 , takeWhile
249253 , takeWhileM
250254 , drop
255+ , dropByTime
251256 , dropWhile
252257 , dropWhileM
253258
@@ -307,18 +312,13 @@ module Streamly.Internal.Data.Stream.StreamD
307312 , mkParallelD
308313
309314 , lastN
310−
311− -- * Time related
312− , takeByTime
313− , dropByTime
314− , currentTime
315315 )
316316where
317317
318318import Control.Concurrent (killThread, myThreadId, takeMVar, threadDelay)
319319import Control.Exception
320320 (Exception, SomeException, AsyncException, fromException)
321−import Control.Monad (void, when)
321+import Control.Monad (void, when, forever)
322322import Control.Monad.Catch (MonadCatch, throwM)
323323import Control.Monad.IO.Class (MonadIO(..))
324324import Control.Monad.Reader (ReaderT)
@@ -327,6 +327,7 @@ import Control.Monad.Trans (MonadTrans(lift))
327327import Control.Monad.Trans.Control (MonadBaseControl)
328328import Data.Bits (shiftR, shiftL, (.|.), (.&.))
329329import Data.Functor.Identity (Identity(..))
330+import Data.Int (Int64)
330331import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef, IORef)
331332import Data.Maybe (fromJust, isJust, isNothing)
332333import Data.Word (Word32)
@@ -346,7 +347,6 @@ import qualified Control.Monad.Reader as Reader
346347import qualified Control.Monad.State.Strict as State
347348import qualified Prelude
348349
349−import Data.Int (Int64)
350350import Streamly.Internal.Mutable.Prim.Var
351351 (Prim, Var, readVar, newVar, modifyVar')
352352import Streamly.Internal.Data.Time.Units
@@ -357,7 +357,8 @@ import Streamly.Internal.Memory.Array.Types (Array(..))
357357import Streamly.Internal.Data.Fold.Types (Fold(..))
358358import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
359359import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
360−import Streamly.Internal.Data.Time.Units (MicroSecond64(..), fromAbsTime, toAbsTime, AbsTime)
360+import Streamly.Internal.Data.Time.Units
361+ (MicroSecond64(..), fromAbsTime, toAbsTime, AbsTime)
361362import Streamly.Internal.Data.Unfold.Types (Unfold(..))
362363import Streamly.Internal.Data.Strict (Tuple3'(..))
363364
@@ -3449,6 +3450,9 @@ pollCounts transf fld (Stream step state) = Stream step' Nothing
34493450
34503451 {-# INLINE_LATE step' #-}
34513452 step' _ Nothing = do
3453+ -- As long as we are using an "Int" for counts lockfree reads from
3454+ -- Var should work correctly on both 32-bit and 64-bit machines.
3455+ -- However, an Int on a 32-bit machine may overflow quickly.
34523456 countVar <- liftIO $ newVar (0 :: Int)
34533457 tid <- forkManaged
34543458 $ void $ runFold fld
@@ -4211,25 +4215,40 @@ dropByTime duration (Stream step1 state1) = Stream step (DropByTimeInit state1)
42114215 Skip s -> Skip (DropByTimeYield s)
42124216 Stop -> Stop
42134217
4218+-- XXX we should move this to stream generation section of this file. Also, the
4219+-- take/drop combinators above should be moved to filtering section.
42144220{-# INLINE_NORMAL currentTime #-}
42154221currentTime :: MonadAsync m => Double -> Stream m AbsTime
42164222currentTime g = Stream step Nothing
4217− where
4218− next timeVar = do
4223+
4224+ where
4225+
4226+ g' = g * 10 ^ (6 :: Int)
4227+
4228+ -- XXX should have a minimum granularity to avoid high CPU usage?
4229+ {-# INLINE delayTime #-}
4230+ delayTime =
4231+ if g' >= fromIntegral (maxBound :: Int)
4232+ then maxBound
4233+ else round g'
4234+
4235+ updateTimeVar timeVar = do
42194236 threadDelay $ delayTime
42204237 MicroSecond64 t <- fromAbsTime <$> getTime Monotonic
42214238 modifyVar' timeVar (const t)
4222− next timeVar
4223− g' = g * 10 ^ (6 :: Int)
4224− {-# INLINE delayTime #-}
4225− delayTime = if g' >= fromIntegral (maxBound :: Int)
4226− then maxBound
4227− else round g'
4239+
42284240 {-# INLINE_LATE step #-}
42294241 step _ Nothing = do
4242+ -- XXX note that this is safe only on a 64-bit machine. On a 32-bit
4243+ -- machine a 64-bit 'Var' cannot be read consistently without a lock
4244+ -- while another thread is writing to it.
42304245 timeVar <- liftIO $ newVar (0 :: Int64)
4231− tid <- forkManaged $ liftIO $ void $ next timeVar
4246+ tid <- forkManaged $ liftIO $ forever (updateTimeVar timeVar)
42324247 return $ Skip $ Just (timeVar, tid)
4248+
42334249 step _ s@(Just (timeVar, _)) = do
42344250 a <- liftIO $ readVar timeVar
4251+ -- XXX we can perhaps use an AbsTime64 using a 64 bit Int for
4252+ -- efficiency. or maybe we can use a representation using Double for
4253+ -- floating precision time
42354254 return $ Yield (toAbsTime (MicroSecond64 a)) s