Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
777 changes: 369 additions & 408 deletions Sources/SwiftNetwork/QUIC/CongestionControl.swift

Large diffs are not rendered by default.

250 changes: 123 additions & 127 deletions Sources/SwiftNetwork/QUIC/Cubic.swift

Large diffs are not rendered by default.

199 changes: 93 additions & 106 deletions Sources/SwiftNetwork/QUIC/Ledbat.swift

Large diffs are not rendered by default.

299 changes: 145 additions & 154 deletions Sources/SwiftNetwork/QUIC/Prague.swift

Large diffs are not rendered by default.

159 changes: 100 additions & 59 deletions Sources/SwiftNetwork/QUIC/QUICPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ public final class QUICPath: MultiplexingDatagramPath<

var bdp = BandwidthDelayProduct()

private var congestionControl: CongestionControl?
private var congestionControl: CongestionControl

var pacer: Pacer

Expand Down Expand Up @@ -337,6 +337,17 @@ public final class QUICPath: MultiplexingDatagramPath<
required init(parent: QUICConnection, in eventContext: inout NetworkContext.EventContext) {
self.rtt = RTT(logPrefixer: parent.logPrefixer)
self.pacer = Pacer()
// Overwritten with the real mss/qlog once `setup()` runs; RX/TX aren't allowed on a
// path until then, so this placeholder is never observed.
var congestionControlState = CongestionControlState()
let cubic = Cubic(
state: &congestionControlState,
pacer: &self.pacer,
mss: 0,
qlog: nil,
logPrefixer: parent.logPrefixer
)
self.congestionControl = CongestionControl(state: congestionControlState, algorithm: .cubic(algorithm: cubic))
super.init(parent: parent, in: &eventContext)
}

Expand Down Expand Up @@ -371,14 +382,15 @@ public final class QUICPath: MultiplexingDatagramPath<

