Documentation
¶
Overview ¶
Package receive implements the receiver-side congestion control for SRT live mode.
Debug Stub (Release Build)
This file provides no-op implementations of debug assertions for release builds. All functions compile to nothing, incurring zero runtime overhead.
For debug builds with actual assertions, build with: go build -tags debug
Reference: lockless_sender_implementation_plan.md Step 7.5.2
Index ¶
- Constants
- func CalcTooRecentThreshold(now uint64, tsbpdDelay uint64, nakRecentPercent float64) uint64
- func New(recvConfig Config) congestion.Receiver
- func NewBTreePacketStore(degree int) packetStore
- func NewListPacketStore() packetStore
- type AtomicTime
- type Config
- type NAKEntry
- type NakEntryWithTime
- type RecvControlPacket
- type RecvControlPacketType
- type RecvControlRing
Constants ¶
const ( // InterPacketIntervalMinUs filters out measurement errors (sub-10µs intervals). InterPacketIntervalMinUs = 10 // InterPacketIntervalMaxUs filters out pauses (>100ms gaps aren't normal inter-packet). InterPacketIntervalMaxUs = 100_000 // InterPacketIntervalDefaultUs is the fallback when no measurement available (1ms = ~1000pps). InterPacketIntervalDefaultUs = 1000 // InterPacketEWMAAlpha is the weight for new samples (0.125 = 1/8). // Old = 0.875, New = 0.125 provides smooth convergence. InterPacketEWMAAlpha = 0.125 )
const DefaultNakConsolidationBudgetUs = 2_000 // 2ms in microseconds
DefaultNakConsolidationBudgetUs is the default time budget for NAK consolidation (2ms). This should be sufficient for consolidating thousands of NAK entries under normal conditions. If consolidation routinely exceeds this budget, it indicates a performance problem.
const DefaultPayloadSizeBytes = 1456
Variables ¶
This section is empty.
Functions ¶
func CalcTooRecentThreshold ¶
CalcTooRecentThreshold calculates the TSBPD threshold for "too recent" packets. Packets with PktTsbpdTime > threshold are considered "too recent" to NAK (they might still arrive out-of-order, not actually lost).
Formula: threshold = now + tsbpdDelay * (1.0 - nakRecentPercent)
With nakRecentPercent = 0.10 (10%):
threshold = now + tsbpdDelay * 0.90
Derivation:
- A packet with PktTsbpdTime = T arrived at (T - tsbpdDelay)
- We wait nakRecentPercent (10%) of TSBPD after arrival before NAKing
- So we NAK when: now >= (T - tsbpdDelay) + tsbpdDelay * nakRecentPercent
- Rearranging: T <= now + tsbpdDelay * (1.0 - nakRecentPercent)
- Packets with T > threshold are "too recent"
Example with tsbpdDelay=3s, nakRecentPercent=0.10:
- threshold = now + 2.7s
- Packets with TSBPD within 2.7s of now are scannable
- Packets with TSBPD more than 2.7s in the future are "too recent"
This function is exported for unit testing.
func New ¶
func New(recvConfig Config) congestion.Receiver
New takes a Config and returns a new Receiver.
func NewBTreePacketStore ¶
func NewBTreePacketStore(degree int) packetStore
NewBTreePacketStore creates a new btree-based packet store
func NewListPacketStore ¶
func NewListPacketStore() packetStore
NewListPacketStore creates a new list-based packet store
Types ¶
type AtomicTime ¶
type AtomicTime struct {
// contains filtered or unexported fields
}
AtomicTime provides atomic operations for time.Time values. Uses atomic.Value under the hood.
func (*AtomicTime) Load ¶
func (at *AtomicTime) Load() time.Time
Load atomically loads the time.Time value. Returns zero time if not yet stored.
func (*AtomicTime) Store ¶
func (at *AtomicTime) Store(t time.Time)
Store atomically stores the time.Time value.
type Config ¶
type Config struct {
InitialSequenceNumber circular.Number
PeriodicACKInterval uint64 // microseconds
PeriodicNAKInterval uint64 // microseconds
OnSendACK func(seq circular.Number, light bool)
OnSendNAK func(list []circular.Number)
OnDeliver func(p packet.Packet)
PacketReorderAlgorithm string // "list" (default) or "btree"
BTreeDegree int // B-tree degree (default: 32, only used if PacketReorderAlgorithm == "btree")
LockTimingMetrics *metrics.LockTimingMetrics // Optional lock timing metrics for performance monitoring
ConnectionMetrics *metrics.ConnectionMetrics // For atomic statistics updates
// Buffer pool for zero-copy support (Phase 2: Lockless Design)
// When provided, receiver.releasePacketFully() returns buffers to this pool
BufferPool *sync.Pool
// NAK btree configuration (Phase 4)
UseNakBtree bool // Enable NAK btree for improved out-of-order handling
SuppressImmediateNak bool // Suppress immediate NAK, let periodic NAK handle gaps
TsbpdDelay uint64 // Microseconds, for scan window calculation
NakRecentPercent float64 // Percentage of TSBPD delay for "recent" window (e.g., 0.10)
NakMergeGap uint32 // Maximum gap to merge into a single range
NakConsolidationBudget uint64 // Microseconds, time budget for consolidation
// FastNAK configuration
FastNakEnabled bool // Enable FastNAK after silence
FastNakThresholdUs uint64 // Microseconds, silence threshold to trigger FastNAK
FastNakRecentEnabled bool // Enable FastNAKRecent to detect sequence jumps
// NAK btree expiry configuration (nak_btree_expiry_optimization.md)
NakExpiryMargin float64 // Margin for expiry: threshold = now + RTO*(1+margin), default 0.10
EWMAWarmupThreshold uint32 // Min samples before EWMA trusted (0=disabled), default 32
// Light ACK configuration (Phase 5: ACK Optimization)
LightACKDifference uint32 // Send Light ACK after N packets progress (default: 64)
// Lock-free ring buffer configuration (Phase 3: Lockless Design)
// When enabled, Push() writes to ring (lock-free), Tick() drains ring before processing
UsePacketRing bool // Enable lock-free ring for packet handoff
PacketRingSize int // Ring capacity per shard (must be power of 2)
PacketRingShards int // Number of shards (must be power of 2)
PacketRingMaxRetries int // Max immediate retries before backoff
PacketRingBackoffDuration time.Duration // Delay between backoff retries
PacketRingMaxBackoffs int // Max backoff iterations (0 = unlimited)
PacketRingRetryStrategy string // Retry strategy: "", "sleep", "next", "random", "adaptive", "spin", "hybrid"
// Event loop configuration (Phase 4: Lockless Design)
// When enabled, replaces timer-driven Tick() with continuous event loop
// REQUIRES: UsePacketRing=true (event loop consumes from ring)
UseEventLoop bool // Enable continuous event loop
EventLoopRateInterval time.Duration // Rate metric calculation interval (default: 1s)
BackoffColdStartPkts int // Packets before adaptive backoff engages
BackoffMinSleep time.Duration // Minimum sleep during idle periods
BackoffMaxSleep time.Duration // Maximum sleep during idle periods
// Time base configuration (Phase 10: EventLoop Time Fix)
// When set, nowFn returns time relative to connection start, matching PktTsbpdTime.
// This is REQUIRED for EventLoop mode to correctly handle TSBPD delivery.
//
// Without these fields: nowFn returns absolute Unix time (~1.7 trillion µs)
// With these fields: nowFn returns TsbpdTimeBase + elapsed_since_StartTime
//
// PktTsbpdTime is calculated as: tsbpdTimeBase + timestamp + tsbpdDelay
// For correct TSBPD comparison, nowFn must use the same time base.
TsbpdTimeBase uint64 // Time base from connection (same as c.tsbpdTimeBase)
StartTime time.Time // Connection start time (same as c.start)
// Debug logging (for investigation)
// LogFunc is called for debug logging following the gosrt pattern:
// LogFunc("receiver:nak:debug", func() string { return "message" })
Debug bool // Enable debug logging
LogFunc func(string, func() string) // Logging callback (lazy evaluation)
// ProcessConnectionControlPackets is called by EventLoop to process
// connection-level control packets (ACKACK, KEEPALIVE).
// Returns number of packets processed.
// Set by connection.go to c.drainRecvControlRing.
// This allows control packets to be processed inline in EventLoop
// rather than by a separate polling goroutine, eliminating latency.
ProcessConnectionControlPackets func() int
}
Config is the configuration for the liveRecv congestion control
type NAKEntry ¶
NAKEntry represents either a single sequence or a range.
type NakEntryWithTime ¶
type NakEntryWithTime struct {
Seq uint32 // Missing sequence number
TsbpdTimeUs uint64 // TSBPD release time for this sequence (microseconds) - for expiry
LastNakedAtUs uint64 // When we last sent NAK for this seq (microseconds) - for suppression
NakCount uint32 // Number of times NAK'd
}
NakEntryWithTime stores a missing sequence number with timing information. Used in NAK btree to track: - When the packet should be delivered (TsbpdTimeUs) - for RTT-aware expiry - When we last sent NAK (LastNakedAtUs) - for NAK suppression - How many times NAK'd (NakCount) - for metrics
Phase 6: RTO Suppression - prevents redundant NAKs within RTO window. Phase 7: NAK Btree Expiry Optimization - RTT-aware early expiry (nak_btree_expiry_optimization.md)
type RecvControlPacket ¶
type RecvControlPacket struct {
// Type identifies the control packet type
Type RecvControlPacketType
// ACKNumber is the ACK number being acknowledged (for ACKACK only)
ACKNumber uint32
// Timestamp is the arrival time in nanoseconds (time.Now().UnixNano())
// Captured at push time to ensure accurate RTT calculation
Timestamp int64
}
RecvControlPacket wraps an ACKACK or KEEPALIVE for ring transport. This is a value type (not pointer) to avoid allocations in the hot path.
func (RecvControlPacket) ArrivalTime ¶
func (p RecvControlPacket) ArrivalTime() time.Time
ArrivalTime returns the Timestamp as a time.Time
type RecvControlPacketType ¶
type RecvControlPacketType uint8
RecvControlPacketType identifies the type of receiver control packet
const ( // RecvControlTypeACKACK is an ACKACK packet for RTT calculation RecvControlTypeACKACK RecvControlPacketType = iota // RecvControlTypeKEEPALIVE is a KEEPALIVE packet for connection maintenance RecvControlTypeKEEPALIVE )
func (RecvControlPacketType) String ¶
func (t RecvControlPacketType) String() string
String returns the string representation of the control packet type
type RecvControlRing ¶
type RecvControlRing struct {
*common.ControlRing[RecvControlPacket]
}
RecvControlRing wraps the generic control ring for receiver control packets. Push*() is called from io_uring completion handlers (multiple goroutines). TryPop() is called from EventLoop (single consumer).
func NewRecvControlRing ¶
func NewRecvControlRing(size, shards int) (*RecvControlRing, error)
NewRecvControlRing creates a receiver control ring.
Parameters:
- size: per-shard capacity (default: 128)
- shards: number of shards (default: 1)
Reference: completely_lockfree_receiver.md Section 6.1.3
func (*RecvControlRing) PushACKACK ¶
func (r *RecvControlRing) PushACKACK(ackNum uint32, arrivalTime time.Time) bool
PushACKACK pushes an ACKACK to the control ring. Thread-safe: can be called from multiple goroutines (io_uring handlers). Returns true if successful, false if ring is full.
The arrivalTime is captured at push time to ensure accurate RTT calculation. This is critical because the EventLoop may process the packet later, and using time.Now() at processing time would include queueing delay.
func (*RecvControlRing) PushKEEPALIVE ¶
func (r *RecvControlRing) PushKEEPALIVE() bool
PushKEEPALIVE pushes a KEEPALIVE to the control ring. Thread-safe: can be called from multiple goroutines (io_uring handlers). Returns true if successful, false if ring is full.