101 lines
2.3 KiB
Go
101 lines
2.3 KiB
Go
package scheduler
|
|
|
|
// This file demonstrates how to add a custom scheduler.
|
|
// To create your own:
|
|
// 1. Copy this file and rename it (e.g., weighted_rtt.go)
|
|
// 2. Implement the three PathScheduler methods
|
|
// 3. Update the init() to register with your scheduler name
|
|
//
|
|
// The scheduler will automatically appear in --list-schedulers.
|
|
|
|
import (
|
|
"sync"
|
|
|
|
quic "github.com/AeonDave/mp-quic-go"
|
|
)
|
|
|
|
func init() {
|
|
Register("weighted", func() quic.PathScheduler {
|
|
return NewWeightedScheduler()
|
|
})
|
|
}
|
|
|
|
// WeightedScheduler is an example custom scheduler that weighs paths
|
|
// by a combination of RTT and available congestion window.
|
|
type WeightedScheduler struct {
|
|
mu sync.Mutex
|
|
quotas map[quic.PathID]uint64
|
|
}
|
|
|
|
// NewWeightedScheduler creates a new weighted scheduler.
|
|
func NewWeightedScheduler() *WeightedScheduler {
|
|
return &WeightedScheduler{
|
|
quotas: make(map[quic.PathID]uint64),
|
|
}
|
|
}
|
|
|
|
// SelectPath picks the path with the best weighted score.
|
|
// Score = (CongestionWindow - BytesInFlight) / (1 + SmoothedRTT_ms)
|
|
// Higher score = more capacity available per unit latency.
|
|
func (s *WeightedScheduler) SelectPath(paths []quic.SchedulerPathInfo, hasRetransmission bool) *quic.SchedulerPathInfo {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if len(paths) == 0 {
|
|
return nil
|
|
}
|
|
if len(paths) == 1 {
|
|
if !hasRetransmission && !paths[0].SendingAllowed {
|
|
return nil
|
|
}
|
|
return &paths[0]
|
|
}
|
|
|
|
var best *quic.SchedulerPathInfo
|
|
var bestScore float64 = -1
|
|
|
|
for i := range paths {
|
|
p := &paths[i]
|
|
if !hasRetransmission && !p.SendingAllowed {
|
|
continue
|
|
}
|
|
if p.PotentiallyFailed {
|
|
continue
|
|
}
|
|
|
|
// Available window
|
|
available := float64(0)
|
|
if p.CongestionWindow > p.BytesInFlight {
|
|
available = float64(p.CongestionWindow - p.BytesInFlight)
|
|
}
|
|
|
|
// RTT factor (ms, minimum 1 to avoid division by zero)
|
|
rttMs := float64(1)
|
|
if p.SmoothedRTT.Milliseconds() > 0 {
|
|
rttMs = float64(p.SmoothedRTT.Milliseconds())
|
|
}
|
|
|
|
score := available / rttMs
|
|
if score > bestScore {
|
|
bestScore = score
|
|
best = p
|
|
}
|
|
}
|
|
|
|
return best
|
|
}
|
|
|
|
// UpdateQuota tracks per-path send counts.
|
|
func (s *WeightedScheduler) UpdateQuota(pathID quic.PathID, packetSize quic.ByteCount) {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.quotas[pathID]++
|
|
}
|
|
|
|
// Reset clears all state.
|
|
func (s *WeightedScheduler) Reset() {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
s.quotas = make(map[quic.PathID]uint64)
|
|
}
|