diff options
Diffstat (limited to 'src/runtime/proc.go')
| -rw-r--r-- | src/runtime/proc.go | 43 |
1 files changed, 39 insertions, 4 deletions
diff --git a/src/runtime/proc.go b/src/runtime/proc.go index ca78587aad..5a942a6831 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -490,8 +490,29 @@ func lockedOSThread() bool { } var ( - allgs []*g + // allgs contains all Gs ever created (including dead Gs), and thus + // never shrinks. + // + // Access via the slice is protected by allglock or stop-the-world. + // Readers that cannot take the lock may (carefully!) use the atomic + // variables below. allglock mutex + allgs []*g + + // allglen and allgptr are atomic variables that contain len(allg) and + // &allg[0] respectively. Proper ordering depends on totally-ordered + // loads and stores. Writes are protected by allglock. + // + // allgptr is updated before allglen. Readers should read allglen + // before allgptr to ensure that allglen is always <= len(allgptr). New + // Gs appended during the race can be missed. For a consistent view of + // all Gs, allglock must be held. + // + // allgptr copies should always be stored as a concrete type or + // unsafe.Pointer, not uintptr, to ensure that GC can still reach it + // even if it points to a stale array. + allglen uintptr + allgptr **g ) func allgadd(gp *g) { @@ -501,10 +522,25 @@ func allgadd(gp *g) { lock(&allglock) allgs = append(allgs, gp) - allglen = uintptr(len(allgs)) + if &allgs[0] != allgptr { + atomicstorep(unsafe.Pointer(&allgptr), unsafe.Pointer(&allgs[0])) + } + atomic.Storeuintptr(&allglen, uintptr(len(allgs))) unlock(&allglock) } +// atomicAllG returns &allgs[0] and len(allgs) for use with atomicAllGIndex. +func atomicAllG() (**g, uintptr) { + length := atomic.Loaduintptr(&allglen) + ptr := (**g)(atomic.Loadp(unsafe.Pointer(&allgptr))) + return ptr, length +} + +// atomicAllGIndex returns ptr[i] with the allgptr returned from atomicAllG. +func atomicAllGIndex(ptr **g, i uintptr) *g { + return *(**g)(add(unsafe.Pointer(ptr), i*sys.PtrSize)) +} + const ( // Number of goroutine ids to grab from sched.goidgen to local per-P cache at once. // 16 seems to provide enough amortization, but other than that it's mostly arbitrary number. @@ -4266,7 +4302,7 @@ func badunlockosthread() { } func gcount() int32 { - n := int32(allglen) - sched.gFree.n - int32(atomic.Load(&sched.ngsys)) + n := int32(atomic.Loaduintptr(&allglen)) - sched.gFree.n - int32(atomic.Load(&sched.ngsys)) for _, _p_ := range allp { n -= _p_.gFree.n } @@ -4970,7 +5006,6 @@ func checkdead() { case _Grunnable, _Grunning, _Gsyscall: - unlock(&allglock) print("runtime: checkdead: find g ", gp.goid, " in status ", s, "\n") throw("checkdead: runnable g") } |
