Runtime Internals Used by Stdlib — Specification¶
Table of Contents¶
- Introduction
runtimepackage godoc — exported helpers- Go memory model statements about runtime
runtime/racedocumentationruntime/traceevent formatruntime/pprofprofile definitionsinternal/pollruntime hooksruntime/metricsschema- Cross-reference table
- References
Introduction¶
This file collects normative text from pkg.go.dev/runtime, the Go memory model document, and runtime/race documentation. Citations are paraphrased where necessary for clarity. The original sources are listed at the end.
runtime package godoc — exported helpers¶
func Gosched()¶
Gosched yields the processor, allowing other goroutines to run. It does not suspend the current goroutine, so execution resumes automatically.
Source: src/runtime/proc.go. Implementation in src/runtime/proc.go:Gosched_m.
func LockOSThread() / func UnlockOSThread()¶
LockOSThread wires the calling goroutine to its current operating system thread. The calling goroutine will always execute in that thread, and no other goroutine will execute in it, until the calling goroutine has made as many calls to UnlockOSThread as to LockOSThread. If the calling goroutine exits without unlocking the thread, the thread will be terminated.
All init functions are run on the startup thread. Calling LockOSThread from an init function will cause the main function to be invoked on that thread.
A goroutine should call LockOSThread before calling OS services or non-Go library functions that depend on per-thread state.
func SetFinalizer(obj any, finalizer any)¶
SetFinalizer sets the finalizer associated with obj to the provided finalizer function. When the garbage collector finds an unreachable block with an associated finalizer, it clears the association and runs finalizer(obj) in a separate goroutine. This makes obj reachable again, but now without an associated finalizer. Assuming that SetFinalizer is not called again, the next time the garbage collector sees that obj is unreachable, it will free obj.
SetFinalizer(obj, nil) clears any finalizer associated with obj.
The argument obj must be a pointer to an object allocated by calling new, by taking the address of a composite literal, or by taking the address of a local variable. The argument finalizer must be a function that takes a single argument to which obj's type can be assigned, and can have arbitrary ignored return values. If either of these is not true, SetFinalizer may abort the program.
Finalizers are run in the dependency order of the values they reference. … In a program that uses finalizers, the program must call SetFinalizer for every value that needs cleanup.
It is not guaranteed that a finalizer will run if the size of *obj is zero bytes, because it may share same address with other zero-size objects in memory. … There is no guarantee that finalizers will run before a program exits, so typically they are useful only for releasing non-memory resources associated with an object during a long-running program.
func Goexit()¶
Goexit terminates the goroutine that calls it. No other goroutine is affected. Goexit runs all deferred calls before terminating the goroutine. Because Goexit is not a panic, any recover calls in those deferred functions will return nil.
Calling Goexit from the main goroutine terminates that goroutine without func main returning. Since func main has not returned, the program continues execution of other goroutines. If all other goroutines exit, the program crashes.
func GC()¶
GC runs a garbage collection and blocks the caller until the garbage collection is complete. It may also block the entire program.
func NumGoroutine() int¶
NumGoroutine returns the number of goroutines that currently exist.
func NumCPU() int¶
NumCPU returns the number of logical CPUs usable by the current process. The set of available CPUs is checked by querying the operating system at process startup.
func GOMAXPROCS(n int) int¶
GOMAXPROCS sets the maximum number of CPUs that can be executing simultaneously and returns the previous setting. If n < 1, it does not change the current setting.
func Stack(buf []byte, all bool) int¶
Stack formats a stack trace of the calling goroutine into buf and returns the number of bytes written. If all is true, Stack formats stack traces of all other goroutines into buf after the trace for the current goroutine.
func SetCPUProfileRate(hz int)¶
SetCPUProfileRate sets the CPU profiling rate to hz samples per second. If hz <= 0, SetCPUProfileRate turns off profiling.
func SetBlockProfileRate(rate int)¶
SetBlockProfileRate controls the fraction of goroutine blocking events that are reported in the blocking profile. The profiler aims to sample an average of one blocking event per rate nanoseconds spent blocked. To include every blocking event in the profile, pass rate = 1.
func SetMutexProfileFraction(rate int) int¶
SetMutexProfileFraction controls the fraction of mutex contention events that are reported in the mutex profile. On average 1/rate events are reported.
type Pinner (Go 1.21+)¶
A Pinner is a set of Go objects each pinned to a fixed location in memory. The Pinner.Pin method pins one object, while Pinner.Unpin unpins all pinned objects.
A pinned object will remain pinned until either Unpin is called or Pinner is no longer referenced and is garbage collected. … It is safe to pin a Go object multiple times, possibly with multiple Pinners. … A pinned object cannot be moved by the garbage collector.
Memory occupied by pinned objects is not reclaimed, even if all references to them go out of scope.
func KeepAlive(x any)¶
KeepAlive marks its argument as currently reachable. This ensures that the object is not freed, and its finalizer is not run, before the point in the program where KeepAlive is called.
Go memory model statements about runtime¶
From the Go memory model document (https://go.dev/ref/mem):
The go statement that starts a new goroutine happens before the goroutine's execution begins.
The exit of a goroutine is not guaranteed to happen before any event in the program. For example, in this program:
the assignment to a is not followed by any synchronization event, so it is not guaranteed to be observed by any other goroutine. In fact, an aggressive compiler might delete the entire go statement.
On finalizers:
The call to runtime.SetFinalizer(x, f) happens before the finalization call f(x).
On sync.Once:
A single call of f from once.Do(f) is synchronized before the return of any call of once.Do(f).
On atomic operations (Go 1.19+):
The APIs in the sync/atomic package are collectively "atomic operations" that can be used to synchronize the execution of different goroutines. If the effect of an atomic operation A is observed by atomic operation B, then A is synchronized before B.
runtime/race documentation¶
From src/runtime/race/README and src/runtime/race.go:
The race detector is integrated with the Go runtime via callbacks that the compiler inserts at every load and store of memory, and at every synchronization point. The runtime forwards these events to the ThreadSanitizer library (tsan_go.cpp).
Compiler-inserted callbacks (declared in src/runtime/race.go): - racefuncenter(callpc uintptr) — at every function entry. - racefuncexit() — at every function exit. - raceread(addr uintptr) — before every read of memory. - racewrite(addr uintptr) — before every write of memory. - racereadrange(addr, size uintptr) — for slice ranges. - racewriterange(addr, size uintptr) — for slice ranges.
Synchronization callbacks (called from runtime when synchronization happens): - raceacquire(addr unsafe.Pointer) — called by sync.Mutex.Lock slow path, by channel receive, etc. - racerelease(addr unsafe.Pointer) — called by sync.Mutex.Unlock, by channel send. - racereleaseacquire(addr unsafe.Pointer) — combined release-acquire.
A happens-before edge from one goroutine's racerelease(p) to another goroutine's raceacquire(p) is established when p is the same address. The TSan runtime maintains a vector clock per goroutine and shadow memory recording the last reader/writer of each word.
runtime/trace event format¶
From src/runtime/trace.go and internal/trace:
Each trace event has the form <event-type-byte> <varint-length> <varint-args>.... Key event types:
| Event | Code | When emitted | Arguments |
|---|---|---|---|
EvGoCreate | 12 | runtime.newproc | new-g-id, new-pc, parent-stack |
EvGoStart | 13 | runtime.execute | g-id, seq |
EvGoEnd | 14 | runtime.goexit0 | — |
EvGoStop | 15 | gopark(non-block) | reason, stack |
EvGoSched | 16 | runtime.Gosched_m | stack |
EvGoPreempt | 17 | preemption signal | stack |
EvGoSleep | 18 | time.Sleep | stack |
EvGoBlock | 19 | gopark(generic) | stack |
EvGoBlockSend | 20 | chan send block | stack |
EvGoBlockRecv | 21 | chan recv block | stack |
EvGoBlockSelect | 22 | select block | stack |
EvGoBlockSync | 23 | sync.Mutex block | stack |
EvGoBlockCond | 24 | sync.Cond.Wait | stack |
EvGoBlockNet | 25 | netpoll block | stack |
EvGoSysCall | 26 | entersyscall | stack |
EvGoSysExit | 27 | exitsyscall | seq, ts |
EvProcStart | 5 | acquirep | thread-id |
EvProcStop | 6 | releasep | — |
EvGCStart | 7 | GC begins | stack |
EvGCDone | 8 | GC ends | — |
Each event is written to a per-P buffer to avoid contention.
runtime/pprof profile definitions¶
From pkg.go.dev/runtime/pprof:
CPU profile¶
Emitted by SIGPROF; rate set via runtime.SetCPUProfileRate or pprof.StartCPUProfile. Samples consist of a stack trace and a count (cycle of the SIGPROF clock).
Goroutine profile¶
Stack traces of all current goroutines.
Implemented via runtime.GoroutineProfile. Requires STW.
Heap profile¶
A sampling of memory allocations of live objects.
Sample rate controlled by runtime.MemProfileRate (default 512 KB).
Allocs profile¶
A sampling of all past memory allocations.
Block profile¶
Stack traces that led to blocking on synchronization primitives.
Recorded only if runtime.SetBlockProfileRate(rate > 0) was called.
Mutex profile¶
Stack traces of holders of contended mutexes.
Recorded only if runtime.SetMutexProfileFraction(rate > 0) was called.
Threadcreate profile¶
Stack traces that led to the creation of new OS threads.
internal/poll runtime hooks¶
internal/poll/fd_poll_runtime.go declares the runtime functions stdlib calls:
// runtime_pollServerInit is called once to initialize the netpoller.
func runtime_pollServerInit()
// runtime_pollOpen registers a fd with the netpoller; returns pollDesc.
func runtime_pollOpen(fd uintptr) (uintptr, int)
// runtime_pollClose deregisters a fd.
func runtime_pollClose(ctx uintptr)
// runtime_pollWait blocks the calling goroutine until the fd is ready
// for the requested operation (mode: 'r' or 'w').
func runtime_pollWait(ctx uintptr, mode int) int
// runtime_pollSetDeadline sets a deadline on a poll desc.
func runtime_pollSetDeadline(ctx uintptr, d int64, mode int)
// runtime_pollUnblock wakes any goroutines waiting on a fd.
func runtime_pollUnblock(ctx uintptr)
These are linked at build time via go:linkname to runtime implementations in src/runtime/netpoll.go.
runtime/metrics schema¶
Since Go 1.16, the runtime/metrics package provides low-overhead access to runtime statistics. Selected metrics:
| Name | Kind | Meaning |
|---|---|---|
/gc/cycles/total:gc-cycles | counter | Total GC cycles completed |
/gc/heap/live:bytes | gauge | Live heap bytes after last GC |
/gc/pauses:seconds | distribution | GC pause durations |
/sched/goroutines:goroutines | gauge | Number of goroutines |
/sched/latencies:seconds | distribution | Time goroutines spent in run queues |
/sched/pauses/stopping/gc:seconds | distribution | STW stopping latency for GC |
/sched/pauses/total/gc:seconds | distribution | Total STW duration for GC |
/sync/mutex/wait/total:seconds | counter | Accumulated mutex wait time |
/cpu/classes/gc/total:cpu-seconds | counter | CPU time attributed to GC |
/cpu/classes/scavenge/total:cpu-seconds | counter | CPU time attributed to scavenger |
Read with:
samples := make([]metrics.Sample, 1)
samples[0].Name = "/sched/goroutines:goroutines"
metrics.Read(samples)
Cross-reference table¶
| Stdlib API | Runtime function | File |
|---|---|---|
time.Sleep | runtime.timeSleep | src/runtime/time.go |
time.NewTimer | runtime.newTimer | src/runtime/time.go |
sync.Mutex.Lock (slow) | runtime.semacquire1 | src/runtime/sema.go |
sync.Mutex.Unlock (slow) | runtime.semrelease | src/runtime/sema.go |
sync.WaitGroup.Wait | runtime_SemacquireWaitGroup | src/runtime/sema.go |
sync.Cond.Wait | runtime.notifyListWait | src/runtime/sema.go |
sync.Cond.Signal | runtime.notifyListNotifyOne | src/runtime/sema.go |
sync.Cond.Broadcast | runtime.notifyListNotifyAll | src/runtime/sema.go |
sync.Pool.Get | runtime_procPin | src/runtime/proc.go |
sync.Once.Do | atomic + Mutex | src/sync/once.go |
net.Conn.Read (blocked) | runtime_pollWait | src/runtime/netpoll.go |
os.Signal | dedicated goroutine via signal_recv | src/runtime/signal_unix.go |
runtime.SetFinalizer | runtime.SetFinalizer (impl) | src/runtime/mfinal.go |
runtime/pprof.StartCPUProfile | runtime.SetCPUProfileRate | src/runtime/cpuprof.go |
References¶
- Go runtime package documentation — https://pkg.go.dev/runtime
- Go memory model — https://go.dev/ref/mem
runtime/raceREADME —$GOROOT/src/runtime/race/README- Russ Cox, "The Go Memory Model" — https://research.swtsh.com/go-mem
- Go execution trace format — https://golang.org/s/go15trace
runtime/metricsdocumentation — https://pkg.go.dev/runtime/metrics- Dmitry Vyukov, "Go scheduler design" — https://golang.org/s/go11sched
- Austin Clements, "Go 1.14 async preemption" — https://golang.org/issue/24543
internal/pollsource —$GOROOT/src/internal/poll/runtime/cgocall.go— cgo entry/exit machinery
This document is the source of truth for any claim made in junior.md, middle.md, senior.md, and professional.md regarding runtime package semantics.