Skip to content

Commit e9347c7

Browse files
rui314dvyukov
authored andcommitted
sync: fix spurious wakeup from WaitGroup.Wait
There is a race condition that causes spurious wakeup from Wait in the following case: G1: decrement wg.counter, observe the counter is now 0 (should unblock goroutines queued *at this moment*) G2: increment wg.counter G2: call Wait() to add itself to the wait queue G1: acquire wg.m, unblock all waiting goroutines In the last step G2 is spuriously woken up by G1. Fixes #7734. LGTM=rsc, dvyukov R=dvyukov, 0xjnml, rsc CC=golang-codereviews https://golang.org/cl/85580043
1 parent 6278a95 commit e9347c7

File tree

2 files changed

+32
-4
lines changed

2 files changed

+32
-4
lines changed

src/pkg/sync/waitgroup.go

+6-4
Original file line numberDiff line numberDiff line change
@@ -67,11 +67,13 @@ func (wg *WaitGroup) Add(delta int) {
6767
return
6868
}
6969
wg.m.Lock()
70-
for i := int32(0); i < wg.waiters; i++ {
71-
runtime_Semrelease(wg.sema)
70+
if atomic.LoadInt32(&wg.counter) == 0 {
71+
for i := int32(0); i < wg.waiters; i++ {
72+
runtime_Semrelease(wg.sema)
73+
}
74+
wg.waiters = 0
75+
wg.sema = nil
7276
}
73-
wg.waiters = 0
74-
wg.sema = nil
7577
wg.m.Unlock()
7678
}
7779

src/pkg/sync/waitgroup_test.go

+26
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package sync_test
66

77
import (
88
. "sync"
9+
"sync/atomic"
910
"testing"
1011
)
1112

@@ -59,6 +60,31 @@ func TestWaitGroupMisuse(t *testing.T) {
5960
t.Fatal("Should panic")
6061
}
6162

63+
func TestWaitGroupRace(t *testing.T) {
64+
// Run this test for about 1ms.
65+
for i := 0; i < 1000; i++ {
66+
wg := &WaitGroup{}
67+
n := new(int32)
68+
// spawn goroutine 1
69+
wg.Add(1)
70+
go func() {
71+
atomic.AddInt32(n, 1)
72+
wg.Done()
73+
}()
74+
// spawn goroutine 2
75+
wg.Add(1)
76+
go func() {
77+
atomic.AddInt32(n, 1)
78+
wg.Done()
79+
}()
80+
// Wait for goroutine 1 and 2
81+
wg.Wait()
82+
if atomic.LoadInt32(n) != 2 {
83+
t.Fatal("Spurious wakeup from Wait")
84+
}
85+
}
86+
}
87+
6288
func BenchmarkWaitGroupUncontended(b *testing.B) {
6389
type PaddedWaitGroup struct {
6490
WaitGroup

0 commit comments

Comments
 (0)