httpdl: drop work-stealing segment pool for one worker per segment

The pool let an idle worker steal the back half of a slower in-flight
segment, behind a mutex that also serialized every advance. At the default
-x 1 it never fired, and it carried the trickiest invariant in the tree
(minSplit >= readBuf keeps an owner's in-flight write out of the stolen tail).

makeSegments already caps the segment count at the connection count, so the
segments map one-to-one onto workers: give each its own goroutine, advance
written with a plain atomic add, and snapshot the fixed slice with no lock.
pool/newPool/acquire/steal/advance and the seg.owned field all go away.

Net -156 lines. Behaviour at the user's flags is unchanged, except a slow
mirror's tail segment is no longer rebalanced onto idle connections.
This commit is contained in:
2026-06-20 23:43:15 +09:00
parent 65104ade92
commit 270812de3e
4 changed files with 101 additions and 257 deletions

View File

@@ -28,10 +28,10 @@ type segState struct {
func controlPath(out string) string { return out + ".got" }
// snapshot builds a control record from the live segments. Callers hold the
// pool lock (see pool.snapshot) so the slice and each segment's frontier are
// stable for the read.
func snapshot(url string, total int64, etag, lastmod string, segs []*seg) control {
// snapshot builds a control record from the live segments. The segment slice is
// fixed once the file is divided and each segment's frontier is owned by one
// worker, so reading written atomically gives a consistent record with no lock.
func snapshot(url string, total int64, etag, lastmod string, segs []seg) control {
c := control{URL: url, Total: total, ETag: etag, LastModified: lastmod, Segs: make([]segState, len(segs))}
for i := range segs {
c.Segs[i] = segState{segs[i].start, segs[i].endOff(), atomic.LoadInt64(&segs[i].written)}