mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: make it possible to exit Go-created threads
Currently, threads created by the runtime exist until the whole program exits. For #14592 and #20395, we want to be able to exit and clean up threads created by the runtime. This commit implements that mechanism. The main difficulty is how to clean up the g0 stack. In cgo mode and on Solaris and Windows where the OS manages thread stacks, we simply arrange to return from mstart and let the system clean up the thread. If the runtime allocated the g0 stack, then we use a new exitThread syscall wrapper that arranges to clear a flag in the M once the stack can safely be reaped and call the thread termination syscall. exitThread is based on the existing exit1 wrapper, which was always meant to terminate the calling thread. However, exit1 has never been used since it was introduced 9 years ago, so it was broken on several platforms. exitThread also has the additional complication of having to flag that the stack is unused, which requires some tricks on platforms that use the stack for syscalls. This still leaves the problem of how to reap the unused g0 stacks. For this, we move the M from allm to a new freem list as part of the M exiting. Later, allocm scans the freem list, finds Ms that are marked as done with their stack, removes these from the list and frees their g0 stacks. This also allows these Ms to be garbage collected. This CL does not yet use any of this functionality. Follow-up CLs will. Likewise, there are no new tests in this CL because we'll need follow-up functionality to test it. Change-Id: Ic851ee74227b6d39c6fc1219fc71b45d3004bc63 Reviewed-on: https://go-review.googlesource.com/46037 Run-TryBot: Austin Clements <austin@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
parent
a9c3d09d0e
commit
eff2b2620d
38 changed files with 476 additions and 102 deletions
|
|
@ -169,9 +169,13 @@ func efaceOf(ep *interface{}) *eface {
|
|||
// a word that is completely ignored by the GC than to have one for which
|
||||
// only a few updates are ignored.
|
||||
//
|
||||
// Gs, Ms, and Ps are always reachable via true pointers in the
|
||||
// allgs, allm, and allp lists or (during allocation before they reach those lists)
|
||||
// Gs and Ps are always reachable via true pointers in the
|
||||
// allgs and allp lists or (during allocation before they reach those lists)
|
||||
// from stack variables.
|
||||
//
|
||||
// Ms are always reachable via true pointers either from allm or
|
||||
// freem. Unlike Gs and Ps we do free Ms, so it's important that
|
||||
// nothing ever hold an muintptr across a safe point.
|
||||
|
||||
// A guintptr holds a goroutine pointer, but typed as a uintptr
|
||||
// to bypass write barriers. It is used in the Gobuf goroutine state
|
||||
|
|
@ -221,6 +225,15 @@ func (pp puintptr) ptr() *p { return (*p)(unsafe.Pointer(pp)) }
|
|||
//go:nosplit
|
||||
func (pp *puintptr) set(p *p) { *pp = puintptr(unsafe.Pointer(p)) }
|
||||
|
||||
// muintptr is a *m that is not tracked by the garbage collector.
|
||||
//
|
||||
// Because we do free Ms, there are some additional constrains on
|
||||
// muintptrs:
|
||||
//
|
||||
// 1. Never hold an muintptr locally across a safe point.
|
||||
//
|
||||
// 2. Any muintptr in the heap must be owned by the M itself so it can
|
||||
// ensure it is not in use when the last true *m is released.
|
||||
type muintptr uintptr
|
||||
|
||||
//go:nosplit
|
||||
|
|
@ -413,7 +426,8 @@ type m struct {
|
|||
inwb bool // m is executing a write barrier
|
||||
newSigstack bool // minit on C thread called sigaltstack
|
||||
printlock int8
|
||||
incgo bool // m is executing a cgo call
|
||||
incgo bool // m is executing a cgo call
|
||||
freeWait uint32 // if == 0, safe to free g0 and delete m (atomic)
|
||||
fastrand [2]uint32
|
||||
needextram bool
|
||||
traceback uint8
|
||||
|
|
@ -440,6 +454,7 @@ type m struct {
|
|||
startingtrace bool
|
||||
syscalltick uint32
|
||||
thread uintptr // thread handle
|
||||
freelink *m // on sched.freem
|
||||
|
||||
// these are here because they are too large to be on the stack
|
||||
// of low-level NOSPLIT functions.
|
||||
|
|
@ -528,12 +543,16 @@ type schedt struct {
|
|||
|
||||
lock mutex
|
||||
|
||||
// When increasing nmidle, nmidlelocked, nmsys, or nmfreed, be
|
||||
// sure to call checkdead().
|
||||
|
||||
midle muintptr // idle m's waiting for work
|
||||
nmidle int32 // number of idle m's waiting for work
|
||||
nmidlelocked int32 // number of locked m's waiting for work
|
||||
mnext int64 // number of m's that have been created and next M ID
|
||||
maxmcount int32 // maximum number of m's allowed (or die)
|
||||
nmsys int32 // number of system m's not counted for deadlock
|
||||
nmfreed int64 // cumulative number of freed m's
|
||||
|
||||
ngsys uint32 // number of system goroutines; updated atomically
|
||||
|
||||
|
|
@ -560,6 +579,10 @@ type schedt struct {
|
|||
deferlock mutex
|
||||
deferpool [5]*_defer
|
||||
|
||||
// freem is the list of m's waiting to be freed when their
|
||||
// m.exited is set. Linked through m.freelink.
|
||||
freem *m
|
||||
|
||||
gcwaiting uint32 // gc is waiting to run
|
||||
stopwait int32
|
||||
stopnote note
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue