module Hans.Layer.Tcp.Timers (
initTimers
, resetIdle
, whenIdleFor
, set2MSL
, calibrateRTO
) where
import Hans.Channel
import Hans.Layer
import Hans.Layer.Tcp.Messages
import Hans.Layer.Tcp.Monad
import Hans.Layer.Tcp.Types
import Hans.Layer.Tcp.Window
import Hans.Timers (Milliseconds)
import Control.Concurrent (forkIO,threadDelay)
import Control.Monad (when,unless,forever,void)
import Data.Time.Clock.POSIX (POSIXTime)
import qualified Data.Foldable as F
every :: Milliseconds -> Tcp () -> Tcp ()
every len body = do
tcp <- self
let timeout = len * 1000
output $ void $ forkIO $ forever $
do threadDelay timeout
send tcp body
initTimers :: Tcp ()
initTimers = do
every 500 slowTimer
every 200 fastTimer
slowTimer :: Tcp ()
slowTimer =
do
eachConnection $ do
do TcpSocket { .. } <- getTcpSocket
unless (tcpState == TimeWait) $
do handleRTO
handleFinWait2
handle2MSL
updateTimers
modifyHost $ \ host ->
host { hostTimeWaits = stepTimeWaitConnections (hostTimeWaits host) }
resetIdle :: Sock ()
resetIdle = modifyTcpTimers_ (\tt -> tt { ttIdle = 0 })
fastTimer :: Tcp ()
fastTimer = eachConnection $ do
tcp <- getTcpSocket
when (tcpState tcp /= TimeWait && needsDelayedAck tcp) ack
updateTimers :: Sock ()
updateTimers =
modifyTcpSocket_ $ \ tcp ->
let tt = tcpTimers tcp
in tcp { tcpTimers = tt
{ tt2MSL = decrement (tt2MSL tt)
, ttIdle = increment (ttIdle tt)
}
}
where
decrement 0 = 0
decrement val = pred val
increment = succ
whenTimer :: (TcpTimers -> SlowTicks) -> Sock () -> Sock ()
whenTimer prj body = do
tt <- getTcpTimers
when (prj tt == 1) body
whenIdleFor :: Int -> Sock () -> Sock ()
whenIdleFor timeout body = do
tt <- getTcpTimers
when (ttIdle tt >= timeout) body
set2MSL :: SlowTicks -> Sock ()
set2MSL val = modifyTcpTimers_ (\tt -> tt { tt2MSL = val })
tcpKeepIntVal :: SlowTicks
tcpKeepIntVal = 150
handle2MSL :: Sock ()
handle2MSL =
do whenTimer tt2MSL $
do TcpSocket { .. } <- getTcpSocket
let TcpTimers { .. } = tcpTimers
if tcpState /= TimeWait && ttIdle <= ttMaxIdle
then set2MSL tcpKeepIntVal
else closeSocket
finWait2Idle :: SlowTicks
finWait2Idle = 1200
handleFinWait2 :: Sock ()
handleFinWait2 = whenState FinWait2
$ whenIdleFor finWait2Idle
$ set2MSL tcpKeepIntVal
handleRTO :: Sock ()
handleRTO = do
s <- getState
when (s /= Closed) (F.mapM_ outputSegment =<< modifyTcpSocket update)
where
update tcp = (segs,tcp { tcpOut = win' })
where
(segs,win') = genRetransmitSegments (tcpOut tcp)
calibrateRTO :: POSIXTime -> POSIXTime -> TcpTimers -> TcpTimers
calibrateRTO sent ackd tt
| ttSRTT tt == 0 = initial
| otherwise = rolling
where
r = ackd sent
initial = updateRTO tt
{ ttSRTT = r
, ttRTTVar = r / 2
}
alpha = 0.125
beta = 0.25
rttvar = (1 beta) * ttRTTVar tt + beta * abs (ttSRTT tt * r)
srtt = (1 alpha) * ttSRTT tt + alpha * r
rolling = updateRTO tt
{ ttRTTVar = rttvar
, ttSRTT = srtt
}
updateRTO tt' = tt'
{ ttRTO = min 128 (ceiling (ttSRTT tt' + max 0.5 (2 * ttRTTVar tt')))
}