quic-0.1.0: Network/QUIC/Recovery/Persistent.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Network.QUIC.Recovery.Persistent (
getMaxAckDelay
, calcPTO
, backOff
, inPersistentCongestion
, findDuration -- for testing
, getPTO
) where
import Data.Sequence (Seq, ViewL(..))
import qualified Data.Sequence as Seq
import Data.UnixTime
import Network.QUIC.Imports
import Network.QUIC.Recovery.Constants
import Network.QUIC.Recovery.Misc
import Network.QUIC.Recovery.Types
import Network.QUIC.Types
getMaxAckDelay :: Maybe EncryptionLevel -> Microseconds -> Microseconds
getMaxAckDelay Nothing n = n
getMaxAckDelay (Just lvl) n
| lvl `elem` [InitialLevel,HandshakeLevel] = 0
| otherwise = n
-- Sec 6.2.1. Computing PTO
-- PTO = smoothed_rtt + max(4*rttvar, kGranularity) + max_ack_delay
calcPTO :: RTT -> Maybe EncryptionLevel -> Microseconds
calcPTO RTT{..} mlvl = smoothedRTT + max (rttvar !<<. 2) kGranularity + dly
where
dly = getMaxAckDelay mlvl maxAckDelay1RTT
backOff :: Microseconds -> Int -> Microseconds
backOff n cnt = n * (2 ^ cnt)
-- Sec 7.8. Persistent Congestion
inPersistentCongestion :: LDCC -> Seq SentPacket -> IO Bool
inPersistentCongestion ldcc@LDCC{..} lostPackets = do
pn <- getPktNumPersistent ldcc
let mduration = findDuration lostPackets pn
case mduration of
Nothing -> return False
Just duration -> do
rtt <- readIORef recoveryRTT
let pto = calcPTO rtt Nothing
Microseconds congestionPeriod = kPersistentCongestionThreshold pto
threshold = microSecondsToUnixDiffTime congestionPeriod
return (duration > threshold)
findDuration :: Seq SentPacket -> PacketNumber -> Maybe UnixDiffTime
findDuration pkts0 pn = leftEdge pkts0 Nothing
where
leftEdge pkts mdiff = case Seq.viewl pkts' of
EmptyL -> mdiff
l :< pkts'' -> case rightEdge (spPacketNumber l) pkts'' Nothing of
(Nothing, pkts''') -> leftEdge pkts''' mdiff
(Just r, pkts''') ->
let diff' = spTimeSent r `diffUnixTime` spTimeSent l
in case mdiff of
Nothing -> leftEdge pkts''' $ Just diff'
Just diff
| diff' > diff -> leftEdge pkts''' $ Just diff'
| otherwise -> leftEdge pkts''' $ Just diff
where
(_, pkts') = Seq.breakl (\x -> spAckEliciting x && spPacketNumber x >= pn) pkts
rightEdge n pkts Nothing = case Seq.viewl pkts of
EmptyL -> (Nothing, Seq.empty)
r :< pkts'
| spPacketNumber r == n + 1 ->
if spAckEliciting r then
rightEdge (n + 1) pkts' $ Just r
else
rightEdge (n + 1) pkts' Nothing
| otherwise -> (Nothing, pkts)
rightEdge n pkts mr0 = case Seq.viewl pkts of
EmptyL -> (mr0, Seq.empty)
r :< pkts'
| spPacketNumber r == n + 1 ->
if spAckEliciting r then
rightEdge (n + 1) pkts' $ Just r
else
rightEdge (n + 1) pkts' mr0
| otherwise -> (mr0, pkts)
getPTO :: LDCC -> IO Microseconds
getPTO LDCC{..} = do
rtt <- readIORef recoveryRTT
return $ calcPTO rtt Nothing