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