...
Source file src/runtime/mgcwork.go
1
2
3
4
5 package runtime
6
7 import (
8 "runtime/internal/atomic"
9 "runtime/internal/sys"
10 "unsafe"
11 )
12
13 const (
14 _WorkbufSize = 2048
15
16
17
18
19
20
21
22 workbufAlloc = 32 << 10
23 )
24
25
26
27
28
29
30 var throwOnGCWork bool
31
32 func init() {
33 if workbufAlloc%pageSize != 0 || workbufAlloc%_WorkbufSize != 0 {
34 throw("bad workbufAlloc")
35 }
36 }
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62 type gcWork struct {
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81 wbuf1, wbuf2 *workbuf
82
83
84
85 bytesMarked uint64
86
87
88
89 scanWork int64
90
91
92
93
94
95 flushedWork bool
96
97
98
99 pauseGen uint32
100
101
102 putGen uint32
103
104
105
106 pauseStack [16]uintptr
107 }
108
109
110
111
112
113
114
115
116 func (w *gcWork) init() {
117 w.wbuf1 = getempty()
118 wbuf2 := trygetfull()
119 if wbuf2 == nil {
120 wbuf2 = getempty()
121 }
122 w.wbuf2 = wbuf2
123 }
124
125 func (w *gcWork) checkPut(ptr uintptr, ptrs []uintptr) {
126 if debugCachedWork {
127 alreadyFailed := w.putGen == w.pauseGen
128 w.putGen = w.pauseGen
129 if m := getg().m; m.locks > 0 || m.mallocing != 0 || m.preemptoff != "" || m.p.ptr().status != _Prunning {
130
131
132
133
134
135 return
136 }
137 for atomic.Load(&gcWorkPauseGen) == w.pauseGen {
138 }
139 if throwOnGCWork {
140 printlock()
141 if alreadyFailed {
142 println("runtime: checkPut already failed at this generation")
143 }
144 println("runtime: late gcWork put")
145 if ptr != 0 {
146 gcDumpObject("ptr", ptr, ^uintptr(0))
147 }
148 for _, ptr := range ptrs {
149 gcDumpObject("ptrs", ptr, ^uintptr(0))
150 }
151 println("runtime: paused at")
152 for _, pc := range w.pauseStack {
153 if pc == 0 {
154 break
155 }
156 f := findfunc(pc)
157 if f.valid() {
158
159
160
161
162
163 printAncestorTracebackFuncInfo(f, pc)
164 } else {
165 println("\tunknown PC ", hex(pc), "\n")
166 }
167 }
168 throw("throwOnGCWork")
169 }
170 }
171 }
172
173
174
175
176 func (w *gcWork) put(obj uintptr) {
177 w.checkPut(obj, nil)
178
179 flushed := false
180 wbuf := w.wbuf1
181 if wbuf == nil {
182 w.init()
183 wbuf = w.wbuf1
184
185 } else if wbuf.nobj == len(wbuf.obj) {
186 w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
187 wbuf = w.wbuf1
188 if wbuf.nobj == len(wbuf.obj) {
189 putfull(wbuf)
190 w.flushedWork = true
191 wbuf = getempty()
192 w.wbuf1 = wbuf
193 flushed = true
194 }
195 }
196
197 wbuf.obj[wbuf.nobj] = obj
198 wbuf.nobj++
199
200
201
202
203
204 if flushed && gcphase == _GCmark {
205 gcController.enlistWorker()
206 }
207 }
208
209
210
211
212 func (w *gcWork) putFast(obj uintptr) bool {
213 w.checkPut(obj, nil)
214
215 wbuf := w.wbuf1
216 if wbuf == nil {
217 return false
218 } else if wbuf.nobj == len(wbuf.obj) {
219 return false
220 }
221
222 wbuf.obj[wbuf.nobj] = obj
223 wbuf.nobj++
224 return true
225 }
226
227
228
229
230
231 func (w *gcWork) putBatch(obj []uintptr) {
232 if len(obj) == 0 {
233 return
234 }
235
236 w.checkPut(0, obj)
237
238 flushed := false
239 wbuf := w.wbuf1
240 if wbuf == nil {
241 w.init()
242 wbuf = w.wbuf1
243 }
244
245 for len(obj) > 0 {
246 for wbuf.nobj == len(wbuf.obj) {
247 putfull(wbuf)
248 w.flushedWork = true
249 w.wbuf1, w.wbuf2 = w.wbuf2, getempty()
250 wbuf = w.wbuf1
251 flushed = true
252 }
253 n := copy(wbuf.obj[wbuf.nobj:], obj)
254 wbuf.nobj += n
255 obj = obj[n:]
256 }
257
258 if flushed && gcphase == _GCmark {
259 gcController.enlistWorker()
260 }
261 }
262
263
264
265
266
267
268
269 func (w *gcWork) tryGet() uintptr {
270 wbuf := w.wbuf1
271 if wbuf == nil {
272 w.init()
273 wbuf = w.wbuf1
274
275 }
276 if wbuf.nobj == 0 {
277 w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
278 wbuf = w.wbuf1
279 if wbuf.nobj == 0 {
280 owbuf := wbuf
281 wbuf = trygetfull()
282 if wbuf == nil {
283 return 0
284 }
285 putempty(owbuf)
286 w.wbuf1 = wbuf
287 }
288 }
289
290 wbuf.nobj--
291 return wbuf.obj[wbuf.nobj]
292 }
293
294
295
296
297
298 func (w *gcWork) tryGetFast() uintptr {
299 wbuf := w.wbuf1
300 if wbuf == nil {
301 return 0
302 }
303 if wbuf.nobj == 0 {
304 return 0
305 }
306
307 wbuf.nobj--
308 return wbuf.obj[wbuf.nobj]
309 }
310
311
312
313
314
315
316
317
318 func (w *gcWork) dispose() {
319 if wbuf := w.wbuf1; wbuf != nil {
320 if wbuf.nobj == 0 {
321 putempty(wbuf)
322 } else {
323 putfull(wbuf)
324 w.flushedWork = true
325 }
326 w.wbuf1 = nil
327
328 wbuf = w.wbuf2
329 if wbuf.nobj == 0 {
330 putempty(wbuf)
331 } else {
332 putfull(wbuf)
333 w.flushedWork = true
334 }
335 w.wbuf2 = nil
336 }
337 if w.bytesMarked != 0 {
338
339
340
341
342 atomic.Xadd64(&work.bytesMarked, int64(w.bytesMarked))
343 w.bytesMarked = 0
344 }
345 if w.scanWork != 0 {
346 atomic.Xaddint64(&gcController.scanWork, w.scanWork)
347 w.scanWork = 0
348 }
349 }
350
351
352
353
354 func (w *gcWork) balance() {
355 if w.wbuf1 == nil {
356 return
357 }
358 if wbuf := w.wbuf2; wbuf.nobj != 0 {
359 w.checkPut(0, wbuf.obj[:wbuf.nobj])
360 putfull(wbuf)
361 w.flushedWork = true
362 w.wbuf2 = getempty()
363 } else if wbuf := w.wbuf1; wbuf.nobj > 4 {
364 w.checkPut(0, wbuf.obj[:wbuf.nobj])
365 w.wbuf1 = handoff(wbuf)
366 w.flushedWork = true
367 } else {
368 return
369 }
370
371 if gcphase == _GCmark {
372 gcController.enlistWorker()
373 }
374 }
375
376
377
378 func (w *gcWork) empty() bool {
379 return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0)
380 }
381
382
383
384
385
386 type workbufhdr struct {
387 node lfnode
388 nobj int
389 }
390
391
392 type workbuf struct {
393 workbufhdr
394
395 obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / sys.PtrSize]uintptr
396 }
397
398
399
400
401
402
403 func (b *workbuf) checknonempty() {
404 if b.nobj == 0 {
405 throw("workbuf is empty")
406 }
407 }
408
409 func (b *workbuf) checkempty() {
410 if b.nobj != 0 {
411 throw("workbuf is not empty")
412 }
413 }
414
415
416
417
418 func getempty() *workbuf {
419 var b *workbuf
420 if work.empty != 0 {
421 b = (*workbuf)(work.empty.pop())
422 if b != nil {
423 b.checkempty()
424 }
425 }
426 if b == nil {
427
428 var s *mspan
429 if work.wbufSpans.free.first != nil {
430 lock(&work.wbufSpans.lock)
431 s = work.wbufSpans.free.first
432 if s != nil {
433 work.wbufSpans.free.remove(s)
434 work.wbufSpans.busy.insert(s)
435 }
436 unlock(&work.wbufSpans.lock)
437 }
438 if s == nil {
439 systemstack(func() {
440 s = mheap_.allocManual(workbufAlloc/pageSize, &memstats.gc_sys)
441 })
442 if s == nil {
443 throw("out of memory")
444 }
445
446 lock(&work.wbufSpans.lock)
447 work.wbufSpans.busy.insert(s)
448 unlock(&work.wbufSpans.lock)
449 }
450
451
452 for i := uintptr(0); i+_WorkbufSize <= workbufAlloc; i += _WorkbufSize {
453 newb := (*workbuf)(unsafe.Pointer(s.base() + i))
454 newb.nobj = 0
455 lfnodeValidate(&newb.node)
456 if i == 0 {
457 b = newb
458 } else {
459 putempty(newb)
460 }
461 }
462 }
463 return b
464 }
465
466
467
468
469 func putempty(b *workbuf) {
470 b.checkempty()
471 work.empty.push(&b.node)
472 }
473
474
475
476
477
478 func putfull(b *workbuf) {
479 b.checknonempty()
480 work.full.push(&b.node)
481 }
482
483
484
485
486 func trygetfull() *workbuf {
487 b := (*workbuf)(work.full.pop())
488 if b != nil {
489 b.checknonempty()
490 return b
491 }
492 return b
493 }
494
495
496 func handoff(b *workbuf) *workbuf {
497
498 b1 := getempty()
499 n := b.nobj / 2
500 b.nobj -= n
501 b1.nobj = n
502 memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), uintptr(n)*unsafe.Sizeof(b1.obj[0]))
503
504
505 putfull(b)
506 return b1
507 }
508
509
510
511
512 func prepareFreeWorkbufs() {
513 lock(&work.wbufSpans.lock)
514 if work.full != 0 {
515 throw("cannot free workbufs when work.full != 0")
516 }
517
518
519
520 work.empty = 0
521 work.wbufSpans.free.takeAll(&work.wbufSpans.busy)
522 unlock(&work.wbufSpans.lock)
523 }
524
525
526
527 func freeSomeWbufs(preemptible bool) bool {
528 const batchSize = 64
529 lock(&work.wbufSpans.lock)
530 if gcphase != _GCoff || work.wbufSpans.free.isEmpty() {
531 unlock(&work.wbufSpans.lock)
532 return false
533 }
534 systemstack(func() {
535 gp := getg().m.curg
536 for i := 0; i < batchSize && !(preemptible && gp.preempt); i++ {
537 span := work.wbufSpans.free.first
538 if span == nil {
539 break
540 }
541 work.wbufSpans.free.remove(span)
542 mheap_.freeManual(span, &memstats.gc_sys)
543 }
544 })
545 more := !work.wbufSpans.free.isEmpty()
546 unlock(&work.wbufSpans.lock)
547 return more
548 }
549
View as plain text