Splittable and unsplittable functions
As I described in Preemption in Go: an introduction, function prologues contain a stack bound check, which triggers a stack growth on failure. The stack guard can be poisoned with a specific value by the runtime to force a goroutine to enter the stack growth algorithm, which can detect the poisoning and give the runtime a chance to preempt the goroutine at a safe-point.
However, there are ways to avoid preemption when needed. One of them is to use proc pinning, as sync.Pool
does, but that’s a bit heavy.
Another way is to use the nosplit
annotation:
//go:nosplit
//go:noinline
func LoadAcquintptr(ptr *uintptr) uintptr {
return *ptr
}
Here, LoadAcquintptr
has a noinline
annotation to prevent inlining. This is to avoid memory reordering that could happen if the load of the pointer was bundled with the code that uses the loaded value. But as it cannot be inlined, it remains a function call with a prologue. The nosplit
annotation tells the compiler to leave the function without a stack bound check, which means it won’t be synchronously preemptable, and it won’t grow the stack. This is needed here to ensure atomicity and consistency.