2016-12-12 11:56:11 -07:00
|
|
|
This is a living document and at times it will be out of date. It is
|
|
|
|
intended to articulate how programming in the Go runtime differs from
|
|
|
|
writing normal Go. It focuses on pervasive concepts rather than
|
|
|
|
details of particular interfaces.
|
|
|
|
|
|
|
|
Scheduler structures
|
|
|
|
====================
|
|
|
|
|
|
|
|
The scheduler manages three types of resources that pervade the
|
|
|
|
runtime: Gs, Ms, and Ps. It's important to understand these even if
|
|
|
|
you're not working on the scheduler.
|
|
|
|
|
|
|
|
Gs, Ms, Ps
|
|
|
|
----------
|
|
|
|
|
|
|
|
A "G" is simply a goroutine. It's represented by type `g`. When a
|
|
|
|
goroutine exits, its `g` object is returned to a pool of free `g`s and
|
|
|
|
can later be reused for some other goroutine.
|
|
|
|
|
|
|
|
An "M" is an OS thread that can be executing user Go code, runtime
|
|
|
|
code, a system call, or be idle. It's represented by type `m`. There
|
|
|
|
can be any number of Ms at a time since any number of threads may be
|
|
|
|
blocked in system calls.
|
|
|
|
|
|
|
|
Finally, a "P" represents the resources required to execute user Go
|
|
|
|
code, such as scheduler and memory allocator state. It's represented
|
|
|
|
by type `p`. There are exactly `GOMAXPROCS` Ps. A P can be thought of
|
|
|
|
like a CPU in the OS scheduler and the contents of the `p` type like
|
|
|
|
per-CPU state. This is a good place to put state that needs to be
|
|
|
|
sharded for efficiency, but doesn't need to be per-thread or
|
|
|
|
per-goroutine.
|
|
|
|
|
|
|
|
The scheduler's job is to match up a G (the code to execute), an M
|
|
|
|
(where to execute it), and a P (the rights and resources to execute
|
|
|
|
it). When an M stops executing user Go code, for example by entering a
|
|
|
|
system call, it returns its P to the idle P pool. In order to resume
|
|
|
|
executing user Go code, for example on return from a system call, it
|
|
|
|
must acquire a P from the idle pool.
|
|
|
|
|
|
|
|
All `g`, `m`, and `p` objects are heap allocated, but are never freed,
|
|
|
|
so their memory remains type stable. As a result, the runtime can
|
|
|
|
avoid write barriers in the depths of the scheduler.
|
|
|
|
|
|
|
|
User stacks and system stacks
|
|
|
|
-----------------------------
|
|
|
|
|
|
|
|
Every non-dead G has a *user stack* associated with it, which is what
|
|
|
|
user Go code executes on. User stacks start small (e.g., 2K) and grow
|
|
|
|
or shrink dynamically.
|
|
|
|
|
|
|
|
Every M has a *system stack* associated with it (also known as the M's
|
|
|
|
"g0" stack because it's implemented as a stub G) and, on Unix
|
|
|
|
platforms, a *signal stack* (also known as the M's "gsignal" stack).
|
|
|
|
System and signal stacks cannot grow, but are large enough to execute
|
|
|
|
runtime and cgo code (8K in a pure Go binary; system-allocated in a
|
|
|
|
cgo binary).
|
|
|
|
|
|
|
|
Runtime code often temporarily switches to the system stack using
|
|
|
|
`systemstack`, `mcall`, or `asmcgocall` to perform tasks that must not
|
|
|
|
be preempted, that must not grow the user stack, or that switch user
|
|
|
|
goroutines. Code running on the system stack is implicitly
|
|
|
|
non-preemptible and the garbage collector does not scan system stacks.
|
|
|
|
While running on the system stack, the current user stack is not used
|
|
|
|
for execution.
|
|
|
|
|
|
|
|
`getg()` and `getg().m.curg`
|
|
|
|
----------------------------
|
|
|
|
|
|
|
|
To get the current user `g`, use `getg().m.curg`.
|
|
|
|
|
|
|
|
`getg()` alone returns the current `g`, but when executing on the
|
|
|
|
system or signal stacks, this will return the current M's "g0" or
|
|
|
|
"gsignal", respectively. This is usually not what you want.
|
|
|
|
|
|
|
|
To determine if you're running on the user stack or the system stack,
|
|
|
|
use `getg() == getg().m.curg`.
|
|
|
|
|
|
|
|
Error handling and reporting
|
|
|
|
============================
|
|
|
|
|
|
|
|
Errors that can reasonably be recovered from in user code should use
|
|
|
|
`panic` like usual. However, there are some situations where `panic`
|
|
|
|
will cause an immediate fatal error, such as when called on the system
|
|
|
|
stack or when called during `mallocgc`.
|
|
|
|
|
|
|
|
Most errors in the runtime are not recoverable. For these, use
|
|
|
|
`throw`, which dumps the traceback and immediately terminates the
|
|
|
|
process. In general, `throw` should be passed a string constant to
|
|
|
|
avoid allocating in perilous situations. By convention, additional
|
|
|
|
details are printed before `throw` using `print` or `println` and the
|
|
|
|
messages are prefixed with "runtime:".
|
|
|
|
|
|
|
|
For runtime error debugging, it's useful to run with
|
|
|
|
`GOTRACEBACK=system` or `GOTRACEBACK=crash`.
|
|
|
|
|
|
|
|
Synchronization
|
|
|
|
===============
|
|
|
|
|
|
|
|
The runtime has multiple synchronization mechanisms. They differ in
|
|
|
|
semantics and, in particular, in whether they interact with the
|
|
|
|
goroutine scheduler or the OS scheduler.
|
|
|
|
|
|
|
|
The simplest is `mutex`, which is manipulated using `lock` and
|
|
|
|
`unlock`. This should be used to protect shared structures for short
|
|
|
|
periods. Blocking on a `mutex` directly blocks the M, without
|
|
|
|
interacting with the Go scheduler. This means it is safe to use from
|
|
|
|
the lowest levels of the runtime, but also prevents any associated G
|
2017-06-28 13:54:46 -06:00
|
|
|
and P from being rescheduled. `rwmutex` is similar.
|
2016-12-12 11:56:11 -07:00
|
|
|
|
|
|
|
For one-shot notifications, use `note`, which provides `notesleep` and
|
|
|
|
`notewakeup`. Unlike traditional UNIX `sleep`/`wakeup`, `note`s are
|
|
|
|
race-free, so `notesleep` returns immediately if the `notewakeup` has
|
|
|
|
already happened. A `note` can be reset after use with `noteclear`,
|
|
|
|
which must not race with a sleep or wakeup. Like `mutex`, blocking on
|
|
|
|
a `note` blocks the M. However, there are different ways to sleep on a
|
|
|
|
`note`:`notesleep` also prevents rescheduling of any associated G and
|
|
|
|
P, while `notetsleepg` acts like a blocking system call that allows
|
|
|
|
the P to be reused to run another G. This is still less efficient than
|
|
|
|
blocking the G directly since it consumes an M.
|
|
|
|
|
|
|
|
To interact directly with the goroutine scheduler, use `gopark` and
|
|
|
|
`goready`. `gopark` parks the current goroutine—putting it in the
|
|
|
|
"waiting" state and removing it from the scheduler's run queue—and
|
|
|
|
schedules another goroutine on the current M/P. `goready` puts a
|
|
|
|
parked goroutine back in the "runnable" state and adds it to the run
|
|
|
|
queue.
|
|
|
|
|
|
|
|
In summary,
|
|
|
|
|
|
|
|
<table>
|
|
|
|
<tr><th></th><th colspan="3">Blocks</th></tr>
|
|
|
|
<tr><th>Interface</th><th>G</th><th>M</th><th>P</th></tr>
|
2017-06-28 13:54:46 -06:00
|
|
|
<tr><td>(rw)mutex</td><td>Y</td><td>Y</td><td>Y</td></tr>
|
2016-12-12 11:56:11 -07:00
|
|
|
<tr><td>note</td><td>Y</td><td>Y</td><td>Y/N</td></tr>
|
|
|
|
<tr><td>park</td><td>Y</td><td>N</td><td>N</td></tr>
|
|
|
|
</table>
|
2016-10-10 14:46:28 -06:00
|
|
|
|
2016-10-14 12:06:38 -06:00
|
|
|
Unmanaged memory
|
|
|
|
================
|
|
|
|
|
|
|
|
In general, the runtime tries to use regular heap allocation. However,
|
|
|
|
in some cases the runtime must allocate objects outside of the garbage
|
|
|
|
collected heap, in *unmanaged memory*. This is necessary if the
|
|
|
|
objects are part of the memory manager itself or if they must be
|
|
|
|
allocated in situations where the caller may not have a P.
|
|
|
|
|
|
|
|
There are three mechanisms for allocating unmanaged memory:
|
|
|
|
|
|
|
|
* sysAlloc obtains memory directly from the OS. This comes in whole
|
|
|
|
multiples of the system page size, but it can be freed with sysFree.
|
|
|
|
|
|
|
|
* persistentalloc combines multiple smaller allocations into a single
|
|
|
|
sysAlloc to avoid fragmentation. However, there is no way to free
|
|
|
|
persistentalloced objects (hence the name).
|
|
|
|
|
|
|
|
* fixalloc is a SLAB-style allocator that allocates objects of a fixed
|
|
|
|
size. fixalloced objects can be freed, but this memory can only be
|
|
|
|
reused by the same fixalloc pool, so it can only be reused for
|
|
|
|
objects of the same type.
|
|
|
|
|
|
|
|
In general, types that are allocated using any of these should be
|
|
|
|
marked `//go:notinheap` (see below).
|
|
|
|
|
|
|
|
Objects that are allocated in unmanaged memory **must not** contain
|
|
|
|
heap pointers unless the following rules are also obeyed:
|
|
|
|
|
|
|
|
1. Any pointers from unmanaged memory to the heap must be added as
|
|
|
|
explicit garbage collection roots in `runtime.markroot`.
|
|
|
|
|
|
|
|
2. If the memory is reused, the heap pointers must be zero-initialized
|
|
|
|
before they become visible as GC roots. Otherwise, the GC may
|
|
|
|
observe stale heap pointers. See "Zero-initialization versus
|
|
|
|
zeroing".
|
|
|
|
|
|
|
|
Zero-initialization versus zeroing
|
|
|
|
==================================
|
|
|
|
|
|
|
|
There are two types of zeroing in the runtime, depending on whether
|
|
|
|
the memory is already initialized to a type-safe state.
|
|
|
|
|
|
|
|
If memory is not in a type-safe state, meaning it potentially contains
|
|
|
|
"garbage" because it was just allocated and it is being initialized
|
|
|
|
for first use, then it must be *zero-initialized* using
|
|
|
|
`memclrNoHeapPointers` or non-pointer writes. This does not perform
|
|
|
|
write barriers.
|
|
|
|
|
|
|
|
If memory is already in a type-safe state and is simply being set to
|
|
|
|
the zero value, this must be done using regular writes, `typedmemclr`,
|
|
|
|
or `memclrHasPointers`. This performs write barriers.
|
|
|
|
|
2016-10-10 14:46:28 -06:00
|
|
|
Runtime-only compiler directives
|
|
|
|
================================
|
|
|
|
|
|
|
|
In addition to the "//go:" directives documented in "go doc compile",
|
|
|
|
the compiler supports additional directives only in the runtime.
|
|
|
|
|
|
|
|
go:systemstack
|
|
|
|
--------------
|
|
|
|
|
|
|
|
`go:systemstack` indicates that a function must run on the system
|
|
|
|
stack. This is checked dynamically by a special function prologue.
|
|
|
|
|
|
|
|
go:nowritebarrier
|
|
|
|
-----------------
|
|
|
|
|
|
|
|
`go:nowritebarrier` directs the compiler to emit an error if the
|
|
|
|
following function contains any write barriers. (It *does not*
|
|
|
|
suppress the generation of write barriers; it is simply an assertion.)
|
|
|
|
|
|
|
|
Usually you want `go:nowritebarrierrec`. `go:nowritebarrier` is
|
|
|
|
primarily useful in situations where it's "nice" not to have write
|
|
|
|
barriers, but not required for correctness.
|
|
|
|
|
|
|
|
go:nowritebarrierrec and go:yeswritebarrierrec
|
|
|
|
----------------------------------------------
|
|
|
|
|
|
|
|
`go:nowritebarrierrec` directs the compiler to emit an error if the
|
|
|
|
following function or any function it calls recursively, up to a
|
|
|
|
`go:yeswritebarrierrec`, contains a write barrier.
|
|
|
|
|
|
|
|
Logically, the compiler floods the call graph starting from each
|
|
|
|
`go:nowritebarrierrec` function and produces an error if it encounters
|
|
|
|
a function containing a write barrier. This flood stops at
|
|
|
|
`go:yeswritebarrierrec` functions.
|
|
|
|
|
|
|
|
`go:nowritebarrierrec` is used in the implementation of the write
|
|
|
|
barrier to prevent infinite loops.
|
|
|
|
|
|
|
|
Both directives are used in the scheduler. The write barrier requires
|
|
|
|
an active P (`getg().m.p != nil`) and scheduler code often runs
|
|
|
|
without an active P. In this case, `go:nowritebarrierrec` is used on
|
|
|
|
functions that release the P or may run without a P and
|
|
|
|
`go:yeswritebarrierrec` is used when code re-acquires an active P.
|
|
|
|
Since these are function-level annotations, code that releases or
|
|
|
|
acquires a P may need to be split across two functions.
|
cmd/compile: add go:notinheap type pragma
This adds a //go:notinheap pragma for declarations of types that must
not be heap allocated. We ensure these rules by disallowing new(T),
make([]T), append([]T), or implicit allocation of T, by disallowing
conversions to notinheap types, and by propagating notinheap to any
struct or array that contains notinheap elements.
The utility of this pragma is that we can eliminate write barriers for
writes to pointers to go:notinheap types, since the write barrier is
guaranteed to be a no-op. This will let us mark several scheduler and
memory allocator structures as go:notinheap, which will let us
disallow write barriers in the scheduler and memory allocator much
more thoroughly and also eliminate some problematic hybrid write
barriers.
This also makes go:nowritebarrierrec and go:yeswritebarrierrec much
more powerful. Currently we use go:nowritebarrier all over the place,
but it's almost never what you actually want: when write barriers are
illegal, they're typically illegal for a whole dynamic scope. Partly
this is because go:nowritebarrier has been around longer, but it's
also because go:nowritebarrierrec couldn't be used in situations that
had no-op write barriers or where some nested scope did allow write
barriers. go:notinheap eliminates many no-op write barriers and
go:yeswritebarrierrec makes it possible to opt back in to write
barriers, so these two changes will let us use go:nowritebarrierrec
far more liberally.
This updates #13386, which is about controlling pointers from non-GC'd
memory to GC'd memory. That would require some additional pragma (or
pragmas), but could build on this pragma.
Change-Id: I6314f8f4181535dd166887c9ec239977b54940bd
Reviewed-on: https://go-review.googlesource.com/30939
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-10-11 20:53:27 -06:00
|
|
|
|
|
|
|
go:notinheap
|
|
|
|
------------
|
|
|
|
|
|
|
|
`go:notinheap` applies to type declarations. It indicates that a type
|
2017-03-16 12:46:53 -06:00
|
|
|
must never be allocated from the GC'd heap. Specifically, pointers to
|
|
|
|
this type must always fail the `runtime.inheap` check. The type may be
|
|
|
|
used for global variables, for stack variables, or for objects in
|
|
|
|
unmanaged memory (e.g., allocated with `sysAlloc`, `persistentalloc`,
|
|
|
|
`fixalloc`, or from a manually-managed span). Specifically:
|
cmd/compile: add go:notinheap type pragma
This adds a //go:notinheap pragma for declarations of types that must
not be heap allocated. We ensure these rules by disallowing new(T),
make([]T), append([]T), or implicit allocation of T, by disallowing
conversions to notinheap types, and by propagating notinheap to any
struct or array that contains notinheap elements.
The utility of this pragma is that we can eliminate write barriers for
writes to pointers to go:notinheap types, since the write barrier is
guaranteed to be a no-op. This will let us mark several scheduler and
memory allocator structures as go:notinheap, which will let us
disallow write barriers in the scheduler and memory allocator much
more thoroughly and also eliminate some problematic hybrid write
barriers.
This also makes go:nowritebarrierrec and go:yeswritebarrierrec much
more powerful. Currently we use go:nowritebarrier all over the place,
but it's almost never what you actually want: when write barriers are
illegal, they're typically illegal for a whole dynamic scope. Partly
this is because go:nowritebarrier has been around longer, but it's
also because go:nowritebarrierrec couldn't be used in situations that
had no-op write barriers or where some nested scope did allow write
barriers. go:notinheap eliminates many no-op write barriers and
go:yeswritebarrierrec makes it possible to opt back in to write
barriers, so these two changes will let us use go:nowritebarrierrec
far more liberally.
This updates #13386, which is about controlling pointers from non-GC'd
memory to GC'd memory. That would require some additional pragma (or
pragmas), but could build on this pragma.
Change-Id: I6314f8f4181535dd166887c9ec239977b54940bd
Reviewed-on: https://go-review.googlesource.com/30939
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
2016-10-11 20:53:27 -06:00
|
|
|
|
|
|
|
1. `new(T)`, `make([]T)`, `append([]T, ...)` and implicit heap
|
|
|
|
allocation of T are disallowed. (Though implicit allocations are
|
|
|
|
disallowed in the runtime anyway.)
|
|
|
|
|
|
|
|
2. A pointer to a regular type (other than `unsafe.Pointer`) cannot be
|
|
|
|
converted to a pointer to a `go:notinheap` type, even if they have
|
|
|
|
the same underlying type.
|
|
|
|
|
|
|
|
3. Any type that contains a `go:notinheap` type is itself
|
|
|
|
`go:notinheap`. Structs and arrays are `go:notinheap` if their
|
|
|
|
elements are. Maps and channels of `go:notinheap` types are
|
|
|
|
disallowed. To keep things explicit, any type declaration where the
|
|
|
|
type is implicitly `go:notinheap` must be explicitly marked
|
|
|
|
`go:notinheap` as well.
|
|
|
|
|
|
|
|
4. Write barriers on pointers to `go:notinheap` types can be omitted.
|
|
|
|
|
|
|
|
The last point is the real benefit of `go:notinheap`. The runtime uses
|
|
|
|
it for low-level internal structures to avoid memory barriers in the
|
|
|
|
scheduler and the memory allocator where they are illegal or simply
|
|
|
|
inefficient. This mechanism is reasonably safe and does not compromise
|
|
|
|
the readability of the runtime.
|