Stream forever with eviction-based per-stream loss detection
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
lossSlots = 1 << 16
|
||||
lossWords = lossSlots / 64
|
||||
)
|
||||
|
||||
type lossWindow struct {
|
||||
mu sync.Mutex
|
||||
base uint64
|
||||
inited bool
|
||||
bits []uint64
|
||||
lost atomic.Uint64
|
||||
late atomic.Uint64
|
||||
_ [16]byte
|
||||
}
|
||||
|
||||
func newLossWindows(n int) []lossWindow {
|
||||
w := make([]lossWindow, n)
|
||||
for i := range w {
|
||||
w[i].bits = make([]uint64, lossWords)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// A sequence number is only judged once it falls out of the window, so frames
|
||||
// still queued in another rx worker are never miscounted as lost.
|
||||
func (w *lossWindow) observe(seq uint64) {
|
||||
w.mu.Lock()
|
||||
if !w.inited {
|
||||
// Start half a window below the first sequence seen, so frames another
|
||||
// rx worker is still holding land inside the window rather than late.
|
||||
if seq > lossSlots/2 {
|
||||
w.base = seq - lossSlots/2
|
||||
}
|
||||
w.inited = true
|
||||
}
|
||||
if seq < w.base {
|
||||
w.mu.Unlock()
|
||||
w.late.Add(1)
|
||||
return
|
||||
}
|
||||
if seq >= w.base+lossSlots {
|
||||
w.evict(seq - lossSlots + 1)
|
||||
}
|
||||
idx := seq & (lossSlots - 1)
|
||||
w.bits[idx>>6] |= 1 << (idx & 63)
|
||||
w.mu.Unlock()
|
||||
}
|
||||
|
||||
func (w *lossWindow) evict(newBase uint64) {
|
||||
span := newBase - w.base
|
||||
if span >= lossSlots {
|
||||
var missing uint64
|
||||
for i := range w.bits {
|
||||
missing += uint64(64 - bits.OnesCount64(w.bits[i]))
|
||||
w.bits[i] = 0
|
||||
}
|
||||
w.lost.Add(missing + span - lossSlots)
|
||||
w.base = newBase
|
||||
return
|
||||
}
|
||||
var missing uint64
|
||||
for s := w.base; s < newBase; s++ {
|
||||
idx := s & (lossSlots - 1)
|
||||
word, bit := idx>>6, uint64(1)<<(idx&63)
|
||||
if w.bits[word]&bit == 0 {
|
||||
missing++
|
||||
continue
|
||||
}
|
||||
w.bits[word] &^= bit
|
||||
}
|
||||
w.lost.Add(missing)
|
||||
w.base = newBase
|
||||
}
|
||||
Reference in New Issue
Block a user