]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/mgcwork.go
runtime: break out system-specific constants into package sys
[gostls13.git] / src / runtime / mgcwork.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package runtime
6
7 import (
8         "runtime/internal/atomic"
9         "runtime/internal/sys"
10         "unsafe"
11 )
12
13 const (
14         _Debugwbufs  = false // if true check wbufs consistency
15         _WorkbufSize = 2048  // in bytes; larger values result in less contention
16 )
17
18 // Garbage collector work pool abstraction.
19 //
20 // This implements a producer/consumer model for pointers to grey
21 // objects.  A grey object is one that is marked and on a work
22 // queue.  A black object is marked and not on a work queue.
23 //
24 // Write barriers, root discovery, stack scanning, and object scanning
25 // produce pointers to grey objects.  Scanning consumes pointers to
26 // grey objects, thus blackening them, and then scans them,
27 // potentially producing new pointers to grey objects.
28
29 // A wbufptr holds a workbuf*, but protects it from write barriers.
30 // workbufs never live on the heap, so write barriers are unnecessary.
31 // Write barriers on workbuf pointers may also be dangerous in the GC.
32 type wbufptr uintptr
33
34 func wbufptrOf(w *workbuf) wbufptr {
35         return wbufptr(unsafe.Pointer(w))
36 }
37
38 func (wp wbufptr) ptr() *workbuf {
39         return (*workbuf)(unsafe.Pointer(wp))
40 }
41
42 // A gcWork provides the interface to produce and consume work for the
43 // garbage collector.
44 //
45 // A gcWork can be used on the stack as follows:
46 //
47 //     var gcw gcWork
48 //     disable preemption
49 //     .. call gcw.put() to produce and gcw.get() to consume ..
50 //     gcw.dispose()
51 //     enable preemption
52 //
53 // Or from the per-P gcWork cache:
54 //
55 //     (preemption must be disabled)
56 //     gcw := &getg().m.p.ptr().gcw
57 //     .. call gcw.put() to produce and gcw.get() to consume ..
58 //     if gcBlackenPromptly {
59 //         gcw.dispose()
60 //     }
61 //
62 // It's important that any use of gcWork during the mark phase prevent
63 // the garbage collector from transitioning to mark termination since
64 // gcWork may locally hold GC work buffers. This can be done by
65 // disabling preemption (systemstack or acquirem).
66 type gcWork struct {
67         // wbuf1 and wbuf2 are the primary and secondary work buffers.
68         //
69         // This can be thought of as a stack of both work buffers'
70         // pointers concatenated. When we pop the last pointer, we
71         // shift the stack up by one work buffer by bringing in a new
72         // full buffer and discarding an empty one. When we fill both
73         // buffers, we shift the stack down by one work buffer by
74         // bringing in a new empty buffer and discarding a full one.
75         // This way we have one buffer's worth of hysteresis, which
76         // amortizes the cost of getting or putting a work buffer over
77         // at least one buffer of work and reduces contention on the
78         // global work lists.
79         //
80         // wbuf1 is always the buffer we're currently pushing to and
81         // popping from and wbuf2 is the buffer that will be discarded
82         // next.
83         //
84         // Invariant: Both wbuf1 and wbuf2 are nil or neither are.
85         wbuf1, wbuf2 wbufptr
86
87         // Bytes marked (blackened) on this gcWork. This is aggregated
88         // into work.bytesMarked by dispose.
89         bytesMarked uint64
90
91         // Scan work performed on this gcWork. This is aggregated into
92         // gcController by dispose and may also be flushed by callers.
93         scanWork int64
94 }
95
96 func (w *gcWork) init() {
97         w.wbuf1 = wbufptrOf(getempty(101))
98         wbuf2 := trygetfull(102)
99         if wbuf2 == nil {
100                 wbuf2 = getempty(103)
101         }
102         w.wbuf2 = wbufptrOf(wbuf2)
103 }
104
105 // put enqueues a pointer for the garbage collector to trace.
106 // obj must point to the beginning of a heap object.
107 //go:nowritebarrier
108 func (ww *gcWork) put(obj uintptr) {
109         w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
110
111         wbuf := w.wbuf1.ptr()
112         if wbuf == nil {
113                 w.init()
114                 wbuf = w.wbuf1.ptr()
115                 // wbuf is empty at this point.
116         } else if wbuf.nobj == len(wbuf.obj) {
117                 w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
118                 wbuf = w.wbuf1.ptr()
119                 if wbuf.nobj == len(wbuf.obj) {
120                         putfull(wbuf, 132)
121                         wbuf = getempty(133)
122                         w.wbuf1 = wbufptrOf(wbuf)
123                 }
124         }
125
126         wbuf.obj[wbuf.nobj] = obj
127         wbuf.nobj++
128 }
129
130 // tryGet dequeues a pointer for the garbage collector to trace.
131 //
132 // If there are no pointers remaining in this gcWork or in the global
133 // queue, tryGet returns 0.  Note that there may still be pointers in
134 // other gcWork instances or other caches.
135 //go:nowritebarrier
136 func (ww *gcWork) tryGet() uintptr {
137         w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
138
139         wbuf := w.wbuf1.ptr()
140         if wbuf == nil {
141                 w.init()
142                 wbuf = w.wbuf1.ptr()
143                 // wbuf is empty at this point.
144         }
145         if wbuf.nobj == 0 {
146                 w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
147                 wbuf = w.wbuf1.ptr()
148                 if wbuf.nobj == 0 {
149                         owbuf := wbuf
150                         wbuf = trygetfull(167)
151                         if wbuf == nil {
152                                 return 0
153                         }
154                         putempty(owbuf, 166)
155                         w.wbuf1 = wbufptrOf(wbuf)
156                 }
157         }
158
159         wbuf.nobj--
160         return wbuf.obj[wbuf.nobj]
161 }
162
163 // get dequeues a pointer for the garbage collector to trace, blocking
164 // if necessary to ensure all pointers from all queues and caches have
165 // been retrieved.  get returns 0 if there are no pointers remaining.
166 //go:nowritebarrier
167 func (ww *gcWork) get() uintptr {
168         w := (*gcWork)(noescape(unsafe.Pointer(ww))) // TODO: remove when escape analysis is fixed
169
170         wbuf := w.wbuf1.ptr()
171         if wbuf == nil {
172                 w.init()
173                 wbuf = w.wbuf1.ptr()
174                 // wbuf is empty at this point.
175         }
176         if wbuf.nobj == 0 {
177                 w.wbuf1, w.wbuf2 = w.wbuf2, w.wbuf1
178                 wbuf = w.wbuf1.ptr()
179                 if wbuf.nobj == 0 {
180                         owbuf := wbuf
181                         wbuf = getfull(185)
182                         if wbuf == nil {
183                                 return 0
184                         }
185                         putempty(owbuf, 184)
186                         w.wbuf1 = wbufptrOf(wbuf)
187                 }
188         }
189
190         // TODO: This might be a good place to add prefetch code
191
192         wbuf.nobj--
193         return wbuf.obj[wbuf.nobj]
194 }
195
196 // dispose returns any cached pointers to the global queue.
197 // The buffers are being put on the full queue so that the
198 // write barriers will not simply reacquire them before the
199 // GC can inspect them. This helps reduce the mutator's
200 // ability to hide pointers during the concurrent mark phase.
201 //
202 //go:nowritebarrier
203 func (w *gcWork) dispose() {
204         if wbuf := w.wbuf1.ptr(); wbuf != nil {
205                 if wbuf.nobj == 0 {
206                         putempty(wbuf, 212)
207                 } else {
208                         putfull(wbuf, 214)
209                 }
210                 w.wbuf1 = 0
211
212                 wbuf = w.wbuf2.ptr()
213                 if wbuf.nobj == 0 {
214                         putempty(wbuf, 218)
215                 } else {
216                         putfull(wbuf, 220)
217                 }
218                 w.wbuf2 = 0
219         }
220         if w.bytesMarked != 0 {
221                 // dispose happens relatively infrequently. If this
222                 // atomic becomes a problem, we should first try to
223                 // dispose less and if necessary aggregate in a per-P
224                 // counter.
225                 atomic.Xadd64(&work.bytesMarked, int64(w.bytesMarked))
226                 w.bytesMarked = 0
227         }
228         if w.scanWork != 0 {
229                 atomic.Xaddint64(&gcController.scanWork, w.scanWork)
230                 w.scanWork = 0
231         }
232 }
233
234 // balance moves some work that's cached in this gcWork back on the
235 // global queue.
236 //go:nowritebarrier
237 func (w *gcWork) balance() {
238         if w.wbuf1 == 0 {
239                 return
240         }
241         if wbuf := w.wbuf2.ptr(); wbuf.nobj != 0 {
242                 putfull(wbuf, 246)
243                 w.wbuf2 = wbufptrOf(getempty(247))
244         } else if wbuf := w.wbuf1.ptr(); wbuf.nobj > 4 {
245                 w.wbuf1 = wbufptrOf(handoff(wbuf))
246         }
247 }
248
249 // empty returns true if w has no mark work available.
250 //go:nowritebarrier
251 func (w *gcWork) empty() bool {
252         return w.wbuf1 == 0 || (w.wbuf1.ptr().nobj == 0 && w.wbuf2.ptr().nobj == 0)
253 }
254
255 // Internally, the GC work pool is kept in arrays in work buffers.
256 // The gcWork interface caches a work buffer until full (or empty) to
257 // avoid contending on the global work buffer lists.
258
259 type workbufhdr struct {
260         node  lfnode // must be first
261         nobj  int
262         inuse bool   // This workbuf is in use by some gorotuine and is not on the work.empty/full queues.
263         log   [4]int // line numbers forming a history of ownership changes to workbuf
264 }
265
266 type workbuf struct {
267         workbufhdr
268         // account for the above fields
269         obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / sys.PtrSize]uintptr
270 }
271
272 // workbuf factory routines. These funcs are used to manage the
273 // workbufs.
274 // If the GC asks for some work these are the only routines that
275 // make wbufs available to the GC.
276 // Each of the gets and puts also take an distinct integer that is used
277 // to record a brief history of changes to ownership of the workbuf.
278 // The convention is to use a unique line number but any encoding
279 // is permissible. For example if you want to pass in 2 bits of information
280 // you could simple add lineno1*100000+lineno2.
281
282 // logget records the past few values of entry to aid in debugging.
283 // logget checks the buffer b is not currently in use.
284 func (b *workbuf) logget(entry int) {
285         if !_Debugwbufs {
286                 return
287         }
288         if b.inuse {
289                 println("runtime: logget fails log entry=", entry,
290                         "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
291                         "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
292                 throw("logget: get not legal")
293         }
294         b.inuse = true
295         copy(b.log[1:], b.log[:])
296         b.log[0] = entry
297 }
298
299 // logput records the past few values of entry to aid in debugging.
300 // logput checks the buffer b is currently in use.
301 func (b *workbuf) logput(entry int) {
302         if !_Debugwbufs {
303                 return
304         }
305         if !b.inuse {
306                 println("runtime: logput fails log entry=", entry,
307                         "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
308                         "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
309                 throw("logput: put not legal")
310         }
311         b.inuse = false
312         copy(b.log[1:], b.log[:])
313         b.log[0] = entry
314 }
315
316 func (b *workbuf) checknonempty() {
317         if b.nobj == 0 {
318                 println("runtime: nonempty check fails",
319                         "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
320                         "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
321                 throw("workbuf is empty")
322         }
323 }
324
325 func (b *workbuf) checkempty() {
326         if b.nobj != 0 {
327                 println("runtime: empty check fails",
328                         "b.log[0]=", b.log[0], "b.log[1]=", b.log[1],
329                         "b.log[2]=", b.log[2], "b.log[3]=", b.log[3])
330                 throw("workbuf is not empty")
331         }
332 }
333
334 // getempty pops an empty work buffer off the work.empty list,
335 // allocating new buffers if none are available.
336 // entry is used to record a brief history of ownership.
337 //go:nowritebarrier
338 func getempty(entry int) *workbuf {
339         var b *workbuf
340         if work.empty != 0 {
341                 b = (*workbuf)(lfstackpop(&work.empty))
342                 if b != nil {
343                         b.checkempty()
344                 }
345         }
346         if b == nil {
347                 b = (*workbuf)(persistentalloc(unsafe.Sizeof(*b), sys.CacheLineSize, &memstats.gc_sys))
348         }
349         b.logget(entry)
350         return b
351 }
352
353 // putempty puts a workbuf onto the work.empty list.
354 // Upon entry this go routine owns b. The lfstackpush relinquishes ownership.
355 //go:nowritebarrier
356 func putempty(b *workbuf, entry int) {
357         b.checkempty()
358         b.logput(entry)
359         lfstackpush(&work.empty, &b.node)
360 }
361
362 // putfull puts the workbuf on the work.full list for the GC.
363 // putfull accepts partially full buffers so the GC can avoid competing
364 // with the mutators for ownership of partially full buffers.
365 //go:nowritebarrier
366 func putfull(b *workbuf, entry int) {
367         b.checknonempty()
368         b.logput(entry)
369         lfstackpush(&work.full, &b.node)
370
371         // We just made more work available. Let the GC controller
372         // know so it can encourage more workers to run.
373         if gcphase == _GCmark {
374                 gcController.enlistWorker()
375         }
376 }
377
378 // trygetfull tries to get a full or partially empty workbuffer.
379 // If one is not immediately available return nil
380 //go:nowritebarrier
381 func trygetfull(entry int) *workbuf {
382         b := (*workbuf)(lfstackpop(&work.full))
383         if b != nil {
384                 b.logget(entry)
385                 b.checknonempty()
386                 return b
387         }
388         return b
389 }
390
391 // Get a full work buffer off the work.full list.
392 // If nothing is available wait until all the other gc helpers have
393 // finished and then return nil.
394 // getfull acts as a barrier for work.nproc helpers. As long as one
395 // gchelper is actively marking objects it
396 // may create a workbuffer that the other helpers can work on.
397 // The for loop either exits when a work buffer is found
398 // or when _all_ of the work.nproc GC helpers are in the loop
399 // looking for work and thus not capable of creating new work.
400 // This is in fact the termination condition for the STW mark
401 // phase.
402 //go:nowritebarrier
403 func getfull(entry int) *workbuf {
404         b := (*workbuf)(lfstackpop(&work.full))
405         if b != nil {
406                 b.logget(entry)
407                 b.checknonempty()
408                 return b
409         }
410
411         incnwait := atomic.Xadd(&work.nwait, +1)
412         if incnwait > work.nproc {
413                 println("runtime: work.nwait=", incnwait, "work.nproc=", work.nproc)
414                 throw("work.nwait > work.nproc")
415         }
416         for i := 0; ; i++ {
417                 if work.full != 0 {
418                         decnwait := atomic.Xadd(&work.nwait, -1)
419                         if decnwait == work.nproc {
420                                 println("runtime: work.nwait=", decnwait, "work.nproc=", work.nproc)
421                                 throw("work.nwait > work.nproc")
422                         }
423                         b = (*workbuf)(lfstackpop(&work.full))
424                         if b != nil {
425                                 b.logget(entry)
426                                 b.checknonempty()
427                                 return b
428                         }
429                         incnwait := atomic.Xadd(&work.nwait, +1)
430                         if incnwait > work.nproc {
431                                 println("runtime: work.nwait=", incnwait, "work.nproc=", work.nproc)
432                                 throw("work.nwait > work.nproc")
433                         }
434                 }
435                 if work.nwait == work.nproc && work.markrootNext >= work.markrootJobs {
436                         return nil
437                 }
438                 _g_ := getg()
439                 if i < 10 {
440                         _g_.m.gcstats.nprocyield++
441                         procyield(20)
442                 } else if i < 20 {
443                         _g_.m.gcstats.nosyield++
444                         osyield()
445                 } else {
446                         _g_.m.gcstats.nsleep++
447                         usleep(100)
448                 }
449         }
450 }
451
452 //go:nowritebarrier
453 func handoff(b *workbuf) *workbuf {
454         // Make new buffer with half of b's pointers.
455         b1 := getempty(915)
456         n := b.nobj / 2
457         b.nobj -= n
458         b1.nobj = n
459         memmove(unsafe.Pointer(&b1.obj[0]), unsafe.Pointer(&b.obj[b.nobj]), uintptr(n)*unsafe.Sizeof(b1.obj[0]))
460         _g_ := getg()
461         _g_.m.gcstats.nhandoff++
462         _g_.m.gcstats.nhandoffcnt += uint64(n)
463
464         // Put b on full list - let first half of b get stolen.
465         putfull(b, 942)
466         return b1
467 }