package sctp
import (
"math"
"sync"
"time"
)
const (
rtoInitial float64 = 1.0 * 1000
rtoMin float64 = 1.0 * 1000
defaultRTOMax float64 = 60.0 * 1000
rtoAlpha float64 = 0.125
rtoBeta float64 = 0.25
maxInitRetrans uint = 8
pathMaxRetrans uint = 5
noMaxRetrans uint = 0
)
type rtoManager struct {
srtt float64
rttvar float64
rto float64
noUpdate bool
mutex sync .RWMutex
rtoMax float64
}
func newRTOManager(rtoMax float64 ) *rtoManager {
mgr := rtoManager {
rto : rtoInitial ,
rtoMax : rtoMax ,
}
if mgr .rtoMax == 0 {
mgr .rtoMax = defaultRTOMax
}
return &mgr
}
func (m *rtoManager ) setNewRTT (rtt float64 ) float64 {
m .mutex .Lock ()
defer m .mutex .Unlock ()
if m .noUpdate {
return m .srtt
}
if m .srtt == 0 {
m .srtt = rtt
m .rttvar = rtt / 2
} else {
m .rttvar = (1 -rtoBeta )*m .rttvar + rtoBeta *(math .Abs (m .srtt -rtt ))
m .srtt = (1 -rtoAlpha )*m .srtt + rtoAlpha *rtt
}
m .rto = math .Min (math .Max (m .srtt +4 *m .rttvar , rtoMin ), m .rtoMax )
return m .srtt
}
func (m *rtoManager ) getRTO () float64 {
m .mutex .RLock ()
defer m .mutex .RUnlock ()
return m .rto
}
func (m *rtoManager ) reset () {
m .mutex .Lock ()
defer m .mutex .Unlock ()
if m .noUpdate {
return
}
m .srtt = 0
m .rttvar = 0
m .rto = rtoInitial
}
func (m *rtoManager ) setRTO (rto float64 , noUpdate bool ) {
m .mutex .Lock ()
defer m .mutex .Unlock ()
m .rto = rto
m .noUpdate = noUpdate
}
type rtxTimerObserver interface {
onRetransmissionTimeout(timerID int , n uint )
onRetransmissionFailure(timerID int )
}
type rtxTimerState uint8
const (
rtxTimerStopped rtxTimerState = iota
rtxTimerStarted
rtxTimerClosed
)
type rtxTimer struct {
timer *time .Timer
observer rtxTimerObserver
id int
maxRetrans uint
rtoMax float64
mutex sync .Mutex
rto float64
nRtos uint
state rtxTimerState
pending uint8
}
func newRTXTimer(id int , observer rtxTimerObserver , maxRetrans uint ,
rtoMax float64 ,
) *rtxTimer {
timer := rtxTimer {
id : id ,
observer : observer ,
maxRetrans : maxRetrans ,
rtoMax : rtoMax ,
}
if timer .rtoMax == 0 {
timer .rtoMax = defaultRTOMax
}
timer .timer = time .AfterFunc (math .MaxInt64 , timer .timeout )
timer .timer .Stop ()
return &timer
}
func (t *rtxTimer ) calculateNextTimeout () time .Duration {
timeout := calculateNextTimeout (t .rto , t .nRtos , t .rtoMax )
return time .Duration (timeout ) * time .Millisecond
}
func (t *rtxTimer ) timeout () {
t .mutex .Lock ()
if t .pending --; t .pending == 0 && t .state == rtxTimerStarted {
if t .nRtos ++; t .maxRetrans == 0 || t .nRtos <= t .maxRetrans {
t .timer .Reset (t .calculateNextTimeout ())
t .pending ++
defer t .observer .onRetransmissionTimeout (t .id , t .nRtos )
} else {
t .state = rtxTimerStopped
defer t .observer .onRetransmissionFailure (t .id )
}
}
t .mutex .Unlock ()
}
func (t *rtxTimer ) start (rto float64 ) bool {
t .mutex .Lock ()
defer t .mutex .Unlock ()
if t .state != rtxTimerStopped {
return false
}
t .rto = rto
t .nRtos = 0
t .state = rtxTimerStarted
t .pending ++
t .timer .Reset (t .calculateNextTimeout ())
return true
}
func (t *rtxTimer ) stop () {
t .mutex .Lock ()
defer t .mutex .Unlock ()
if t .state == rtxTimerStarted {
if t .timer .Stop () {
t .pending --
}
t .state = rtxTimerStopped
}
}
func (t *rtxTimer ) close () {
t .mutex .Lock ()
defer t .mutex .Unlock ()
if t .state == rtxTimerStarted && t .timer .Stop () {
t .pending --
}
t .state = rtxTimerClosed
}
func (t *rtxTimer ) isRunning () bool {
t .mutex .Lock ()
defer t .mutex .Unlock ()
return t .state == rtxTimerStarted
}
func calculateNextTimeout(rto float64 , nRtos uint , rtoMax float64 ) float64 {
if nRtos < 31 {
m := 1 << nRtos
return math .Min (rto *float64 (m ), rtoMax )
}
return rtoMax
}
The pages are generated with Golds v0.8.2 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .