Skip to content
Open
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
78 changes: 58 additions & 20 deletions Sources/SwiftNetwork/Protocols/IPProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,8 @@ public struct IPProtocol: NetworkProtocol {

mutating func appendReassembledPackets(
_ log: borrowing NetworkLoggerState,
reassembled: inout FrameArray
reassembled: inout FrameArray,
now: NetworkClock.Instant
) {
guard let empty = reassemblyState?.inputReassemblyFrames.isEmpty, !empty else {
return
Expand Down Expand Up @@ -623,7 +624,7 @@ public struct IPProtocol: NetworkProtocol {
}
newFrame.metadataComplete = true
if self.flags.calculateReceiveTime {
newFrame.timestamp = Frame.FrameTimestamp.receiveTime(.now)
newFrame.timestamp = Frame.FrameTimestamp.receiveTime(now)
}
reassembled.add(frame: newFrame)

Expand All @@ -637,13 +638,14 @@ public struct IPProtocol: NetworkProtocol {
_ log: borrowing NetworkLoggerState,
ipID: UInt16,
reassembled: inout FrameArray,
forceFlush: Bool
forceFlush: Bool,
now: NetworkClock.Instant
) {
let hasAccumulatedFragments = reassemblyState?.inputReassemblyFrames.isEmpty == false
let isNewID = reassemblyState?.reassemblyID != ipID

if hasAccumulatedFragments && (isNewID || forceFlush) {
appendReassembledPackets(log, reassembled: &reassembled)
appendReassembledPackets(log, reassembled: &reassembled, now: now)
// Only discard buffered fragments when the IP ID changes
if isNewID && !forceFlush {
var dropped = 0
Expand Down Expand Up @@ -671,7 +673,11 @@ public struct IPProtocol: NetworkProtocol {
}
}

mutating func processInboundFrames(_ log: borrowing NetworkLoggerState, _ inboundFrames: inout FrameArray) {
mutating func processInboundFrames(
_ log: borrowing NetworkLoggerState,
_ inboundFrames: inout FrameArray,
now: NetworkClock.Instant
) {
let localAddress: UInt32 = self.localAddress.addressValue
let remoteAddress: UInt32 = self.remoteAddress.addressValue
let mask = (0xF000_0000 as UInt32).bigEndian
Expand Down Expand Up @@ -784,7 +790,7 @@ public struct IPProtocol: NetworkProtocol {
break
}
if self.flags.calculateReceiveTime {
frame.timestamp = Frame.FrameTimestamp.receiveTime(.now)
frame.timestamp = Frame.FrameTimestamp.receiveTime(now)
}
if self.flags.receiveHopLimit {
frame.hopLimit = ttl
Expand Down Expand Up @@ -842,7 +848,13 @@ public struct IPProtocol: NetworkProtocol {
continue
}

processReassembly(log, ipID: identifier, reassembled: &reassembledFragments, forceFlush: false)
processReassembly(
log,
ipID: identifier,
reassembled: &reassembledFragments,
forceFlush: false,
now: now
)
let currentFragmentCount = reassemblyState?.inputReassemblyFrames.count ?? 0
guard currentFragmentCount < IPMaxFragmentCount else {
frame.finalize(success: false)
Expand Down Expand Up @@ -896,7 +908,7 @@ public struct IPProtocol: NetworkProtocol {
}
self.counters.rxPackets += 1
}
processReassembly(log, ipID: 0, reassembled: &reassembledFragments, forceFlush: true)
processReassembly(log, ipID: 0, reassembled: &reassembledFragments, forceFlush: true, now: now)
processedFrames.add(frames: reassembledFragments)
inboundFrames.add(frames: processedFrames)
}
Expand Down Expand Up @@ -1245,7 +1257,8 @@ public struct IPProtocol: NetworkProtocol {

mutating func appendReassembledPackets(
_ log: borrowing NetworkLoggerState,
reassembled: inout FrameArray
reassembled: inout FrameArray,
now: NetworkClock.Instant
) {
guard let empty = reassemblyState?.inputReassemblyFrames.isEmpty, !empty else {
return
Expand Down Expand Up @@ -1381,7 +1394,7 @@ public struct IPProtocol: NetworkProtocol {
newFrame.hopLimit = firstHopLimit
}
if self.flags.calculateReceiveTime {
newFrame.timestamp = Frame.FrameTimestamp.receiveTime(.now)
newFrame.timestamp = Frame.FrameTimestamp.receiveTime(now)
}
newFrame.metadataComplete = true
reassembled.add(frame: newFrame)
Expand All @@ -1396,13 +1409,14 @@ public struct IPProtocol: NetworkProtocol {
_ log: borrowing NetworkLoggerState,
fragmentID: UInt32,
reassembled: inout FrameArray,
forceFlush: Bool
forceFlush: Bool,
now: NetworkClock.Instant
) {
let hasAccumulatedFragments = reassemblyState?.inputReassemblyFrames.isEmpty == false
let isNewID = reassemblyState?.reassemblyID != fragmentID

if hasAccumulatedFragments && (isNewID || forceFlush) {
appendReassembledPackets(log, reassembled: &reassembled)
appendReassembledPackets(log, reassembled: &reassembled, now: now)
// Only discard buffered fragments when the IP ID change
if isNewID && !forceFlush {
var dropped = 0
Expand All @@ -1426,7 +1440,11 @@ public struct IPProtocol: NetworkProtocol {
}
}

mutating func processInboundFrames(_ log: borrowing NetworkLoggerState, _ inboundFrames: inout FrameArray) {
mutating func processInboundFrames(
_ log: borrowing NetworkLoggerState,
_ inboundFrames: inout FrameArray,
now: NetworkClock.Instant
) {

let localAddress = self.localAddress.addressValue
let remoteAddress = self.remoteAddress.addressValue
Expand Down Expand Up @@ -1558,7 +1576,7 @@ public struct IPProtocol: NetworkProtocol {
break
}
if self.flags.calculateReceiveTime {
frame.timestamp = Frame.FrameTimestamp.receiveTime(.now)
frame.timestamp = Frame.FrameTimestamp.receiveTime(now)
}
if self.flags.receiveHopLimit {
frame.hopLimit = hopLimit
Expand Down Expand Up @@ -1639,7 +1657,8 @@ public struct IPProtocol: NetworkProtocol {
log,
fragmentID: fragmentID,
reassembled: &reassembledFragments,
forceFlush: false
forceFlush: false,
now: now
)

let currentFragmentCount = reassemblyState?.inputReassemblyFrames.count ?? 0
Expand Down Expand Up @@ -1685,7 +1704,13 @@ public struct IPProtocol: NetworkProtocol {
}
self.counters.rxPackets += 1
}
processReassembly(log, fragmentID: 0, reassembled: &reassembledFragments, forceFlush: true)
processReassembly(
log,
fragmentID: 0,
reassembled: &reassembledFragments,
forceFlush: true,
now: now
)
processedFrames.add(frames: reassembledFragments)
inboundFrames.add(frames: processedFrames)
}
Expand Down Expand Up @@ -2015,7 +2040,12 @@ public struct IPProtocol: NetworkProtocol {
guard var inboundFrames, !inboundFrames.isEmpty else {
return nil
}
IPInstance.processInbound(&self.instanceType, log: self.log, frames: &inboundFrames)
IPInstance.processInbound(
&self.instanceType,
log: self.log,
frames: &inboundFrames,
now: NetworkClock.Instant.now
)
guard !inboundFrames.isEmpty else {
log.error("Dropped inbound packets, checking for more")
continue
Expand Down Expand Up @@ -2060,18 +2090,26 @@ public struct IPProtocol: NetworkProtocol {
try invokeSendDatagrams(datagrams)
}

/// - Parameter now: Read lazily, and only when something will use it. Every consumer of
/// this instant is behind `calculateReceiveTime`, so reading the clock unconditionally
/// would charge a clock read to every inbound batch of a stack that never asks for
/// receive timestamps. Resolved once here, so the frames of a batch still share one
/// instant.
@inline(__always)
private static func processInbound(
_ instanceType: inout IPInstanceType,
log: borrowing NetworkLoggerState,
frames: inout FrameArray
frames: inout FrameArray,
now: @autoclosure () -> NetworkClock.Instant
) {
switch instanceType {
case .ipv4(var instance):
instance.processInboundFrames(log, &frames)
let receiveTime = instance.flags.calculateReceiveTime ? now() : .zero
instance.processInboundFrames(log, &frames, now: receiveTime)
instanceType = .ipv4(instance)
case .ipv6(var instance):
instance.processInboundFrames(log, &frames)
let receiveTime = instance.flags.calculateReceiveTime ? now() : .zero
instance.processInboundFrames(log, &frames, now: receiveTime)
instanceType = .ipv6(instance)
}
}
Expand Down
9 changes: 5 additions & 4 deletions Sources/SwiftNetwork/QUIC/Ack.swift
Original file line number Diff line number Diff line change
Expand Up @@ -551,7 +551,7 @@ struct Ack: ~Copyable, PrefixedLoggable, NonCopyableTimerUser {
mutating func append(
packetNumberSpace: PacketNumberSpace,
packetNumber: PacketNumber,
now: NetworkClock.Instant = .now
now: NetworkClock.Instant
) {
withAckSpace(packetNumberSpace: packetNumberSpace) { ackSpace in
ackSpace.append(packetNumber, packetNumberSpace: packetNumberSpace, now: now)
Expand Down Expand Up @@ -590,7 +590,7 @@ struct Ack: ~Copyable, PrefixedLoggable, NonCopyableTimerUser {
isAckSet: Bool,
setAckFrame: (PacketNumberSpace, consuming QUICFrame, Bool) -> Void,
ecnCounter: ECNCounter?,
now: NetworkClock.Instant = .now
now: NetworkClock.Instant
) -> Bool {
var shouldSend = false
if isAckSet {
Expand Down Expand Up @@ -1237,7 +1237,8 @@ extension Ack {
mutating func buildForTesting(
for packetNumberSpace: PacketNumberSpace,
setAckFrame: (PacketNumberSpace, consuming QUICFrame, Bool) -> Void,
ecnCounter: ECNCounter? = nil
ecnCounter: ECNCounter? = nil,
now: NetworkClock.Instant
) -> Int {
var size = 0
let delayExponent = localDelayExponent
Expand All @@ -1249,7 +1250,7 @@ extension Ack {
delaySize: delaySize,
setAckFrame: setAckFrame,
ecnCounter: ecnCounter,
now: .now
now: now
)
return true
}
Expand Down
40 changes: 27 additions & 13 deletions Sources/SwiftNetwork/QUIC/CongestionControl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,19 @@ enum CongestionControl {
path: QUICPath?,
mss: Int,
packetsLost: Bool,
now: NetworkClock.Instant,
qlog: QLog? = nil
) {
switch self {
case .cubic(algorithm: var cubic):
cubic.ackEnd(rtt: rtt, path: path, mss: mss, packetsLost: packetsLost, qlog: qlog)
cubic.ackEnd(rtt: rtt, path: path, mss: mss, packetsLost: packetsLost, now: now, qlog: qlog)
self = .cubic(algorithm: cubic)
#if !NETWORK_EMBEDDED
case .ledbat(algorithm: var ledbat):
ledbat.ackEnd(rtt: rtt, path: path, mss: mss, packetsLost: packetsLost, qlog: qlog)
ledbat.ackEnd(rtt: rtt, path: path, mss: mss, packetsLost: packetsLost, now: now, qlog: qlog)
self = .ledbat(algorithm: ledbat)
case .prague(algorithm: var prague):
prague.ackEnd(rtt: rtt, path: path, mss: mss, packetsLost: packetsLost, qlog: qlog)
prague.ackEnd(rtt: rtt, path: path, mss: mss, packetsLost: packetsLost, now: now, qlog: qlog)
self = .prague(algorithm: prague)
#endif
}
Expand Down Expand Up @@ -145,15 +146,17 @@ enum CongestionControl {
bytesLost: Int,
largestLostSentTime: NetworkClock.Instant,
mss: Int,
smoothedRTT: NetworkDuration
smoothedRTT: NetworkDuration,
now: NetworkClock.Instant
) -> Bool {
switch self {
case .cubic(algorithm: var cubic):
let reducedCongestionWindow = cubic.packetLost(
bytesLost: bytesLost,
largestLostSentTime: largestLostSentTime,
mss: mss,
smoothedRTT: smoothedRTT
smoothedRTT: smoothedRTT,
now: now
)
self = .cubic(algorithm: cubic)
return reducedCongestionWindow
Expand All @@ -163,7 +166,8 @@ enum CongestionControl {
bytesLost: bytesLost,
largestLostSentTime: largestLostSentTime,
mss: mss,
smoothedRTT: smoothedRTT
smoothedRTT: smoothedRTT,
now: now
)
self = .ledbat(algorithm: ledbat)
return reducedCongestionWindow
Expand All @@ -172,7 +176,8 @@ enum CongestionControl {
bytesLost: bytesLost,
largestLostSentTime: largestLostSentTime,
mss: mss,
smoothedRTT: smoothedRTT
smoothedRTT: smoothedRTT,
now: now
)
self = .prague(algorithm: prague)
return reducedCongestionWindow
Expand Down Expand Up @@ -329,11 +334,12 @@ protocol CongestionControlProtocol: PrefixedLoggable {
path: QUICPath?,
mss: Int,
packetsLost: Bool,
now: NetworkClock.Instant,
qlog: QLog?
)
mutating func spuriousRetransmit(qlog: QLog?)
mutating func idleTimeout(mss: Int, qlog: QLog?)
mutating func enterRecovery(mss: Int, qlog: QLog?)
mutating func enterRecovery(mss: Int, now: NetworkClock.Instant, qlog: QLog?)
mutating func processECN(
path: QUICPath?,
ceCount: Int,
Expand All @@ -343,6 +349,7 @@ protocol CongestionControlProtocol: PrefixedLoggable {
largestAckedSentTime: NetworkClock.Instant,
mss: Int,
smoothedRTT: NetworkDuration,
now: NetworkClock.Instant,
qlog: QLog?
)
mutating func packetLost(
Expand All @@ -351,9 +358,15 @@ protocol CongestionControlProtocol: PrefixedLoggable {
largestLostSentTime: NetworkClock.Instant,
mss: Int,
smoothedRTT: NetworkDuration,
now: NetworkClock.Instant,
qlog: QLog?
) -> Bool
mutating func linkFlowControl(largestAckSentTime: NetworkClock.Instant, mss: Int, qlog: QLog?)
mutating func linkFlowControl(
largestAckSentTime: NetworkClock.Instant,
mss: Int,
now: NetworkClock.Instant,
qlog: QLog?
)
mutating func persistentCongestion(mss: Int, qlog: QLog?)
mutating func mssChanged(mss: Int, qlog: QLog?)
mutating func packetDiscarded(bytesSent: Int, qlog: QLog?)
Expand Down Expand Up @@ -469,22 +482,24 @@ extension CongestionControlProtocol {
mutating func congestionEvent(
sentTime: NetworkClock.Instant,
mss: Int,
now: NetworkClock.Instant,
qlog: QLog? = nil
) -> Bool {
// If the packet was sent before recovery started, do nothing
if packetInRecovery(sentTime: sentTime) { return false }
// Enter recovery if the packet was sent
// after start of the previous recovery period
enterRecovery(mss: mss, qlog: qlog)
enterRecovery(mss: mss, now: now, qlog: qlog)
return true
}

mutating func linkFlowControl(
largestAckSentTime: NetworkClock.Instant,
mss: Int,
now: NetworkClock.Instant,
qlog: QLog? = nil
) {
congestionEvent(sentTime: largestAckSentTime, mss: mss, qlog: qlog)
congestionEvent(sentTime: largestAckSentTime, mss: mss, now: now, qlog: qlog)
log.debug(
"Link was flow controlled, reduced congestion window is \(congestionWindow) bytes"
)
Expand Down Expand Up @@ -526,8 +541,7 @@ extension CongestionControlProtocol {
}
}

mutating func revalidateCongestionWindow(smoothedRTT: NetworkDuration) -> Bool {
let now = NetworkClock.Instant.now
mutating func revalidateCongestionWindow(smoothedRTT: NetworkDuration, now: NetworkClock.Instant) -> Bool {
if pipeAckSampleEnd == .zero {
pipeAckNewRound(target: now.advanced(by: smoothedRTT))
}
Expand Down
Loading