@@ -116,6 +116,7 @@ var modinfo string
116
116
var (
117
117
m0 m
118
118
mtPtr atomic.Pointer [mainThreadOnce ]
119
+ m0wait = make (chan struct {}, 1 )
119
120
g0 g
120
121
mcache0 * mcache
121
122
raceprocctx0 uintptr
@@ -127,8 +128,16 @@ func init() {
127
128
}
128
129
129
130
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 {}
130
138
//m0once make sure to only send m0wait once if mainthread.Yield is not called.
131
139
m0once Once
140
+ callDo atomic.Bool
132
141
}
133
142
134
143
// copy from sync package.
@@ -152,18 +161,12 @@ func (o *Once) doSlow(f func()) {
152
161
}
153
162
}
154
163
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
-
165
164
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
167
170
}
168
171
169
172
// This slice records the initializing tasks that need to be
@@ -194,6 +197,7 @@ func mainThreadDo(f func()) {
194
197
panic ("runtime: nested call mainthread.Do" )
195
198
}
196
199
g .inMainThreadDo = true
200
+ defer func () { g .inMainThreadDo = false }()
197
201
if g .m == & m0 {
198
202
// lock os thread ensure that the main thread always
199
203
// run only f during a call to f.
@@ -204,10 +208,11 @@ func mainThreadDo(f func()) {
204
208
}
205
209
mt := mtPtr .Load ()
206
210
mt .m0once .Do (func () {
207
- m0wait <- struct {}{}
211
+ mt .callDo .Store (true )
212
+ mt .m0wait <- struct {}{}
208
213
})
209
- m0func <- f
210
- _ = <- m0exec
214
+ mt . m0func <- f
215
+ _ = <- mt . m0exec
211
216
}
212
217
213
218
func mainThreadYield () {
@@ -224,23 +229,35 @@ func mainThreadYield() {
224
229
lockOSThread ()
225
230
defer func () {
226
231
unlockOSThread ()
232
+ g .inMainThreadDo = false
227
233
}()
234
+ i := 0
235
+ mt := mtPtr .Load ()
228
236
for {
229
237
select {
230
- case f := <- m0func :
238
+ case f := <- mt .m0func :
239
+ i ++
231
240
f ()
232
- m0exec <- struct {}{}
241
+ mt . m0exec <- struct {}{}
233
242
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
+ }
234
251
// because there is only one main thread,
235
252
// can use Store directly without considering concurrent call Yield.
236
253
mtPtr .Store (newmainThreadInfo ())
237
254
for {
238
255
select {
239
256
// if there is a new send from m0func before the mtPtr is updated after
240
257
// all the send from m0func has been received.
241
- case f := <- m0func :
258
+ case f := <- mt . m0func :
242
259
f ()
243
- m0exec <- struct {}{}
260
+ mt . m0exec <- struct {}{}
244
261
default :
245
262
return
246
263
}
0 commit comments