http2: fix flakiness from t.Log when GOOS=js · golang/net@aad0180 (original) (raw)
`@@ -24,9 +24,10 @@ type synctestGroup struct {
`
24
24
`}
`
25
25
``
26
26
`type goroutine struct {
`
27
``
`-
id int
`
28
``
`-
parent int
`
29
``
`-
state string
`
``
27
`+
id int
`
``
28
`+
parent int
`
``
29
`+
state string
`
``
30
`+
syscall bool
`
30
31
`}
`
31
32
``
32
33
`// newSynctest creates a new group with the synthetic clock set the provided time.
`
`@@ -76,6 +77,14 @@ func (g *synctestGroup) Wait() {
`
76
77
`return
`
77
78
` }
`
78
79
`runtime.Gosched()
`
``
80
`+
if runtime.GOOS == "js" {
`
``
81
`+
// When GOOS=js, we appear to need to time.Sleep to make progress
`
``
82
`+
// on some syscalls. In particular, without this sleep
`
``
83
`+
// writing to stdout (including via t.Log) can block forever.
`
``
84
`+
for range 10 {
`
``
85
`+
time.Sleep(1)
`
``
86
`+
}
`
``
87
`+
}
`
79
88
` }
`
80
89
`}
`
81
90
``
`@@ -87,6 +96,9 @@ func (g *synctestGroup) idle() bool {
`
87
96
`if !g.gids[gr.id] && !g.gids[gr.parent] {
`
88
97
`continue
`
89
98
` }
`
``
99
`+
if gr.syscall {
`
``
100
`+
return false
`
``
101
`+
}
`
90
102
`// From runtime/runtime2.go.
`
91
103
`switch gr.state {
`
92
104
`case "IO wait":
`
`@@ -97,9 +109,6 @@ func (g *synctestGroup) idle() bool {
`
97
109
`case "chan receive":
`
98
110
`case "chan send":
`
99
111
`case "sync.Cond.Wait":
`
100
``
`-
case "sync.Mutex.Lock":
`
101
``
`-
case "sync.RWMutex.RLock":
`
102
``
`-
case "sync.RWMutex.Lock":
`
103
112
`default:
`
104
113
`return false
`
105
114
` }
`
`@@ -138,6 +147,10 @@ func stacks(all bool) []goroutine {
`
138
147
`panic(fmt.Errorf("3 unparsable goroutine stack:\n%s", gs))
`
139
148
` }
`
140
149
`state, rest, ok := strings.Cut(rest, "]")
`
``
150
`+
isSyscall := false
`
``
151
`+
if strings.Contains(rest, "\nsyscall.") {
`
``
152
`+
isSyscall = true
`
``
153
`+
}
`
141
154
`var parent int
`
142
155
`_, rest, ok = strings.Cut(rest, "\ncreated by ")
`
143
156
`if ok && strings.Contains(rest, " in goroutine ") {
`
`@@ -155,9 +168,10 @@ func stacks(all bool) []goroutine {
`
155
168
` }
`
156
169
` }
`
157
170
`goroutines = append(goroutines, goroutine{
`
158
``
`-
id: id,
`
159
``
`-
parent: parent,
`
160
``
`-
state: state,
`
``
171
`+
id: id,
`
``
172
`+
parent: parent,
`
``
173
`+
state: state,
`
``
174
`+
syscall: isSyscall,
`
161
175
` })
`
162
176
` }
`
163
177
`return goroutines
`
`@@ -291,3 +305,25 @@ func (tm *fakeTimer) Stop() bool {
`
291
305
`delete(tm.g.timers, tm)
`
292
306
`return stopped
`
293
307
`}
`
``
308
+
``
309
`+
// TestSynctestLogs verifies that t.Log works,
`
``
310
`+
// in particular that the GOOS=js workaround in synctestGroup.Wait is working.
`
``
311
`+
// (When GOOS=js, writing to stdout can hang indefinitely if some goroutine loops
`
``
312
`+
// calling runtime.Gosched; see Wait for the workaround.)
`
``
313
`+
func TestSynctestLogs(t *testing.T) {
`
``
314
`+
g := newSynctest(time.Now())
`
``
315
`+
donec := make(chan struct{})
`
``
316
`+
go func() {
`
``
317
`+
g.Join()
`
``
318
`+
for range 100 {
`
``
319
`+
t.Logf("logging a long line")
`
``
320
`+
}
`
``
321
`+
close(donec)
`
``
322
`+
}()
`
``
323
`+
g.Wait()
`
``
324
`+
select {
`
``
325
`+
case <-donec:
`
``
326
`+
default:
`
``
327
`+
panic("done")
`
``
328
`+
}
`
``
329
`+
}
`