This is an automated email from the ASF dual-hosted git repository.
BewareMyPower pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pulsar-client-go.git
The following commit(s) were added to refs/heads/master by this push:
new 1796b8a7 fix: remove default case in waitWithContext to prevent
busy-spin deadlock (#1503)
1796b8a7 is described below
commit 1796b8a718bfd984ca5ceceb8a7b46226e866d8a
Author: Zixuan Liu <[email protected]>
AuthorDate: Mon Jun 1 14:19:22 2026 +0800
fix: remove default case in waitWithContext to prevent busy-spin deadlock
(#1503)
---
pulsar/internal/channel_cond.go | 3 +--
pulsar/internal/channel_cond_test.go | 35 +++++++++++++++++++++++++++++++++++
2 files changed, 36 insertions(+), 2 deletions(-)
diff --git a/pulsar/internal/channel_cond.go b/pulsar/internal/channel_cond.go
index 38301abe..94b303a1 100644
--- a/pulsar/internal/channel_cond.go
+++ b/pulsar/internal/channel_cond.go
@@ -47,6 +47,7 @@ func (c *chCond) wait() {
}
// waitWithContext Same as wait() call, but the end condition can also be
controlled through the context.
+// It blocks until either a broadcast occurs or the context is done.
func (c *chCond) waitWithContext(ctx context.Context) bool {
n := c.notifyChan()
c.L.Unlock()
@@ -56,8 +57,6 @@ func (c *chCond) waitWithContext(ctx context.Context) bool {
return true
case <-ctx.Done():
return false
- default:
- return true
}
}
diff --git a/pulsar/internal/channel_cond_test.go
b/pulsar/internal/channel_cond_test.go
index 93a04084..0a58f4fa 100644
--- a/pulsar/internal/channel_cond_test.go
+++ b/pulsar/internal/channel_cond_test.go
@@ -53,3 +53,38 @@ func TestChCondWithContext(_ *testing.T) {
cancel()
wg.Wait()
}
+
+func TestChCondWithContextBlocks(t *testing.T) {
+ // Verify that waitWithContext actually blocks (does not return via a
default case)
+ // until either a broadcast or context cancellation occurs.
+ cond := newCond(&sync.Mutex{})
+ started := make(chan struct{})
+ done := make(chan struct{})
+
+ go func() {
+ cond.L.Lock()
+ close(started)
+ cond.waitWithContext(context.Background())
+ cond.L.Unlock()
+ close(done)
+ }()
+
+ <-started
+ // Give the goroutine time to enter the select. If there were a default
case,
+ // it would return immediately and close done before the broadcast
below.
+ time.Sleep(20 * time.Millisecond)
+
+ select {
+ case <-done:
+ t.Fatal("waitWithContext returned before broadcast — default
case must have fired")
+ default:
+ }
+
+ cond.broadcast()
+
+ select {
+ case <-done:
+ case <-time.After(time.Second):
+ t.Fatal("waitWithContext did not unblock after broadcast")
+ }
+}