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
5package runtime
6
7import (
8	"runtime/internal/atomic"
9	"runtime/internal/sys"
10	"unsafe"
11)
12
13const (
14	_WorkbufSize = 2048 // in bytes; larger values result in less contention
15
16	// workbufAlloc is the number of bytes to allocate at a time
17	// for new workbufs. This must be a multiple of pageSize and
18	// should be a multiple of _WorkbufSize.
19	//
20	// Larger values reduce workbuf allocation overhead. Smaller
21	// values reduce heap fragmentation.
22	workbufAlloc = 32 << 10
23)
24
25func init() {
26	if workbufAlloc%pageSize != 0 || workbufAlloc%_WorkbufSize != 0 {
27		throw("bad workbufAlloc")
28	}
29}
30
31// Garbage collector work pool abstraction.
32//
33// This implements a producer/consumer model for pointers to grey
34// objects. A grey object is one that is marked and on a work
35// queue. A black object is marked and not on a work queue.
36//
37// Write barriers, root discovery, stack scanning, and object scanning
38// produce pointers to grey objects. Scanning consumes pointers to
39// grey objects, thus blackening them, and then scans them,
40// potentially producing new pointers to grey objects.
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//     (preemption must be disabled)
48//     gcw := &getg().m.p.ptr().gcw
49//     .. call gcw.put() to produce and gcw.tryGet() to consume ..
50//
51// It's important that any use of gcWork during the mark phase prevent
52// the garbage collector from transitioning to mark termination since
53// gcWork may locally hold GC work buffers. This can be done by
54// disabling preemption (systemstack or acquirem).
55type gcWork struct {
56	// wbuf1 and wbuf2 are the primary and secondary work buffers.
57	//
58	// This can be thought of as a stack of both work buffers'
59	// pointers concatenated. When we pop the last pointer, we
60	// shift the stack up by one work buffer by bringing in a new
61	// full buffer and discarding an empty one. When we fill both
62	// buffers, we shift the stack down by one work buffer by
63	// bringing in a new empty buffer and discarding a full one.
64	// This way we have one buffer's worth of hysteresis, which
65	// amortizes the cost of getting or putting a work buffer over
66	// at least one buffer of work and reduces contention on the
67	// global work lists.
68	//
69	// wbuf1 is always the buffer we're currently pushing to and
70	// popping from and wbuf2 is the buffer that will be discarded
71	// next.
72	//
73	// Invariant: Both wbuf1 and wbuf2 are nil or neither are.
74	wbuf1, wbuf2 *workbuf
75
76	// Bytes marked (blackened) on this gcWork. This is aggregated
77	// into work.bytesMarked by dispose.
78	bytesMarked uint64
79
80	// Scan work performed on this gcWork. This is aggregated into
81	// gcController by dispose and may also be flushed by callers.
82	scanWork int64
83
84	// flushedWork indicates that a non-empty work buffer was
85	// flushed to the global work list since the last gcMarkDone
86	// termination check. Specifically, this indicates that this
87	// gcWork may have communicated work to another gcWork.
88	flushedWork bool
89}
90
91// Most of the methods of gcWork are go:nowritebarrierrec because the
92// write barrier itself can invoke gcWork methods but the methods are
93// not generally re-entrant. Hence, if a gcWork method invoked the
94// write barrier while the gcWork was in an inconsistent state, and
95// the write barrier in turn invoked a gcWork method, it could
96// permanently corrupt the gcWork.
97
98func (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// put enqueues a pointer for the garbage collector to trace.
108// obj must point to the beginning of a heap object or an oblet.
109//go:nowritebarrierrec
110func (w *gcWork) put(obj uintptr) {
111	flushed := false
112	wbuf := w.wbuf1
113	// Record that this may acquire the wbufSpans or heap lock to
114	// allocate a workbuf.
115	lockWithRankMayAcquire(&work.wbufSpans.lock, lockRankWbufSpans)
116	lockWithRankMayAcquire(&mheap_.lock, lockRankMheap)
117	if wbuf == nil {
118		w.init()
119		wbuf = w.wbuf1
120		// wbuf is empty at this point.
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	// If we put a buffer on full, let the GC controller know so
137	// it can encourage more workers to run. We delay this until
138	// the end of put so that w is in a consistent state, since
139	// enlistWorker may itself manipulate w.
140	if flushed && gcphase == _GCmark {
141		gcController.enlistWorker()
142	}
143}
144
145// putFast does a put and reports whether it can be done quickly
146// otherwise it returns false and the caller needs to call put.
147//go:nowritebarrierrec
148func (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// putBatch performs a put on every pointer in obj. See put for
162// constraints on these pointers.
163//
164//go:nowritebarrierrec
165func (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// tryGet dequeues a pointer for the garbage collector to trace.
196//
197// If there are no pointers remaining in this gcWork or in the global
198// queue, tryGet returns 0.  Note that there may still be pointers in
199// other gcWork instances or other caches.
200//go:nowritebarrierrec
201func (w *gcWork) tryGet() uintptr {
202	wbuf := w.wbuf1
203	if wbuf == nil {
204		w.init()
205		wbuf = w.wbuf1
206		// wbuf is empty at this point.
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// tryGetFast dequeues a pointer for the garbage collector to trace
227// if one is readily available. Otherwise it returns 0 and
228// the caller is expected to call tryGet().
229//go:nowritebarrierrec
230func (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// dispose returns any cached pointers to the global queue.
244// The buffers are being put on the full queue so that the
245// write barriers will not simply reacquire them before the
246// GC can inspect them. This helps reduce the mutator's
247// ability to hide pointers during the concurrent mark phase.
248//
249//go:nowritebarrierrec
250func (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		// dispose happens relatively infrequently. If this
271		// atomic becomes a problem, we should first try to
272		// dispose less and if necessary aggregate in a per-P
273		// counter.
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// balance moves some work that's cached in this gcWork back on the
284// global queue.
285//go:nowritebarrierrec
286func (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 // handoff did putfull
297	} else {
298		return
299	}
300	// We flushed a buffer to the full list, so wake a worker.
301	if gcphase == _GCmark {
302		gcController.enlistWorker()
303	}
304}
305
306// empty reports whether w has no mark work available.
307//go:nowritebarrierrec
308func (w *gcWork) empty() bool {
309	return w.wbuf1 == nil || (w.wbuf1.nobj == 0 && w.wbuf2.nobj == 0)
310}
311
312// Internally, the GC work pool is kept in arrays in work buffers.
313// The gcWork interface caches a work buffer until full (or empty) to
314// avoid contending on the global work buffer lists.
315
316type workbufhdr struct {
317	node lfnode // must be first
318	nobj int
319}
320
321//go:notinheap
322type workbuf struct {
323	workbufhdr
324	// account for the above fields
325	obj [(_WorkbufSize - unsafe.Sizeof(workbufhdr{})) / sys.PtrSize]uintptr
326}
327
328// workbuf factory routines. These funcs are used to manage the
329// workbufs.
330// If the GC asks for some work these are the only routines that
331// make wbufs available to the GC.
332
333func (b *workbuf) checknonempty() {
334	if b.nobj == 0 {
335		throw("workbuf is empty")
336	}
337}
338
339func (b *workbuf) checkempty() {
340	if b.nobj != 0 {
341		throw("workbuf is not empty")
342	}
343}
344
345// getempty pops an empty work buffer off the work.empty list,
346// allocating new buffers if none are available.
347//go:nowritebarrier
348func 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	// Record that this may acquire the wbufSpans or heap lock to
357	// allocate a workbuf.
358	lockWithRankMayAcquire(&work.wbufSpans.lock, lockRankWbufSpans)
359	lockWithRankMayAcquire(&mheap_.lock, lockRankMheap)
360	if b == nil {
361		// Allocate more workbufs.
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			// Record the new span in the busy list.
380			lock(&work.wbufSpans.lock)
381			work.wbufSpans.busy.insert(s)
382			unlock(&work.wbufSpans.lock)
383		}
384		// Slice up the span into new workbufs. Return one and
385		// put the rest on the empty list.
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// putempty puts a workbuf onto the work.empty list.
401// Upon entry this go routine owns b. The lfstack.push relinquishes ownership.
402//go:nowritebarrier
403func putempty(b *workbuf) {
404	b.checkempty()
405	work.empty.push(&b.node)
406}
407
408// putfull puts the workbuf on the work.full list for the GC.
409// putfull accepts partially full buffers so the GC can avoid competing
410// with the mutators for ownership of partially full buffers.
411//go:nowritebarrier
412func putfull(b *workbuf) {
413	b.checknonempty()
414	work.full.push(&b.node)
415}
416
417// trygetfull tries to get a full or partially empty workbuffer.
418// If one is not immediately available return nil
419//go:nowritebarrier
420func 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//go:nowritebarrier
430func handoff(b *workbuf) *workbuf {
431	// Make new buffer with half of b's pointers.
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	// Put b on full list - let first half of b get stolen.
439	putfull(b)
440	return b1
441}
442
443// prepareFreeWorkbufs moves busy workbuf spans to free list so they
444// can be freed to the heap. This must only be called when all
445// workbufs are on the empty list.
446func prepareFreeWorkbufs() {
447	lock(&work.wbufSpans.lock)
448	if work.full != 0 {
449		throw("cannot free workbufs when work.full != 0")
450	}
451	// Since all workbufs are on the empty list, we don't care
452	// which ones are in which spans. We can wipe the entire empty
453	// list and move all workbuf spans to the free list.
454	work.empty = 0
455	work.wbufSpans.free.takeAll(&work.wbufSpans.busy)
456	unlock(&work.wbufSpans.lock)
457}
458
459// freeSomeWbufs frees some workbufs back to the heap and returns
460// true if it should be called again to free more.
461func freeSomeWbufs(preemptible bool) bool {
462	const batchSize = 64 // ~1–2 µs per span.
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