keid-frp-banana-0.1.1.0: src/Engine/ReactiveBanana/Timer.hs
module Engine.ReactiveBanana.Timer
( every
) where
import RIO
import Reactive.Banana qualified as RB
import Reactive.Banana.Frameworks qualified as RBF
import Resource.Region qualified as Region
import UnliftIO.Resource (ResourceT)
{- | An async process that will run forever and fire monotonic timestamp events.
Events would be processed serially on the timer thread and delays would be adjusted to keep up.
Events for the intervals "missed" would fire right away.
-}
every
:: (MonadUnliftIO m)
=> Int -- ^ Timer interval in microseconds (for 'threadDelay')
-> ResourceT m (RBF.MomentIO (RB.Event Double))
every delayMS = do
(addHandler, fire) <- liftIO RBF.newAddHandler
ticker <- async do
begin <- getMonotonicTime
threadDelay delayMS
forever do
before <- getMonotonicTime
liftIO $ fire before
after <- getMonotonicTime
let
tickNum = (after - begin) * 1e6 / fromIntegral delayMS :: Double
intTick = truncate tickNum :: Integer
driftTicks = tickNum - fromInteger intTick :: Double
driftMS = driftTicks * fromIntegral delayMS :: Double
adjustedDelay = max 0 $ delayMS - ceiling driftMS :: Int
-- when (driftTicks > 0.01) $
-- -- traceShowM driftTicks
-- traceShowM (delayMS, (tickNum, intTick, driftTicks), driftMS, adjustedDelay)
threadDelay adjustedDelay
Region.attachAsync ticker
pure $ RBF.fromAddHandler addHandler