util/eventbus: block for the subscriber during SubscribeFunc close (#17642)

Prior to this change a SubscriberFunc treated the call to the subscriber's
function as the completion of delivery. But that means when we are closing the
subscriber, that callback could continue to execute for some time after the
close returns.

For channel-based subscribers that works OK because the close takes effect
before the subscriber ever sees the event. To make the two subscriber types
symmetric, we should also wait for the callback to finish before returning.
This ensures that a Close of the client means the same thing with both kinds of
subscriber.

Updates #17638

Change-Id: I82fd31bcaa4e92fab07981ac0e57e6e3a7d9d60b
Signed-off-by: M. J. Fromberger <fromberger@tailscale.com>
This commit is contained in:
M. J. Fromberger
2025-10-31 09:58:09 -07:00
committed by GitHub
parent 061e6266cf
commit 4c856078e4
2 changed files with 70 additions and 8 deletions
+7
View File
@@ -324,6 +324,13 @@ func (s *SubscriberFunc[T]) dispatch(ctx context.Context, vals *queue[DeliveredE
case val := <-acceptCh():
vals.Add(val)
case <-ctx.Done():
// Wait for the callback to be complete, but not forever.
s.slow.Reset(5 * slowSubscriberTimeout)
select {
case <-s.slow.C:
s.logf("giving up on subscriber for %T after %v at close", t, time.Since(start))
case <-callDone:
}
return false
case ch := <-snapshot:
ch <- vals.Snapshot()