{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE NamedFieldPuns #-}

module Network.QUIC.Recovery.Metrics (
    updateRTT
  , updateCC
  , metricsUpdated
  , setInitialCongestionWindow
  ) where

import Control.Concurrent.STM
import Data.Sequence (Seq)

import Network.QUIC.Imports
import Network.QUIC.Qlog
import Network.QUIC.Recovery.Constants
import Network.QUIC.Recovery.Misc
import Network.QUIC.Recovery.Persistent
import Network.QUIC.Recovery.Types
import Network.QUIC.Types

updateRTT :: LDCC -> EncryptionLevel -> Microseconds -> Microseconds -> IO ()
updateRTT :: LDCC -> EncryptionLevel -> Microseconds -> Microseconds -> IO ()
updateRTT ldcc :: LDCC
ldcc@LDCC{Array EncryptionLevel (IORef Bool)
Array EncryptionLevel (IORef PeerPacketNumbers)
Array EncryptionLevel (IORef LossDetection)
Array EncryptionLevel (IORef SentPackets)
TVar (Maybe EncryptionLevel)
TVar TimerInfoQ
TVar CC
TVar SentPackets
IORef Bool
IORef PacketNumber
IORef (Maybe TimeoutKey)
IORef (Maybe TimerInfo)
IORef PeerPacketNumbers
IORef RTT
ConnState
PlainPacket -> IO ()
QLogger
timerInfoQ :: LDCC -> TVar TimerInfoQ
previousRTT1PPNs :: LDCC -> IORef PeerPacketNumbers
peerPacketNumbers :: LDCC -> Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: LDCC -> IORef PacketNumber
speedingUp :: LDCC -> IORef Bool
ptoPing :: LDCC -> TVar (Maybe EncryptionLevel)
lostCandidates :: LDCC -> TVar SentPackets
timerInfo :: LDCC -> IORef (Maybe TimerInfo)
timerKey :: LDCC -> IORef (Maybe TimeoutKey)
lossDetection :: LDCC -> Array EncryptionLevel (IORef LossDetection)
sentPackets :: LDCC -> Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: LDCC -> Array EncryptionLevel (IORef Bool)
recoveryCC :: LDCC -> TVar CC
recoveryRTT :: LDCC -> IORef RTT
putRetrans :: LDCC -> PlainPacket -> IO ()
ldccQlogger :: LDCC -> QLogger
ldccState :: LDCC -> ConnState
timerInfoQ :: TVar TimerInfoQ
previousRTT1PPNs :: IORef PeerPacketNumbers
peerPacketNumbers :: Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: IORef PacketNumber
speedingUp :: IORef Bool
ptoPing :: TVar (Maybe EncryptionLevel)
lostCandidates :: TVar SentPackets
timerInfo :: IORef (Maybe TimerInfo)
timerKey :: IORef (Maybe TimeoutKey)
lossDetection :: Array EncryptionLevel (IORef LossDetection)
sentPackets :: Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: Array EncryptionLevel (IORef Bool)
recoveryCC :: TVar CC
recoveryRTT :: IORef RTT
putRetrans :: PlainPacket -> IO ()
ldccQlogger :: QLogger
ldccState :: ConnState
..} EncryptionLevel
lvl Microseconds
latestRTT0 Microseconds
ackDelay0 = LDCC -> IO () -> IO ()
metricsUpdated LDCC
ldcc (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
    Bool
firstTime <- IORef RTT -> (RTT -> (RTT, Bool)) -> IO Bool
forall a b. IORef a -> (a -> (a, b)) -> IO b
atomicModifyIORef' IORef RTT
recoveryRTT RTT -> (RTT, Bool)
update
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when Bool
firstTime (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        LDCC -> IO ()
setPktNumPersistent LDCC
ldcc
        LDCC -> Debug -> IO ()
forall q. KeepQlog q => q -> Debug -> IO ()
qlogDebug LDCC
ldcc (Debug -> IO ()) -> Debug -> IO ()
forall a b. (a -> b) -> a -> b
$ LogStr -> Debug
Debug LogStr
"RTT first sample"
  where
    -- don't use latestRTT, use latestRTT0 instead
    --
    -- First time:
    -- Overwriting the initial value with the first sample.
    -- Initial value was used to calculate PTO.
    --
    -- smoothed_rtt = rtt_sample
    -- rttvar = rtt_sample / 2
    update :: RTT -> (RTT, Bool)
update rtt :: RTT
rtt@RTT{PacketNumber
Microseconds
ptoCount :: RTT -> PacketNumber
maxAckDelay1RTT :: RTT -> Microseconds
minRTT :: RTT -> Microseconds
rttvar :: RTT -> Microseconds
smoothedRTT :: RTT -> Microseconds
latestRTT :: RTT -> Microseconds
ptoCount :: PacketNumber
maxAckDelay1RTT :: Microseconds
minRTT :: Microseconds
rttvar :: Microseconds
smoothedRTT :: Microseconds
latestRTT :: Microseconds
..} | Microseconds
latestRTT Microseconds -> Microseconds -> Bool
forall a. Eq a => a -> a -> Bool
== PacketNumber -> Microseconds
Microseconds PacketNumber
0 = (RTT
rtt {
        latestRTT :: Microseconds
latestRTT   = Microseconds
latestRTT0
      , minRTT :: Microseconds
minRTT      = Microseconds
latestRTT0
      , smoothedRTT :: Microseconds
smoothedRTT = Microseconds
latestRTT0
      , rttvar :: Microseconds
rttvar      = Microseconds
latestRTT0 Microseconds -> PacketNumber -> Microseconds
forall a. Bits a => a -> PacketNumber -> a
`unsafeShiftR` PacketNumber
1
      }, Bool
True)
    -- Others:
    update rtt :: RTT
rtt@RTT{PacketNumber
Microseconds
ptoCount :: PacketNumber
maxAckDelay1RTT :: Microseconds
minRTT :: Microseconds
rttvar :: Microseconds
smoothedRTT :: Microseconds
latestRTT :: Microseconds
ptoCount :: RTT -> PacketNumber
maxAckDelay1RTT :: RTT -> Microseconds
minRTT :: RTT -> Microseconds
rttvar :: RTT -> Microseconds
smoothedRTT :: RTT -> Microseconds
latestRTT :: RTT -> Microseconds
..} = (RTT
rtt {
        latestRTT :: Microseconds
latestRTT   = Microseconds
latestRTT0
      , minRTT :: Microseconds
minRTT      = Microseconds
minRTT'
      , smoothedRTT :: Microseconds
smoothedRTT = Microseconds
smoothedRTT'
      , rttvar :: Microseconds
rttvar      = Microseconds
rttvar'
      }, Bool
False)
      where
        -- minRTT ignores ack delay.
        minRTT' :: Microseconds
minRTT' = Microseconds -> Microseconds -> Microseconds
forall a. Ord a => a -> a -> a
min Microseconds
minRTT Microseconds
latestRTT0
        -- Limit ack_delay by max_ack_delay
        -- ack_delay = min(Ack Delay in ACK Frame, max_ack_delay)
        ackDelay :: Microseconds
ackDelay = Microseconds -> Microseconds -> Microseconds
forall a. Ord a => a -> a -> a
min Microseconds
ackDelay0 (Microseconds -> Microseconds) -> Microseconds -> Microseconds
forall a b. (a -> b) -> a -> b
$ Maybe EncryptionLevel -> Microseconds -> Microseconds
getMaxAckDelay (EncryptionLevel -> Maybe EncryptionLevel
forall a. a -> Maybe a
Just EncryptionLevel
lvl) Microseconds
maxAckDelay1RTT
        -- Adjust for ack delay if plausible.
        -- adjusted_rtt = latest_rtt
        -- if (latest_rtt >= min_rtt + ack_delay):
        --   adjusted_rtt = latest_rtt - ack_delay
        adjustedRTT :: Microseconds
adjustedRTT
          | Microseconds
latestRTT0 Microseconds -> Microseconds -> Bool
forall a. Ord a => a -> a -> Bool
>= Microseconds
minRTT Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
+ Microseconds
ackDelay = Microseconds
latestRTT0 Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
- Microseconds
ackDelay
          | Bool
otherwise                       = Microseconds
latestRTT0
        -- rttvar_sample = abs(smoothed_rtt - adjusted_rtt)
        -- rttvar = 3/4 * rttvar + 1/4 * rttvar_sample
        rttvar' :: Microseconds
rttvar' = Microseconds
rttvar Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
- (Microseconds
rttvar Microseconds -> PacketNumber -> Microseconds
forall a. Bits a => a -> PacketNumber -> a
.>>. PacketNumber
2)
                Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
+ (Microseconds -> Microseconds
forall a. Num a => a -> a
abs (Microseconds
smoothedRTT Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
- Microseconds
adjustedRTT) Microseconds -> PacketNumber -> Microseconds
forall a. Bits a => a -> PacketNumber -> a
.>>. PacketNumber
2)
        -- smoothed_rtt = 7/8 * smoothed_rtt + 1/8 * adjusted_rtt
        smoothedRTT' :: Microseconds
smoothedRTT' = Microseconds
smoothedRTT Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
- (Microseconds
smoothedRTT Microseconds -> PacketNumber -> Microseconds
forall a. Bits a => a -> PacketNumber -> a
.>>. PacketNumber
3)
                     Microseconds -> Microseconds -> Microseconds
forall a. Num a => a -> a -> a
+ (Microseconds
adjustedRTT Microseconds -> PacketNumber -> Microseconds
forall a. Bits a => a -> PacketNumber -> a
.>>. PacketNumber
3)

updateCC :: LDCC -> Seq SentPacket -> Bool -> IO ()
updateCC :: LDCC -> Seq SentPacket -> Bool -> IO ()
updateCC ldcc :: LDCC
ldcc@LDCC{Array EncryptionLevel (IORef Bool)
Array EncryptionLevel (IORef PeerPacketNumbers)
Array EncryptionLevel (IORef LossDetection)
Array EncryptionLevel (IORef SentPackets)
TVar (Maybe EncryptionLevel)
TVar TimerInfoQ
TVar CC
TVar SentPackets
IORef Bool
IORef PacketNumber
IORef (Maybe TimeoutKey)
IORef (Maybe TimerInfo)
IORef PeerPacketNumbers
IORef RTT
ConnState
PlainPacket -> IO ()
QLogger
timerInfoQ :: TVar TimerInfoQ
previousRTT1PPNs :: IORef PeerPacketNumbers
peerPacketNumbers :: Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: IORef PacketNumber
speedingUp :: IORef Bool
ptoPing :: TVar (Maybe EncryptionLevel)
lostCandidates :: TVar SentPackets
timerInfo :: IORef (Maybe TimerInfo)
timerKey :: IORef (Maybe TimeoutKey)
lossDetection :: Array EncryptionLevel (IORef LossDetection)
sentPackets :: Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: Array EncryptionLevel (IORef Bool)
recoveryCC :: TVar CC
recoveryRTT :: IORef RTT
putRetrans :: PlainPacket -> IO ()
ldccQlogger :: QLogger
ldccState :: ConnState
timerInfoQ :: LDCC -> TVar TimerInfoQ
previousRTT1PPNs :: LDCC -> IORef PeerPacketNumbers
peerPacketNumbers :: LDCC -> Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: LDCC -> IORef PacketNumber
speedingUp :: LDCC -> IORef Bool
ptoPing :: LDCC -> TVar (Maybe EncryptionLevel)
lostCandidates :: LDCC -> TVar SentPackets
timerInfo :: LDCC -> IORef (Maybe TimerInfo)
timerKey :: LDCC -> IORef (Maybe TimeoutKey)
lossDetection :: LDCC -> Array EncryptionLevel (IORef LossDetection)
sentPackets :: LDCC -> Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: LDCC -> Array EncryptionLevel (IORef Bool)
recoveryCC :: LDCC -> TVar CC
recoveryRTT :: LDCC -> IORef RTT
putRetrans :: LDCC -> PlainPacket -> IO ()
ldccQlogger :: LDCC -> QLogger
ldccState :: LDCC -> ConnState
..} Seq SentPacket
lostPackets Bool
isRecovery = do
    Bool
persistent <- LDCC -> Seq SentPacket -> IO Bool
inPersistentCongestion LDCC
ldcc Seq SentPacket
lostPackets
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Bool
persistent Bool -> Bool -> Bool
|| Bool -> Bool
not Bool
isRecovery) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ do
        PacketNumber
minWindow <- LDCC -> IO PacketNumber
kMinimumWindow LDCC
ldcc
        TimeMicrosecond
now <- IO TimeMicrosecond
getTimeMicrosecond
        LDCC -> IO () -> IO ()
metricsUpdated LDCC
ldcc (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ TVar CC -> (CC -> CC) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar CC
recoveryCC ((CC -> CC) -> STM ()) -> (CC -> CC) -> STM ()
forall a b. (a -> b) -> a -> b
$ \cc :: CC
cc@CC{PacketNumber
Maybe TimeMicrosecond
CCMode
ccMode :: CC -> CCMode
numOfAckEliciting :: CC -> PacketNumber
bytesAcked :: CC -> PacketNumber
ssthresh :: CC -> PacketNumber
congestionRecoveryStartTime :: CC -> Maybe TimeMicrosecond
congestionWindow :: CC -> PacketNumber
bytesInFlight :: CC -> PacketNumber
ccMode :: CCMode
numOfAckEliciting :: PacketNumber
bytesAcked :: PacketNumber
ssthresh :: PacketNumber
congestionRecoveryStartTime :: Maybe TimeMicrosecond
congestionWindow :: PacketNumber
bytesInFlight :: PacketNumber
..} ->
            let halfWindow :: PacketNumber
halfWindow = PacketNumber -> PacketNumber -> PacketNumber
forall a. Ord a => a -> a -> a
max PacketNumber
minWindow (PacketNumber -> PacketNumber) -> PacketNumber -> PacketNumber
forall a b. (a -> b) -> a -> b
$ PacketNumber -> PacketNumber
kLossReductionFactor PacketNumber
congestionWindow
                cwin :: PacketNumber
cwin
                  | Bool
persistent = PacketNumber
minWindow
                  | Bool
otherwise  = PacketNumber
halfWindow
                sst :: PacketNumber
sst            = PacketNumber
halfWindow
                mode :: CCMode
mode
                  | PacketNumber
cwin PacketNumber -> PacketNumber -> Bool
forall a. Ord a => a -> a -> Bool
< PacketNumber
sst = CCMode
SlowStart -- persistent
                  | Bool
otherwise  = CCMode
Recovery
            in CC
cc {
                congestionRecoveryStartTime :: Maybe TimeMicrosecond
congestionRecoveryStartTime = TimeMicrosecond -> Maybe TimeMicrosecond
forall a. a -> Maybe a
Just TimeMicrosecond
now
              , congestionWindow :: PacketNumber
congestionWindow = PacketNumber
cwin
              , ssthresh :: PacketNumber
ssthresh         = PacketNumber
sst
              , ccMode :: CCMode
ccMode           = CCMode
mode
              , bytesAcked :: PacketNumber
bytesAcked       = PacketNumber
0
              }
        CC{CCMode
ccMode :: CCMode
ccMode :: CC -> CCMode
ccMode} <- TVar CC -> IO CC
forall a. TVar a -> IO a
readTVarIO TVar CC
recoveryCC
        LDCC -> CCMode -> IO ()
forall q. KeepQlog q => q -> CCMode -> IO ()
qlogContestionStateUpdated LDCC
ldcc CCMode
ccMode

setInitialCongestionWindow :: LDCC -> Int -> IO ()
setInitialCongestionWindow :: LDCC -> PacketNumber -> IO ()
setInitialCongestionWindow ldcc :: LDCC
ldcc@LDCC{Array EncryptionLevel (IORef Bool)
Array EncryptionLevel (IORef PeerPacketNumbers)
Array EncryptionLevel (IORef LossDetection)
Array EncryptionLevel (IORef SentPackets)
TVar (Maybe EncryptionLevel)
TVar TimerInfoQ
TVar CC
TVar SentPackets
IORef Bool
IORef PacketNumber
IORef (Maybe TimeoutKey)
IORef (Maybe TimerInfo)
IORef PeerPacketNumbers
IORef RTT
ConnState
PlainPacket -> IO ()
QLogger
timerInfoQ :: TVar TimerInfoQ
previousRTT1PPNs :: IORef PeerPacketNumbers
peerPacketNumbers :: Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: IORef PacketNumber
speedingUp :: IORef Bool
ptoPing :: TVar (Maybe EncryptionLevel)
lostCandidates :: TVar SentPackets
timerInfo :: IORef (Maybe TimerInfo)
timerKey :: IORef (Maybe TimeoutKey)
lossDetection :: Array EncryptionLevel (IORef LossDetection)
sentPackets :: Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: Array EncryptionLevel (IORef Bool)
recoveryCC :: TVar CC
recoveryRTT :: IORef RTT
putRetrans :: PlainPacket -> IO ()
ldccQlogger :: QLogger
ldccState :: ConnState
timerInfoQ :: LDCC -> TVar TimerInfoQ
previousRTT1PPNs :: LDCC -> IORef PeerPacketNumbers
peerPacketNumbers :: LDCC -> Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: LDCC -> IORef PacketNumber
speedingUp :: LDCC -> IORef Bool
ptoPing :: LDCC -> TVar (Maybe EncryptionLevel)
lostCandidates :: LDCC -> TVar SentPackets
timerInfo :: LDCC -> IORef (Maybe TimerInfo)
timerKey :: LDCC -> IORef (Maybe TimeoutKey)
lossDetection :: LDCC -> Array EncryptionLevel (IORef LossDetection)
sentPackets :: LDCC -> Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: LDCC -> Array EncryptionLevel (IORef Bool)
recoveryCC :: LDCC -> TVar CC
recoveryRTT :: LDCC -> IORef RTT
putRetrans :: LDCC -> PlainPacket -> IO ()
ldccQlogger :: LDCC -> QLogger
ldccState :: LDCC -> ConnState
..} PacketNumber
pktSiz = LDCC -> IO () -> IO ()
metricsUpdated LDCC
ldcc (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
    STM () -> IO ()
forall a. STM a -> IO a
atomically (STM () -> IO ()) -> STM () -> IO ()
forall a b. (a -> b) -> a -> b
$ do TVar CC -> (CC -> CC) -> STM ()
forall a. TVar a -> (a -> a) -> STM ()
modifyTVar' TVar CC
recoveryCC ((CC -> CC) -> STM ()) -> (CC -> CC) -> STM ()
forall a b. (a -> b) -> a -> b
$ \CC
cc -> CC
cc {
        congestionWindow :: PacketNumber
congestionWindow = PacketNumber -> PacketNumber
kInitialWindow PacketNumber
pktSiz
      }

----------------------------------------------------------------

metricsUpdated :: LDCC -> IO () -> IO ()
metricsUpdated :: LDCC -> IO () -> IO ()
metricsUpdated ldcc :: LDCC
ldcc@LDCC{Array EncryptionLevel (IORef Bool)
Array EncryptionLevel (IORef PeerPacketNumbers)
Array EncryptionLevel (IORef LossDetection)
Array EncryptionLevel (IORef SentPackets)
TVar (Maybe EncryptionLevel)
TVar TimerInfoQ
TVar CC
TVar SentPackets
IORef Bool
IORef PacketNumber
IORef (Maybe TimeoutKey)
IORef (Maybe TimerInfo)
IORef PeerPacketNumbers
IORef RTT
ConnState
PlainPacket -> IO ()
QLogger
timerInfoQ :: TVar TimerInfoQ
previousRTT1PPNs :: IORef PeerPacketNumbers
peerPacketNumbers :: Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: IORef PacketNumber
speedingUp :: IORef Bool
ptoPing :: TVar (Maybe EncryptionLevel)
lostCandidates :: TVar SentPackets
timerInfo :: IORef (Maybe TimerInfo)
timerKey :: IORef (Maybe TimeoutKey)
lossDetection :: Array EncryptionLevel (IORef LossDetection)
sentPackets :: Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: Array EncryptionLevel (IORef Bool)
recoveryCC :: TVar CC
recoveryRTT :: IORef RTT
putRetrans :: PlainPacket -> IO ()
ldccQlogger :: QLogger
ldccState :: ConnState
timerInfoQ :: LDCC -> TVar TimerInfoQ
previousRTT1PPNs :: LDCC -> IORef PeerPacketNumbers
peerPacketNumbers :: LDCC -> Array EncryptionLevel (IORef PeerPacketNumbers)
pktNumPersistent :: LDCC -> IORef PacketNumber
speedingUp :: LDCC -> IORef Bool
ptoPing :: LDCC -> TVar (Maybe EncryptionLevel)
lostCandidates :: LDCC -> TVar SentPackets
timerInfo :: LDCC -> IORef (Maybe TimerInfo)
timerKey :: LDCC -> IORef (Maybe TimeoutKey)
lossDetection :: LDCC -> Array EncryptionLevel (IORef LossDetection)
sentPackets :: LDCC -> Array EncryptionLevel (IORef SentPackets)
spaceDiscarded :: LDCC -> Array EncryptionLevel (IORef Bool)
recoveryCC :: LDCC -> TVar CC
recoveryRTT :: LDCC -> IORef RTT
putRetrans :: LDCC -> PlainPacket -> IO ()
ldccQlogger :: LDCC -> QLogger
ldccState :: LDCC -> ConnState
..} IO ()
body = do
    RTT
rtt0 <- IORef RTT -> IO RTT
forall a. IORef a -> IO a
readIORef IORef RTT
recoveryRTT
    CC
cc0 <- TVar CC -> IO CC
forall a. TVar a -> IO a
readTVarIO TVar CC
recoveryCC
    IO ()
body
    RTT
rtt1 <- IORef RTT -> IO RTT
forall a. IORef a -> IO a
readIORef IORef RTT
recoveryRTT
    CC
cc1 <- TVar CC -> IO CC
forall a. TVar a -> IO a
readTVarIO TVar CC
recoveryCC
    let ~[(String, PacketNumber)]
diff = [Maybe (String, PacketNumber)] -> [(String, PacketNumber)]
forall a. [Maybe a] -> [a]
catMaybes [
            String
-> Microseconds -> Microseconds -> Maybe (String, PacketNumber)
forall a.
a -> Microseconds -> Microseconds -> Maybe (a, PacketNumber)
time String
"min_rtt"      (RTT -> Microseconds
minRTT      RTT
rtt0) (RTT -> Microseconds
minRTT      RTT
rtt1)
          , String
-> Microseconds -> Microseconds -> Maybe (String, PacketNumber)
forall a.
a -> Microseconds -> Microseconds -> Maybe (a, PacketNumber)
time String
"smoothed_rtt" (RTT -> Microseconds
smoothedRTT RTT
rtt0) (RTT -> Microseconds
smoothedRTT RTT
rtt1)
          , String
-> Microseconds -> Microseconds -> Maybe (String, PacketNumber)
forall a.
a -> Microseconds -> Microseconds -> Maybe (a, PacketNumber)
time String
"latest_rtt"   (RTT -> Microseconds
latestRTT   RTT
rtt0) (RTT -> Microseconds
latestRTT   RTT
rtt1)
          , String
-> Microseconds -> Microseconds -> Maybe (String, PacketNumber)
forall a.
a -> Microseconds -> Microseconds -> Maybe (a, PacketNumber)
time String
"rtt_variance" (RTT -> Microseconds
rttvar      RTT
rtt0) (RTT -> Microseconds
rttvar      RTT
rtt1)
          , String
-> PacketNumber -> PacketNumber -> Maybe (String, PacketNumber)
forall b a. Eq b => a -> b -> b -> Maybe (a, b)
numb String
"pto_count"    (RTT -> PacketNumber
ptoCount    RTT
rtt0) (RTT -> PacketNumber
ptoCount    RTT
rtt1)
          , String
-> PacketNumber -> PacketNumber -> Maybe (String, PacketNumber)
forall b a. Eq b => a -> b -> b -> Maybe (a, b)
numb String
"bytes_in_flight"   (CC -> PacketNumber
bytesInFlight CC
cc0) (CC -> PacketNumber
bytesInFlight CC
cc1)
          , String
-> PacketNumber -> PacketNumber -> Maybe (String, PacketNumber)
forall b a. Eq b => a -> b -> b -> Maybe (a, b)
numb String
"congestion_window" (CC -> PacketNumber
congestionWindow CC
cc0) (CC -> PacketNumber
congestionWindow CC
cc1)
          , String
-> PacketNumber -> PacketNumber -> Maybe (String, PacketNumber)
forall b a. Eq b => a -> b -> b -> Maybe (a, b)
numb String
"ssthresh"          (CC -> PacketNumber
ssthresh CC
cc0) (CC -> PacketNumber
ssthresh CC
cc1)
          ]
    Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless ([(String, PacketNumber)] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [(String, PacketNumber)]
diff) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$ LDCC -> MetricsDiff -> IO ()
forall q. KeepQlog q => q -> MetricsDiff -> IO ()
qlogMetricsUpdated LDCC
ldcc (MetricsDiff -> IO ()) -> MetricsDiff -> IO ()
forall a b. (a -> b) -> a -> b
$ [(String, PacketNumber)] -> MetricsDiff
MetricsDiff [(String, PacketNumber)]
diff
  where
    time :: a -> Microseconds -> Microseconds -> Maybe (a, PacketNumber)
time a
tag (Microseconds PacketNumber
v0) (Microseconds PacketNumber
v1)
      | PacketNumber
v0 PacketNumber -> PacketNumber -> Bool
forall a. Eq a => a -> a -> Bool
== PacketNumber
v1  = Maybe (a, PacketNumber)
forall a. Maybe a
Nothing
      | Bool
otherwise = (a, PacketNumber) -> Maybe (a, PacketNumber)
forall a. a -> Maybe a
Just (a
tag,PacketNumber
v1)
    numb :: a -> b -> b -> Maybe (a, b)
numb a
tag b
v0 b
v1
      | b
v0 b -> b -> Bool
forall a. Eq a => a -> a -> Bool
== b
v1  = Maybe (a, b)
forall a. Maybe a
Nothing
      | Bool
otherwise = (a, b) -> Maybe (a, b)
forall a. a -> Maybe a
Just (a
tag,b
v1)