Skip to content

Commit 2a331ca

Browse files
committed
runtime: document relaxed access to arena_used
The unsynchronized accesses to mheap_.arena_used in the concurrent part of the garbage collector look like a problem waiting to happen. In fact, they are safe, but the reason is somewhat subtle and undocumented. This commit documents this reasoning. Related to issue #9984. Change-Id: Icdbf2329c1aa11dbe2396a71eb5fc2a85bd4afd5 Reviewed-on: https://go-review.googlesource.com/11254 Reviewed-by: Dmitry Vyukov <[email protected]>
1 parent 874a605 commit 2a331ca

File tree

2 files changed

+22
-0
lines changed

2 files changed

+22
-0
lines changed

src/runtime/mbarrier.go

+13
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,19 @@ import "unsafe"
5858
// barriers, which will slow down both the mutator and the GC, we always grey
5959
// the ptr object regardless of the slot's color.
6060
//
61+
// Another place where we intentionally omit memory barriers is when
62+
// accessing mheap_.arena_used to check if a pointer points into the
63+
// heap. On relaxed memory machines, it's possible for a mutator to
64+
// extend the size of the heap by updating arena_used, allocate an
65+
// object from this new region, and publish a pointer to that object,
66+
// but for tracing running on another processor to observe the pointer
67+
// but use the old value of arena_used. In this case, tracing will not
68+
// mark the object, even though it's reachable. However, the mutator
69+
// is guaranteed to execute a write barrier when it publishes the
70+
// pointer, so it will take care of marking the object. A general
71+
// consequence of this is that the garbage collector may cache the
72+
// value of mheap_.arena_used. (See issue #9984.)
73+
//
6174
//
6275
// Stack writes:
6376
//

src/runtime/mgcmark.go

+9
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,15 @@ func scanblock(b0, n0 uintptr, ptrmask *uint8, gcw *gcWork) {
768768
// object (it ignores n).
769769
//go:nowritebarrier
770770
func scanobject(b uintptr, gcw *gcWork) {
771+
// Note that arena_used may change concurrently during
772+
// scanobject and hence scanobject may encounter a pointer to
773+
// a newly allocated heap object that is *not* in
774+
// [start,used). It will not mark this object; however, we
775+
// know that it was just installed by a mutator, which means
776+
// that mutator will execute a write barrier and take care of
777+
// marking it. This is even more pronounced on relaxed memory
778+
// architectures since we access arena_used without barriers
779+
// or synchronization, but the same logic applies.
771780
arena_start := mheap_.arena_start
772781
arena_used := mheap_.arena_used
773782

0 commit comments

Comments
 (0)