From d446ba99a4e2b147de3afc29ff8d6ddfa4041203 Mon Sep 17 00:00:00 2001 From: Austin Clements Date: Thu, 17 Dec 2015 16:31:56 -0800 Subject: [PATCH] runtime: document stack barrier synchronization rules Change-Id: I545e53561f37bceabd26d814d272cecc3ff19847 Reviewed-on: https://go-review.googlesource.com/18024 Reviewed-by: Russ Cox --- src/runtime/mstkbar.go | 22 ++++++++++++++++++++++ src/runtime/runtime2.go | 2 +- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/runtime/mstkbar.go b/src/runtime/mstkbar.go index 7d7235d259..016625ae92 100644 --- a/src/runtime/mstkbar.go +++ b/src/runtime/mstkbar.go @@ -102,6 +102,26 @@ // enabling write barriers globally during the concurrent scan phase. // However, traditionally, write barriers are not enabled during this // phase. +// +// Synchronization +// --------------- +// +// For the most part, accessing and modifying stack barriers is +// synchronized around GC safe points. Installing stack barriers +// forces the G to a safe point, while all other operations that +// modify stack barriers run on the G and prevent it from reaching a +// safe point. +// +// Subtlety arises when a G may be tracebacked when *not* at a safe +// point. This happens during sigprof. For this, each G has a "stack +// barrier lock" (see gcLockStackBarriers, gcUnlockStackBarriers). +// Operations that manipulate stack barriers acquire this lock, while +// sigprof tries to acquire it and simply skips the traceback if it +// can't acquire it. There is one exception for performance and +// complexity reasons: hitting a stack barrier manipulates the stack +// barrier list without acquiring the stack barrier lock. For this, +// gentraceback performs a special fix up if the traceback starts in +// the stack barrier function. package runtime @@ -321,6 +341,8 @@ func setNextBarrierPC(pc uintptr) { // //go:nosplit func gcLockStackBarriers(gp *g) { + // Disable preemption so scanstack cannot run while the caller + // is manipulating the stack barriers. acquirem() for !atomic.Cas(&gp.stackLock, 0, 1) { osyield() diff --git a/src/runtime/runtime2.go b/src/runtime/runtime2.go index 4a4d5f81ba..9549d1f531 100644 --- a/src/runtime/runtime2.go +++ b/src/runtime/runtime2.go @@ -233,7 +233,7 @@ type g struct { sched gobuf syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc - stkbar []stkbar // stack barriers, from low to high + stkbar []stkbar // stack barriers, from low to high (see top of mstkbar.go) stkbarPos uintptr // index of lowest stack barrier not hit stktopsp uintptr // expected sp at top of stack, to check in traceback param unsafe.Pointer // passed parameter on wakeup