let pacerEnabled = (pacePackets || QUICPreferences.shared.pacePackets)
self.pacer = Pacer(enabled: pacerEnabled)
self.congestionControl = .cubic(
algorithm: Cubic(
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
var congestionControlState = CongestionControlState()
let cubic = Cubic(
state: &congestionControlState,
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
self.congestionControl = CongestionControl(state: congestionControlState, algorithm: .cubic(algorithm: cubic))

self.spinValue = parentProtocol.initialSpinValue
}
Expand Down Expand Up @@ -438,42 +450,52 @@ public final class QUICPath: MultiplexingDatagramPath<
}

func resetCongestionControl() {
switch self.congestionControl {
switch self.congestionControl.algorithm {
case .cubic:
self.congestionControl = .cubic(
algorithm: Cubic(
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
var congestionControlState = CongestionControlState()
let cubic = Cubic(
state: &congestionControlState,
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
self.congestionControl = CongestionControl(
state: congestionControlState,
algorithm: .cubic(algorithm: cubic)
)
#if !NETWORK_EMBEDDED
case .ledbat:
self.congestionControl = .ledbat(
algorithm: Ledbat(
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
var congestionControlState = CongestionControlState()
let ledbat = Ledbat(
state: &congestionControlState,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
self.congestionControl = CongestionControl(
state: congestionControlState,
algorithm: .ledbat(algorithm: ledbat)
)
case .prague:
self.congestionControl = .prague(
algorithm: Prague(
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
var congestionControlState = CongestionControlState()
let prague = Prague(
state: &congestionControlState,
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
self.congestionControl = CongestionControl(
state: congestionControlState,
algorithm: .prague(algorithm: prague)
)
#endif
case .none:
break
}
}

func idleTimeoutCongestionControl() {
self.congestionControl?.idleTimeout(mss: mss)
self.congestionControl.idleTimeout(mss: mss)
}

func setupL4SState(l4sEnabled: Bool?) {
Expand All @@ -494,50 +516,69 @@ public final class QUICPath: MultiplexingDatagramPath<
func markAsBackground(_ background: Bool) {
#if !NETWORK_EMBEDDED
// Use LEDBAT for background cases
switch self.congestionControl {
switch self.congestionControl.algorithm {
case .cubic:
if !background { return } // Nothing to do, already not background
let oldState = self.congestionControl.state
var newState = CongestionControlState()
var ledbat = Ledbat(
state: &newState,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
ledbat.inherit(
from: self.congestionControl!,
from: oldState,
state: &newState,
mss: self.initialMSS,
qlog: parentProtocol.qLog
)
self.congestionControl = .ledbat(algorithm: ledbat)
self.congestionControl = CongestionControl(
state: newState,
algorithm: .ledbat(algorithm: ledbat)
)
case .ledbat:
if background { return } // Nothing to do, already background
// Inherit from the current LEDBAT so bytes in flight (and the window) carry over.
let oldState = self.congestionControl.state
var newState = CongestionControlState()
var cubic = Cubic(
state: &newState,
pacer: &self.pacer,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
cubic.inherit(
from: self.congestionControl!,
from: oldState,
state: &newState,
mss: self.initialMSS,
qlog: parentProtocol.qLog
)
self.congestionControl = .cubic(algorithm: cubic)
self.congestionControl = CongestionControl(
state: newState,
algorithm: .cubic(algorithm: cubic)
)
case .prague:
if !background { return } // Nothing to do, already not background
let oldState = self.congestionControl.state
var newState = CongestionControlState()
var ledbat = Ledbat(
state: &newState,
mss: self.initialMSS,
qlog: parentProtocol.qLog,
logPrefixer: self.log
)
ledbat.inherit(
from: self.congestionControl!,
from: oldState,
state: &newState,
mss: self.initialMSS,
qlog: parentProtocol.qLog
)
self.congestionControl = .ledbat(algorithm: ledbat)
case .none:
break
self.congestionControl = CongestionControl(
state: newState,
algorithm: .ledbat(algorithm: ledbat)
)
}
#endif
}
Expand Down Expand Up @@ -718,22 +759,22 @@ public final class QUICPath: MultiplexingDatagramPath<
extension QUICPath {
@inline(always)
var congestionControlWindow: UInt64 {
congestionControl?.congestionWindow ?? 0
congestionControl.congestionWindow
}

@inline(always)
var congestionControlAvailableCongestionWindow: UInt64 {
congestionControl?.availableCongestionWindow ?? 0
congestionControl.availableCongestionWindow
}

@inline(always)
func congestionControlCanSend(packetLength: Int) -> Bool {
congestionControl?.canSend(packetLength: packetLength) ?? false
congestionControl.canSend(packetLength: packetLength)
}

@inline(always)
func congestionControlPersistentCongestion(mss: Int, qlog: QLog? = nil) {
congestionControl?.persistentCongestion(mss: mss, qlog: qlog)
congestionControl.persistentCongestion(mss: mss, qlog: qlog)
}

@inline(always)
Expand All @@ -744,7 +785,7 @@ extension QUICPath {
packetsLost: Bool,
qlog: QLog? = nil
) {
congestionControl?.ackEnd(
congestionControl.ackEnd(
rtt: rtt,
path: self,
mss: mss,
Expand All @@ -756,12 +797,12 @@ extension QUICPath {

@inline(always)
func congestionControlPacketsSent(bytesSent: Int, qlog: QLog? = nil) {
congestionControl?.packetSent(bytesSent: bytesSent, qlog: qlog)
congestionControl.packetSent(bytesSent: bytesSent, qlog: qlog)
}

@inline(always)
func congestionControlPacketsAcked(bytesAcked: Int, sentTime: NetworkClock.Instant) {
congestionControl?.packetsAcked(bytesAcked: bytesAcked, sentTime: sentTime)
congestionControl.packetsAcked(bytesAcked: bytesAcked, sentTime: sentTime)
}

@inline(always)
Expand All @@ -773,49 +814,49 @@ extension QUICPath {
) -> Bool {
// Loss accounting doesn't repace this path, so there is no path to hand down.
let unpacedPath: QUICPath? = nil
return congestionControl?.packetsLost(
return congestionControl.packetsLost(
path: unpacedPath,
bytesLost: bytesLost,
largestLostSentTime: largestLostSentTime,
mss: mss,
smoothedRTT: smoothedRTT,
now: parentProtocol.now
) ?? false
)
}

@inline(always)
func congestionControlPacketDiscarded(bytesSent: Int, qlog: QLog? = nil) {
congestionControl?.packetDiscarded(bytesSent: bytesSent, qlog: qlog)
congestionControl.packetDiscarded(bytesSent: bytesSent, qlog: qlog)
}

@inline(always)
func congestionControlAckBegin() {
congestionControl?.ackBegin()
congestionControl.ackBegin()
}

@inline(always)
var congestionControlBytesInFlight: UInt64 {
congestionControl?.bytesInFlight ?? 0
congestionControl.bytesInFlight
}

@inline(always)
var congestionControlName: String {
congestionControl?.name ?? "none"
congestionControl.name
}

@inline(always)
func congestionControlSpuriousRetransmit(qlog: QLog? = nil) {
congestionControl?.spuriousRetransmit(qlog: qlog)
congestionControl.spuriousRetransmit(qlog: qlog)
}

@inline(always)
func congestionControlMSSChanged(mss: Int) {
congestionControl?.mssChanged(mss: mss)
congestionControl.mssChanged(mss: mss)
}

@inline(always)
func congestionControlIdleTimeout(mss: Int) {
congestionControl?.idleTimeout(mss: mss)
congestionControl.idleTimeout(mss: mss)
}

@inline(always)
Expand All @@ -831,7 +872,7 @@ extension QUICPath {
) {
// ECN accounting doesn't repace this path, so there is no path to hand down.
let unpacedPath: QUICPath? = nil
congestionControl?.processECN(
congestionControl.processECN(
path: unpacedPath,
ceCount: ceCount,
packetsAcked: packetsAcked,
Expand All @@ -847,7 +888,7 @@ extension QUICPath {

@inline(always)
func congestionControlFilloutDataTransferSnapshot(snapshot: inout DataTransferSnapshot) {
congestionControl?.filloutDataTransferSnapshot(dataTransferSnapshot: &snapshot)
congestionControl.filloutDataTransferSnapshot(dataTransferSnapshot: &snapshot)
}
}

Expand Down
Loading
Loading