@@ -17,9 +17,11 @@ const defaultRingBufferCapacity = 256
1717// assembled packet data. It sits between manageReader (producer, calls
1818// Enqueue) and the application goroutine (consumer, calls Dequeue/Done).
1919//
20- // Slots are pre-allocated and reused: each slot's backing array grows via
21- // append to fit incoming data, then stays at its high-water mark, avoiding
22- // per-message allocation in steady state.
20+ // Buffers are obtained from a shared BufferPool. Enqueue copies data into a
21+ // pooled buffer; Dequeue returns that buffer's data and advances the tail
22+ // immediately, and Done releases the buffer back to the pool. Keeping the
23+ // pool behind Dequeue/Done means the consumer does not need to know whether
24+ // the queue is backed by a pool or by fixed buffers.
2325//
2426// After Close, Dequeue drains any queued messages before returning the close
2527// error. This ensures graceful shutdown (KindClose/KindCloseSend) delivers
@@ -28,43 +30,50 @@ type ringBuffer struct {
2830 mu sync.Mutex
2931 cond sync.Cond
3032
31- buf [][]byte // ring of byte slices
32- head int // next write position (producer)
33- tail int // next read position (consumer)
34- count int // number of occupied slots
33+ pool * BufferPool // shared pool buffers are obtained from
34+ buf []* []byte // ring of pooled buffer pointers
35+ head int // next write position (producer)
36+ tail int // next read position (consumer)
37+ count int // number of occupied slots
3538
36- held bool // true between Dequeue and Done
37- err error // terminal error, set by Close
39+ held * [] byte // buffer from the last Dequeue, released by Done
40+ err error // terminal error, set by Close
3841}
3942
40- func (rb * ringBuffer ) init () {
43+ func (rb * ringBuffer ) init (pool * BufferPool ) {
4144 rb .cond .L = & rb .mu
42- rb .buf = make ([][]byte , defaultRingBufferCapacity )
45+ rb .pool = pool
46+ rb .buf = make ([]* []byte , defaultRingBufferCapacity )
4347}
4448
45- // Enqueue copies data into the next write slot. If the buffer is full, it
46- // blocks until a slot is freed or the buffer is closed. If the buffer is
47- // closed, Enqueue returns silently without enqueuing .
49+ // Enqueue copies data into a pooled buffer and places it in the next write
50+ // slot. If the buffer is full, it blocks until a slot is freed or the buffer
51+ // is closed. If the buffer is closed , Enqueue returns silently.
4852func (rb * ringBuffer ) Enqueue (data []byte ) {
53+ b := rb .pool .Get ()
54+ * b = append (* b , data ... )
55+
4956 rb .mu .Lock ()
5057 defer rb .mu .Unlock ()
5158
5259 for rb .count == len (rb .buf ) && rb .err == nil {
5360 rb .cond .Wait ()
5461 }
5562 if rb .err != nil {
63+ rb .pool .Put (b )
5664 return
5765 }
5866
59- rb .buf [rb .head ] = append ( rb . buf [ rb . head ][: 0 ], data ... )
67+ rb .buf [rb .head ] = b
6068 rb .head = (rb .head + 1 ) % len (rb .buf )
6169 rb .count ++
6270 rb .cond .Broadcast ()
6371}
6472
65- // Dequeue returns the data from the next read slot. If the buffer is empty,
66- // it blocks until data is available or the buffer is closed. The returned
67- // slice is valid until Done is called.
73+ // Dequeue returns the data from the next buffered message and advances the
74+ // tail. The returned slice is valid until Done is called, which releases the
75+ // underlying buffer back to the pool. Done must be called exactly once after
76+ // each successful Dequeue.
6877func (rb * ringBuffer ) Dequeue () ([]byte , error ) {
6978 rb .mu .Lock ()
7079 defer rb .mu .Unlock ()
@@ -76,37 +85,31 @@ func (rb *ringBuffer) Dequeue() ([]byte, error) {
7685 return nil , rb .err
7786 }
7887
79- rb .held = true
80- return rb .buf [rb .tail ], nil
81- }
82-
83- // Done advances the read pointer, making the slot available for reuse.
84- // It must be called exactly once after each successful Dequeue.
85- //
86- // TODO(shubham): remove this method once a shared buffer pool is introduced.
87- // With a pool, Dequeue will advance the tail immediately and the caller will
88- // return the buffer to the pool directly.
89- func (rb * ringBuffer ) Done () {
90- rb .mu .Lock ()
91- defer rb .mu .Unlock ()
92-
88+ b := rb .buf [rb .tail ]
89+ rb .buf [rb .tail ] = nil
9390 rb .tail = (rb .tail + 1 ) % len (rb .buf )
9491 rb .count --
95- rb .held = false
92+ rb .held = b
9693 rb .cond .Broadcast ()
94+
95+ return * b , nil
96+ }
97+
98+ // Done releases the buffer from the most recent Dequeue back to the pool,
99+ // invalidating the slice that Dequeue returned. It must be called exactly
100+ // once after each successful Dequeue. Because the queue is single-consumer,
101+ // Done is only ever called from the same goroutine as Dequeue.
102+ func (rb * ringBuffer ) Done () {
103+ rb .pool .Put (rb .held )
104+ rb .held = nil
97105}
98106
99107// Close marks the buffer as closed with the given error. All blocked Enqueue
100- // and Dequeue calls are woken and will return. Close waits for any in-progress
101- // Dequeue/Done pair to complete before setting the error. Subsequent calls are
102- // no-ops.
108+ // and Dequeue calls are woken and will return. Subsequent calls are no-ops.
103109func (rb * ringBuffer ) Close (err error ) {
104110 rb .mu .Lock ()
105111 defer rb .mu .Unlock ()
106112
107- for rb .held {
108- rb .cond .Wait ()
109- }
110113 if rb .err != nil {
111114 return
112115 }
0 commit comments