package main import ( "sync/atomic" "golang.org/x/sys/unix" ) type txStats struct { errs atomic.Uint64 // The exclusive upper bound on any sequence number this stream can have put // on the wire. Read by the receiving half of the same stream, which is the // only thing that can tell a gap it must write off from a sequence number // that was never sent at all. sent atomic.Uint64 } type txWorker struct { fd int stream uint16 spec *frameSpec batch int stats *txStats startTx <-chan struct{} } func (w *txWorker) run(done *atomic.Bool) { // The batch is not a multiple of the pattern count, so the pairing of pattern // to size rotates every pass rather than settling into a fixed one. bufs := make([][]byte, w.batch) pats := make([]int, w.batch) for i := range bufs { bufs[i] = make([]byte, w.spec.maxSize) pats[i] = i % len(patterns) w.spec.prefill(bufs[i], pats[i]) } hdrs, iovs := newMmsghdrs(bufs) <-w.startTx var seq uint64 si := 0 for !done.Load() { for i := 0; i < w.batch; i++ { size := w.spec.sizes[si] si++ if si == len(w.spec.sizes) { si = 0 } putHeader(bufs[i], pats[i], w.stream, seq+uint64(i), size-minFrame) iovs[i].Len = uint64(size) } // Published before the send rather than after it, so it is never behind a // frame already in flight. Overshooting a partial send is harmless: it is // a bound, and one the next pass raises again. w.stats.sent.Store(seq + uint64(w.batch)) n, err := sendmmsg(w.fd, hdrs) if n > 0 { seq += uint64(n) } // Taking fewer of the vector than offered is the ring's room, not a frame // lost: the rest go on the next pass. if n < 0 && err != unix.EINTR && err != unix.EAGAIN && err != unix.ENOBUFS { w.stats.errs.Add(1) } } }