{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Ouroboros.Consensus.BlockchainTime.WallClock.Simple (
simpleBlockchainTime
, getWallClockSlot
, waitUntilNextSlot
) where
import Control.Monad
import Control.ResourceRegistry
import Data.Bifunctor
import Data.Fixed (divMod')
import Data.Time (NominalDiffTime)
import Data.Void
import Ouroboros.Consensus.Block
import Ouroboros.Consensus.BlockchainTime.API
import Ouroboros.Consensus.BlockchainTime.WallClock.Types
import Ouroboros.Consensus.BlockchainTime.WallClock.Util
import Ouroboros.Consensus.Util.IOLike
import Ouroboros.Consensus.Util.Time
simpleBlockchainTime :: forall m. IOLike m
=> ResourceRegistry m
-> SystemTime m
-> SlotLength
-> NominalDiffTime
-> m (BlockchainTime m)
simpleBlockchainTime :: forall (m :: * -> *).
IOLike m =>
ResourceRegistry m
-> SystemTime m
-> SlotLength
-> NominalDiffTime
-> m (BlockchainTime m)
simpleBlockchainTime ResourceRegistry m
registry SystemTime m
time SlotLength
slotLen NominalDiffTime
maxClockRewind = do
SystemTime m -> m ()
forall (m :: * -> *). SystemTime m -> m ()
systemTimeWait SystemTime m
time
firstSlot <- (SlotNo, NominalDiffTime) -> SlotNo
forall a b. (a, b) -> a
fst ((SlotNo, NominalDiffTime) -> SlotNo)
-> m (SlotNo, NominalDiffTime) -> m SlotNo
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> SystemTime m -> SlotLength -> m (SlotNo, NominalDiffTime)
forall (m :: * -> *).
IOLike m =>
SystemTime m -> SlotLength -> m (SlotNo, NominalDiffTime)
getWallClockSlot SystemTime m
time SlotLength
slotLen
slotVar <- newTVarIO firstSlot
void $ forkLinkedThread registry "simpleBlockchainTime" $
loop slotVar firstSlot
return BlockchainTime {
getCurrentSlot = CurrentSlot <$> readTVar slotVar
}
where
loop :: StrictTVar m SlotNo
-> SlotNo
-> m Void
loop :: StrictTVar m SlotNo -> SlotNo -> m Void
loop StrictTVar m SlotNo
slotVar = SlotNo -> m Void
go
where
go :: SlotNo -> m Void
go :: SlotNo -> m Void
go SlotNo
current = do
next <- SystemTime m -> SlotLength -> NominalDiffTime -> SlotNo -> m SlotNo
forall (m :: * -> *).
IOLike m =>
SystemTime m -> SlotLength -> NominalDiffTime -> SlotNo -> m SlotNo
waitUntilNextSlot SystemTime m
time SlotLength
slotLen NominalDiffTime
maxClockRewind SlotNo
current
atomically $ writeTVar slotVar next
go next
slotFromUTCTime :: SlotLength -> RelativeTime -> (SlotNo, NominalDiffTime)
slotFromUTCTime :: SlotLength -> RelativeTime -> (SlotNo, NominalDiffTime)
slotFromUTCTime SlotLength
slotLen (RelativeTime NominalDiffTime
now) =
(Word64 -> SlotNo)
-> (Word64, NominalDiffTime) -> (SlotNo, NominalDiffTime)
forall a b c. (a -> b) -> (a, c) -> (b, c)
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first Word64 -> SlotNo
SlotNo ((Word64, NominalDiffTime) -> (SlotNo, NominalDiffTime))
-> (Word64, NominalDiffTime) -> (SlotNo, NominalDiffTime)
forall a b. (a -> b) -> a -> b
$ NominalDiffTime
now NominalDiffTime -> NominalDiffTime -> (Word64, NominalDiffTime)
forall a b. (Real a, Integral b) => a -> a -> (b, a)
`divMod'` SlotLength -> NominalDiffTime
getSlotLength SlotLength
slotLen
delayUntilNextSlot :: SlotLength -> RelativeTime -> NominalDiffTime
delayUntilNextSlot :: SlotLength -> RelativeTime -> NominalDiffTime
delayUntilNextSlot SlotLength
slotLen RelativeTime
now =
SlotLength -> NominalDiffTime
getSlotLength SlotLength
slotLen NominalDiffTime -> NominalDiffTime -> NominalDiffTime
forall a. Num a => a -> a -> a
- NominalDiffTime
timeSpent
where
(SlotNo
_curSlot, NominalDiffTime
timeSpent) = SlotLength -> RelativeTime -> (SlotNo, NominalDiffTime)
slotFromUTCTime SlotLength
slotLen RelativeTime
now
getWallClockSlot :: IOLike m
=> SystemTime m
-> SlotLength
-> m (SlotNo, NominalDiffTime)
getWallClockSlot :: forall (m :: * -> *).
IOLike m =>
SystemTime m -> SlotLength -> m (SlotNo, NominalDiffTime)
getWallClockSlot SystemTime{m ()
m RelativeTime
systemTimeWait :: forall (m :: * -> *). SystemTime m -> m ()
systemTimeCurrent :: m RelativeTime
systemTimeWait :: m ()
systemTimeCurrent :: forall (m :: * -> *). SystemTime m -> m RelativeTime
..} SlotLength
slotLen =
SlotLength -> RelativeTime -> (SlotNo, NominalDiffTime)
slotFromUTCTime SlotLength
slotLen (RelativeTime -> (SlotNo, NominalDiffTime))
-> m RelativeTime -> m (SlotNo, NominalDiffTime)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> m RelativeTime
systemTimeCurrent
waitUntilNextSlot :: IOLike m
=> SystemTime m
-> SlotLength
-> NominalDiffTime
-> SlotNo
-> m SlotNo
waitUntilNextSlot :: forall (m :: * -> *).
IOLike m =>
SystemTime m -> SlotLength -> NominalDiffTime -> SlotNo -> m SlotNo
waitUntilNextSlot time :: SystemTime m
time@SystemTime{m ()
m RelativeTime
systemTimeWait :: forall (m :: * -> *). SystemTime m -> m ()
systemTimeCurrent :: forall (m :: * -> *). SystemTime m -> m RelativeTime
systemTimeCurrent :: m RelativeTime
systemTimeWait :: m ()
..} SlotLength
slotLen NominalDiffTime
maxClockRewind SlotNo
oldCurrent = do
now <- m RelativeTime
systemTimeCurrent
let delay = SlotLength -> RelativeTime -> NominalDiffTime
delayUntilNextSlot SlotLength
slotLen RelativeTime
now
threadDelay (nominalDelay delay)
afterDelay <- systemTimeCurrent
let (newCurrent, _timeInNewCurrent) = slotFromUTCTime slotLen afterDelay
if | newCurrent > oldCurrent ->
return newCurrent
| newCurrent <= oldCurrent,
now `diffRelTime` afterDelay <= maxClockRewind ->
waitUntilNextSlot time slotLen maxClockRewind oldCurrent
| otherwise ->
throwIO $ SystemClockMovedBack oldCurrent newCurrent