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

`+

}

`