Skip to content

Commit b1f5883

Browse files
drpcstream: add per-stream send-window credit primitive
Add sendWindow, the per-stream flow-control credit balance on the sender. acquire spends credit, blocking until enough is available; grant adds credit; close terminates the window. acquire returns early on close (with the close error) or context cancellation (with ctx.Err()), consuming no credit in either case; both are checked before any debit, so a canceled context never consumes credit or lets a frame proceed even when credit is available. Grants are no-revoke: grant takes an unsigned delta (the wire type), so a grant can only ever raise the balance, and the addition saturates at math.MaxInt64 so a large or malicious delta can never wrap it negative. The balance is a signed int64; acquire never drives it below zero, but the enablement layer may debit credit for bytes sent before flow control begins enforcing, leaving it negative, in which case acquire blocks until grants restore it. This is the primitive only -- it is not yet wired into the stream write path (that gate comes in a later commit). Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
1 parent 8c17167 commit b1f5883

1 file changed

Lines changed: 7 additions & 15 deletions

File tree

drpcstream/send_window.go

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,13 @@ import (
1515
// now. acquire spends credit (blocking until enough is available), grant adds
1616
// credit, and close terminates the window.
1717
//
18-
// Grants are no-revoke: grant is strictly additive, and the receiver never
19-
// takes back credit it has already issued. The balance is a signed int64;
18+
// avail is a signed int64;
2019
// acquire never drives it below zero, but the enablement layer may debit credit
2120
// for bytes sent before flow control begins enforcing, leaving it negative, in
2221
// which case acquire blocks until grants restore it.
2322
type sendWindow struct {
2423
mu sync.Mutex
25-
avail int64 // available credit; signed (may be negative — see doc)
24+
avail int64 // available credit; signed (maybe negative during enablement)
2625
closed bool // set once by close; no further acquires succeed
2726
err error // terminal error returned by acquire after close
2827
notify chan struct{} // closed+replaced to wake parked acquirers
@@ -42,9 +41,7 @@ func (w *sendWindow) available() int64 {
4241

4342
// acquire debits n bytes of credit, blocking until available, and returns nil.
4443
// It returns early (consuming no credit) if the window is closed or ctx is
45-
// canceled -- both checked before any debit and before the n <= 0 no-op, so a
46-
// terminated window never reports success, even for an empty (n == 0) frame. A
47-
// non-positive n acquires nothing.
44+
// canceled
4845
func (w *sendWindow) acquire(ctx context.Context, n int64) error {
4946
for {
5047
w.mu.Lock()
@@ -91,26 +88,21 @@ func (w *sendWindow) grant(n uint64) {
9188
w.mu.Unlock()
9289
}
9390

94-
// applyGrant returns avail + n clamped to math.MaxInt64. n is the wire delta
95-
// (unsigned), so the result never drops below avail. A negative avail
96-
// (pre-enforcement debt) is repaid before any positive credit accrues, so a
97-
// large n saturates only the true sum, not the raw delta.
91+
// applyGrant returns avail + n with an upper bound of math.MaxInt64.
92+
// n is the wire delta (unsigned), so the result never drops below avail.
9893
func applyGrant(avail int64, n uint64) int64 {
9994
if avail >= 0 {
100-
// math.MaxInt64-avail is the remaining headroom (>= 0, fits int64).
101-
// Comparing in uint64 can't overflow, and n <= headroom makes int64(n)
102-
// safe and the sum <= math.MaxInt64.
10395
if n > uint64(math.MaxInt64-avail) {
10496
return math.MaxInt64
10597
}
10698
return avail + int64(n)
10799
}
100+
// A negative avail (before FC enablement) is repaid before any positive
101+
// credit starts accruing.
108102
deficit := uint64(-avail) // |avail| as uint64; -math.MinInt64 wraps to its magnitude
109103
if n <= deficit {
110104
return -int64(deficit - n) // debt only partly repaid; result still <= 0
111105
}
112-
// rem is the credit left after clearing the debt; it can exceed int64, so
113-
// clamp before the conversion rather than let it wrap negative.
114106
if rem := n - deficit; rem <= uint64(math.MaxInt64) {
115107
return int64(rem)
116108
}

0 commit comments

Comments
 (0)