Skip to content

Commit b3a7342

Browse files
authored
pkg/tools/batcher: stop scheduler panicking when b.data is closed externally (#3714)
* pkg/tools/batcher: stop scheduler panicking when b.data is closed externally scheduler()'s defer unconditionally calls close(b.data). If the channel was closed by the caller (or an upstream producer) instead of via the normal Close()-sends-nil path, the receive on b.data returns ok == false, scheduler returns, and the deferred close(b.data) then fires on an already-closed channel: panic: close of closed channel reliably reproducible under the #3653 steps (manually closing b.data while Start() is running). Track whether we observed the external-close via a local `externallyClosed` flag set in the `ok == false` branch. The defer only closes b.data when that flag is false, i.e. when the scheduler exited through the nil-message or ticker paths and still owns the channel. No behaviour change on the graceful Close() path. Fixes #3653 * ci: retrigger after transient gha outage Signed-off-by: SAY-5 <say.apm35@gmail.com> --------- Signed-off-by: SAY-5 <say.apm35@gmail.com>
1 parent 30074d1 commit b3a7342

1 file changed

Lines changed: 11 additions & 1 deletion

File tree

pkg/tools/batcher/batcher.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,21 @@ func (b *Batcher[T]) Put(ctx context.Context, data *T) error {
151151

152152
func (b *Batcher[T]) scheduler() {
153153
ticker := time.NewTicker(b.config.interval)
154+
// Track whether b.data was closed by an external caller so the
155+
// cleanup below does not close it a second time. The only routes
156+
// out of this function that leave b.data open are the nil-message
157+
// and ticker paths; the ok == false branch means someone already
158+
// closed the channel, and calling close(b.data) again there would
159+
// panic with "close of closed channel" (#3653).
160+
externallyClosed := false
154161
defer func() {
155162
ticker.Stop()
156163
for _, ch := range b.chArrays {
157164
close(ch)
158165
}
159-
close(b.data)
166+
if !externallyClosed {
167+
close(b.data)
168+
}
160169
b.wait.Done()
161170
}()
162171

@@ -169,6 +178,7 @@ func (b *Batcher[T]) scheduler() {
169178
case data, ok := <-b.data:
170179
if !ok {
171180
// If the data channel is closed unexpectedly
181+
externallyClosed = true
172182
return
173183
}
174184
if data == nil {

0 commit comments

Comments
 (0)