const testSmallBuf … type wbBuf … const wbBufEntries … const wbMaxEntriesPerCall … // reset empties b by resetting its next and end pointers. func (b *wbBuf) reset() { … } // discard resets b's next pointer, but not its end pointer. // // This must be nosplit because it's called by wbBufFlush. // //go:nosplit func (b *wbBuf) discard() { … } // empty reports whether b contains no pointers. func (b *wbBuf) empty() bool { … } // getX returns space in the write barrier buffer to store X pointers. // getX will flush the buffer if necessary. Callers should use this as: // // buf := &getg().m.p.ptr().wbBuf // p := buf.get2() // p[0], p[1] = old, new // ... actual memory write ... // // The caller must ensure there are no preemption points during the // above sequence. There must be no preemption points while buf is in // use because it is a per-P resource. There must be no preemption // points between the buffer put and the write to memory because this // could allow a GC phase change, which could result in missed write // barriers. // // getX must be nowritebarrierrec to because write barriers here would // corrupt the write barrier buffer. It (and everything it calls, if // it called anything) has to be nosplit to avoid scheduling on to a // different P and a different buffer. // //go:nowritebarrierrec //go:nosplit func (b *wbBuf) get1() *[1]uintptr { … } //go:nowritebarrierrec //go:nosplit func (b *wbBuf) get2() *[2]uintptr { … } // wbBufFlush flushes the current P's write barrier buffer to the GC // workbufs. // // This must not have write barriers because it is part of the write // barrier implementation. // // This and everything it calls must be nosplit because 1) the stack // contains untyped slots from gcWriteBarrier and 2) there must not be // a GC safe point between the write barrier test in the caller and // flushing the buffer. // // TODO: A "go:nosplitrec" annotation would be perfect for this. // //go:nowritebarrierrec //go:nosplit func wbBufFlush() { … } // wbBufFlush1 flushes p's write barrier buffer to the GC work queue. // // This must not have write barriers because it is part of the write // barrier implementation, so this may lead to infinite loops or // buffer corruption. // // This must be non-preemptible because it uses the P's workbuf. // //go:nowritebarrierrec //go:systemstack func wbBufFlush1(pp *p) { … }