Skip to content

Commit ada39ca

Browse files
fix race in m0func not send and Yield in <-m0func
Change-Id: Ia43c1f0b7e846950326f4666f446fed6ef032f1e
1 parent 6f69909 commit ada39ca

File tree

2 files changed

+36
-29
lines changed

2 files changed

+36
-29
lines changed

src/runtime/proc.go

+35-18
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ var modinfo string
116116
var (
117117
m0 m
118118
mtPtr atomic.Pointer[mainThreadOnce]
119+
m0wait = make(chan struct{}, 1)
119120
g0 g
120121
mcache0 *mcache
121122
raceprocctx0 uintptr
@@ -127,8 +128,16 @@ func init() {
127128
}
128129

129130
type mainThreadOnce struct {
131+
// m0func pass f from a call to mainthread.Do on non-main thread to the main thread.
132+
m0func chan func()
133+
// m0wait send a signal that the non-main thread is waiting for mainthread.Yield.
134+
m0wait chan struct{}
135+
// m0exec notifies mainthread.Do when the f passed from Do on the non-main thread
136+
// to mainthread.Yield on the main thread has completed.
137+
m0exec chan struct{}
130138
//m0once make sure to only send m0wait once if mainthread.Yield is not called.
131139
m0once Once
140+
callDo atomic.Bool
132141
}
133142

134143
// copy from sync package.
@@ -152,18 +161,12 @@ func (o *Once) doSlow(f func()) {
152161
}
153162
}
154163

155-
var (
156-
// m0func pass f from a call to mainthread.Do on non-main thread to the main thread.
157-
m0func = make(chan func())
158-
// m0wait send a signal that the non-main thread is waiting for mainthread.Yield.
159-
m0wait = make(chan struct{})
160-
// m0exec notifies mainthread.Do when the f passed from Do on the non-main thread
161-
// to mainthread.Yield on the main thread has completed.
162-
m0exec = make(chan struct{}, 1)
163-
)
164-
165164
func newmainThreadInfo() *mainThreadOnce {
166-
return new(mainThreadOnce)
165+
ret := new(mainThreadOnce)
166+
ret.m0func = make(chan func())
167+
ret.m0wait = m0wait
168+
ret.m0exec = make(chan struct{}, 1)
169+
return ret
167170
}
168171

169172
// This slice records the initializing tasks that need to be
@@ -194,6 +197,7 @@ func mainThreadDo(f func()) {
194197
panic("runtime: nested call mainthread.Do")
195198
}
196199
g.inMainThreadDo = true
200+
defer func() { g.inMainThreadDo = false }()
197201
if g.m == &m0 {
198202
// lock os thread ensure that the main thread always
199203
// run only f during a call to f.
@@ -204,10 +208,11 @@ func mainThreadDo(f func()) {
204208
}
205209
mt := mtPtr.Load()
206210
mt.m0once.Do(func() {
207-
m0wait <- struct{}{}
211+
mt.callDo.Store(true)
212+
mt.m0wait <- struct{}{}
208213
})
209-
m0func <- f
210-
_ = <-m0exec
214+
mt.m0func <- f
215+
_ = <-mt.m0exec
211216
}
212217

213218
func mainThreadYield() {
@@ -224,23 +229,35 @@ func mainThreadYield() {
224229
lockOSThread()
225230
defer func() {
226231
unlockOSThread()
232+
g.inMainThreadDo = false
227233
}()
234+
i := 0
235+
mt := mtPtr.Load()
228236
for {
229237
select {
230-
case f := <-m0func:
238+
case f := <-mt.m0func:
239+
i++
231240
f()
232-
m0exec <- struct{}{}
241+
mt.m0exec <- struct{}{}
233242
default:
243+
if mt.callDo.Load() && i == 0 {
244+
// if g1 on main thread
245+
// <-mainthread.Waiting
246+
// mainthread.Yield (in now code path)
247+
// g2 on other thread
248+
// mainthread.Do (not yet send f)
249+
continue
250+
}
234251
// because there is only one main thread,
235252
// can use Store directly without considering concurrent call Yield.
236253
mtPtr.Store(newmainThreadInfo())
237254
for {
238255
select {
239256
// if there is a new send from m0func before the mtPtr is updated after
240257
// all the send from m0func has been received.
241-
case f := <-m0func:
258+
case f := <-mt.m0func:
242259
f()
243-
m0exec <- struct{}{}
260+
mt.m0exec <- struct{}{}
244261
default:
245262
return
246263
}

src/runtime/testdata/testprog/mainthread.go

+1-11
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,18 @@ import (
88
"fmt"
99
"runtime"
1010
"runtime/mainthread"
11-
"sync"
1211
)
1312

1413
func MainThread() {
15-
var wg sync.WaitGroup
16-
runtime.LockOSThread()
17-
wg.Add(1)
1814
go func() {
19-
defer wg.Done()
2015
mainthread.Do(func() { println("Ok") })
2116
}()
2217
<-mainthread.Waiting()
2318
mainthread.Yield()
24-
wg.Wait()
2519
}
2620

2721
func init() {
22+
runtime.LockOSThread()
2823
register("MainThread", func() {
2924
println("expect: Ok")
3025
MainThread()
@@ -36,16 +31,12 @@ func init() {
3631
}
3732

3833
func MainThread2() {
39-
var wg sync.WaitGroup
4034
defer func() {
4135
if err := recover(); err != nil {
4236
fmt.Print(err)
4337
}
4438
}()
45-
runtime.LockOSThread()
46-
wg.Add(1)
4739
go func() {
48-
defer wg.Done()
4940
mainthread.Do(func() {
5041
print("hello,")
5142
mainthread.Do(func() {
@@ -55,5 +46,4 @@ func MainThread2() {
5546
}()
5647
<-mainthread.Waiting()
5748
mainthread.Yield()
58-
wg.Wait()
5949
}

0 commit comments

Comments
 (0)