runtime: remove __go_alloc and __go_free

Move allocg and handling of allgs slice from C to Go.
    
    Reviewed-on: https://go-review.googlesource.com/34797

From-SVN: r244036
This commit is contained in:
Ian Lance Taylor 2017-01-03 22:58:48 +00:00
parent c65f76af36
commit 0f2a6e84c6
18 changed files with 352 additions and 443 deletions

View File

@ -1,4 +1,4 @@
eac28020ee4b2532d4cd43f448fe612e84e0a108 dfe446c5a54ca0febabb81b542cc4e634c6f5c30
The first line of this file holds the git revision number of the last The first line of this file holds the git revision number of the last
merge done from the gofrontend repository. merge done from the gofrontend repository.

View File

@ -556,7 +556,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
stopTheWorld("profile") stopTheWorld("profile")
n = 1 n = 1
for _, gp1 := range allgs() { for _, gp1 := range allgs {
if isOK(gp1) { if isOK(gp1) {
n++ n++
} }
@ -571,7 +571,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
r = r[1:] r = r[1:]
// Save other goroutines. // Save other goroutines.
for _, gp1 := range allgs() { for _, gp1 := range allgs {
if isOK(gp1) { if isOK(gp1) {
if len(r) == 0 { if len(r) == 0 {
// Should be impossible, but better to return a // Should be impossible, but better to return a

View File

@ -11,15 +11,18 @@ import (
// Functions temporarily called by C code. // Functions temporarily called by C code.
//go:linkname newextram runtime.newextram //go:linkname newextram runtime.newextram
//go:linkname checkdead runtime.checkdead
//go:linkname schedtrace runtime.schedtrace
//go:linkname allgadd runtime.allgadd
// Functions temporarily in C that have not yet been ported. // Functions temporarily in C that have not yet been ported.
func allocm(*p, bool, *unsafe.Pointer, *uintptr) *m func allocm(*p, bool, *unsafe.Pointer, *uintptr) *m
func malg(bool, bool, *unsafe.Pointer, *uintptr) *g func malg(bool, bool, *unsafe.Pointer, *uintptr) *g
func allgadd(*g)
// C functions for ucontext management. // C functions for ucontext management.
func setGContext() func setGContext()
func makeGContext(*g, unsafe.Pointer, uintptr) func makeGContext(*g, unsafe.Pointer, uintptr)
func getTraceback(me, gp *g)
// main_init_done is a signal used by cgocallbackg that initialization // main_init_done is a signal used by cgocallbackg that initialization
// has been completed. It is made before _cgo_notify_runtime_init_done, // has been completed. It is made before _cgo_notify_runtime_init_done,
@ -27,6 +30,39 @@ func makeGContext(*g, unsafe.Pointer, uintptr)
// it is closed, meaning cgocallbackg can reliably receive from it. // it is closed, meaning cgocallbackg can reliably receive from it.
var main_init_done chan bool var main_init_done chan bool
var (
allgs []*g
allglock mutex
)
func allgadd(gp *g) {
if readgstatus(gp) == _Gidle {
throw("allgadd: bad status Gidle")
}
lock(&allglock)
allgs = append(allgs, gp)
allglen = uintptr(len(allgs))
// Grow GC rescan list if necessary.
if len(allgs) > cap(work.rescan.list) {
lock(&work.rescan.lock)
l := work.rescan.list
// Let append do the heavy lifting, but keep the
// length the same.
work.rescan.list = append(l[:cap(l)], 0)[:len(l)]
unlock(&work.rescan.lock)
}
unlock(&allglock)
}
// All reads and writes of g's status go through readgstatus, casgstatus
// castogscanstatus, casfrom_Gscanstatus.
//go:nosplit
func readgstatus(gp *g) uint32 {
return atomic.Load(&gp.atomicstatus)
}
// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus // If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
// and casfrom_Gscanstatus instead. // and casfrom_Gscanstatus instead.
// casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that // casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that
@ -328,3 +364,170 @@ func lockextra(nilokay bool) *m {
func unlockextra(mp *m) { func unlockextra(mp *m) {
atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp))) atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp)))
} }
// Check for deadlock situation.
// The check is based on number of running M's, if 0 -> deadlock.
func checkdead() {
// For -buildmode=c-shared or -buildmode=c-archive it's OK if
// there are no running goroutines. The calling program is
// assumed to be running.
if islibrary || isarchive {
return
}
// If we are dying because of a signal caught on an already idle thread,
// freezetheworld will cause all running threads to block.
// And runtime will essentially enter into deadlock state,
// except that there is a thread that will call exit soon.
if panicking > 0 {
return
}
// -1 for sysmon
run := sched.mcount - sched.nmidle - sched.nmidlelocked - 1
if run > 0 {
return
}
if run < 0 {
print("runtime: checkdead: nmidle=", sched.nmidle, " nmidlelocked=", sched.nmidlelocked, " mcount=", sched.mcount, "\n")
throw("checkdead: inconsistent counts")
}
grunning := 0
lock(&allglock)
for i := 0; i < len(allgs); i++ {
gp := allgs[i]
if isSystemGoroutine(gp) {
continue
}
s := readgstatus(gp)
switch s &^ _Gscan {
case _Gwaiting:
grunning++
case _Grunnable,
_Grunning,
_Gsyscall:
unlock(&allglock)
print("runtime: checkdead: find g ", gp.goid, " in status ", s, "\n")
throw("checkdead: runnable g")
}
}
unlock(&allglock)
if grunning == 0 { // possible if main goroutine calls runtime·Goexit()
throw("no goroutines (main called runtime.Goexit) - deadlock!")
}
// Maybe jump time forward for playground.
gp := timejump()
if gp != nil {
// Temporarily commented out for gccgo.
// For gccgo this code will never run anyhow.
// casgstatus(gp, _Gwaiting, _Grunnable)
// globrunqput(gp)
// _p_ := pidleget()
// if _p_ == nil {
// throw("checkdead: no p for timer")
// }
// mp := mget()
// if mp == nil {
// // There should always be a free M since
// // nothing is running.
// throw("checkdead: no m for timer")
// }
// nmp.nextp.set(_p_)
// notewakeup(&mp.park)
// return
}
getg().m.throwing = -1 // do not dump full stacks
throw("all goroutines are asleep - deadlock!")
}
var starttime int64
func schedtrace(detailed bool) {
now := nanotime()
if starttime == 0 {
starttime = now
}
gomaxprocs := int32(GOMAXPROCS(0))
lock(&sched.lock)
print("SCHED ", (now-starttime)/1e6, "ms: gomaxprocs=", gomaxprocs, " idleprocs=", sched.npidle, " threads=", sched.mcount, " spinningthreads=", sched.nmspinning, " idlethreads=", sched.nmidle, " runqueue=", sched.runqsize)
if detailed {
print(" gcwaiting=", sched.gcwaiting, " nmidlelocked=", sched.nmidlelocked, " stopwait=", sched.stopwait, " sysmonwait=", sched.sysmonwait, "\n")
}
// We must be careful while reading data from P's, M's and G's.
// Even if we hold schedlock, most data can be changed concurrently.
// E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil.
for i := int32(0); i < gomaxprocs; i++ {
_p_ := allp[i]
if _p_ == nil {
continue
}
mp := _p_.m.ptr()
h := atomic.Load(&_p_.runqhead)
t := atomic.Load(&_p_.runqtail)
if detailed {
id := int32(-1)
if mp != nil {
id = mp.id
}
print(" P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gfreecnt, "\n")
} else {
// In non-detailed mode format lengths of per-P run queues as:
// [len1 len2 len3 len4]
print(" ")
if i == 0 {
print("[")
}
print(t - h)
if i == gomaxprocs-1 {
print("]\n")
}
}
}
if !detailed {
unlock(&sched.lock)
return
}
for mp := allm(); mp != nil; mp = mp.alllink {
_p_ := mp.p.ptr()
gp := mp.curg
lockedg := mp.lockedg
id1 := int32(-1)
if _p_ != nil {
id1 = _p_.id
}
id2 := int64(-1)
if gp != nil {
id2 = gp.goid
}
id3 := int64(-1)
if lockedg != nil {
id3 = lockedg.goid
}
print(" M", mp.id, ": p=", id1, " curg=", id2, " mallocing=", mp.mallocing, " throwing=", mp.throwing, " preemptoff=", mp.preemptoff, ""+" locks=", mp.locks, " dying=", mp.dying, " helpgc=", mp.helpgc, " spinning=", mp.spinning, " blocked=", mp.blocked, " lockedg=", id3, "\n")
}
lock(&allglock)
for gi := 0; gi < len(allgs); gi++ {
gp := allgs[gi]
mp := gp.m
lockedm := gp.lockedm
id1 := int32(-1)
if mp != nil {
id1 = mp.id
}
id2 := int32(-1)
if lockedm != nil {
id2 = lockedm.id
}
print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n")
}
unlock(&allglock)
unlock(&sched.lock)
}

View File

@ -755,9 +755,13 @@ const _TracebackMaxFrames = 100
var ( var (
// emptystring string // emptystring string
// allglen uintptr
allglen uintptr
// allm *m // allm *m
// allp [_MaxGomaxprocs + 1]*p
allp [_MaxGomaxprocs + 1]*p
// gomaxprocs int32 // gomaxprocs int32
panicking uint32 panicking uint32

View File

@ -476,12 +476,6 @@ func UnlockOSThread()
func lockOSThread() func lockOSThread()
func unlockOSThread() func unlockOSThread()
func allm() *m func allm() *m
func allgs() []*g
//go:nosplit
func readgstatus(gp *g) uint32 {
return atomic.Load(&gp.atomicstatus)
}
// Temporary for gccgo until we port malloc.go // Temporary for gccgo until we port malloc.go
func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer
@ -489,9 +483,6 @@ func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer
// Temporary for gccgo until we port mheap.go // Temporary for gccgo until we port mheap.go
func setprofilebucket(p unsafe.Pointer, b *bucket) func setprofilebucket(p unsafe.Pointer, b *bucket)
// Currently in proc.c.
func tracebackothers(*g)
// Temporary for gccgo until we port mgc.go. // Temporary for gccgo until we port mgc.go.
func setgcpercent(int32) int32 func setgcpercent(int32) int32
@ -530,9 +521,7 @@ func getZerobase() *uintptr {
// Temporary for gccgo until we port proc.go. // Temporary for gccgo until we port proc.go.
func sigprof() func sigprof()
func mcount() int32 func mcount() int32
func gcount() int32
func goexit1() func goexit1()
func schedtrace(bool)
func freezetheworld() func freezetheworld()
// Get signal trampoline, written in C. // Get signal trampoline, written in C.
@ -562,6 +551,30 @@ func getCgoHasExtraM() *bool {
return &cgoHasExtraM return &cgoHasExtraM
} }
// Temporary for gccgo until we port proc.go.
//go:linkname getAllP runtime.getAllP
func getAllP() **p {
return &allp[0]
}
// Temporary for gccgo until we port proc.go.
//go:linkname allocg runtime.allocg
func allocg() *g {
return new(g)
}
// Temporary for gccgo until we port the garbage collector.
//go:linkname getallglen runtime.getallglen
func getallglen() uintptr {
return allglen
}
// Temporary for gccgo until we port the garbage collector.
//go:linkname getallg runtime.getallg
func getallg(i int) *g {
return allgs[i]
}
// Throw and rethrow an exception. // Throw and rethrow an exception.
func throwException() func throwException()
func rethrowException() func rethrowException()
@ -579,3 +592,27 @@ func getPanicking() uint32 {
// Temporary for gccgo until we port mcache.go. // Temporary for gccgo until we port mcache.go.
func allocmcache() *mcache func allocmcache() *mcache
// Temporary for gccgo until we port mgc.go.
// This is just so that allgadd will compile.
var work struct {
rescan struct {
lock mutex
list []guintptr
}
}
// gcount is temporary for gccgo until more of proc.go is ported.
// This is a copy of the C function we used to use.
func gcount() int32 {
n := int32(0)
lock(&allglock)
for _, gp := range allgs {
s := readgstatus(gp)
if s == _Grunnable || s == _Grunning || s == _Gsyscall || s == _Gwaiting {
n++
}
}
unlock(&allglock)
return n
}

View File

@ -171,3 +171,58 @@ func isSystemGoroutine(gp *g) bool {
// FIXME. // FIXME.
return false return false
} }
func tracebackothers(me *g) {
var tb tracebackg
tb.gp = me
level, _, _ := gotraceback()
// Show the current goroutine first, if we haven't already.
g := getg()
gp := g.m.curg
if gp != nil && gp != me {
print("\n")
goroutineheader(gp)
gp.traceback = &tb
getTraceback(me, gp)
printtrace(tb.locbuf[:tb.c], nil)
printcreatedby(gp)
}
lock(&allglock)
for _, gp := range allgs {
if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 {
continue
}
print("\n")
goroutineheader(gp)
// gccgo's only mechanism for doing a stack trace is
// _Unwind_Backtrace. And that only works for the
// current thread, not for other random goroutines.
// So we need to switch context to the goroutine, get
// the backtrace, and then switch back.
//
// This means that if g is running or in a syscall, we
// can't reliably print a stack trace. FIXME.
// Note: gp.m == g.m occurs when tracebackothers is
// called from a signal handler initiated during a
// systemstack call. The original G is still in the
// running state, and we want to print its stack.
if gp.m != g.m && readgstatus(gp)&^_Gscan == _Grunning {
print("\tgoroutine running on other thread; stack unavailable\n")
printcreatedby(gp)
} else if readgstatus(gp)&^_Gscan == _Gsyscall {
print("\tgoroutine in C code; stack unavailable\n")
printcreatedby(gp)
} else {
gp.traceback = &tb
getTraceback(me, gp)
printtrace(tb.locbuf[:tb.c], nil)
printcreatedby(gp)
}
}
unlock(&allglock)
}

View File

@ -13,7 +13,6 @@
#include <unistd.h> #include <unistd.h>
#include "runtime.h" #include "runtime.h"
#include "go-alloc.h"
#include "array.h" #include "array.h"
#include "arch.h" #include "arch.h"
#include "malloc.h" #include "malloc.h"

View File

@ -15,7 +15,6 @@
#endif #endif
#include "runtime.h" #include "runtime.h"
#include "go-alloc.h"
#include "array.h" #include "array.h"
#include "arch.h" #include "arch.h"
#include "malloc.h" #include "malloc.h"

View File

@ -4,7 +4,6 @@
Use of this source code is governed by a BSD-style Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file. */ license that can be found in the LICENSE file. */
#include "go-alloc.h"
#include "runtime.h" #include "runtime.h"
#include "arch.h" #include "arch.h"
#include "malloc.h" #include "malloc.h"

View File

@ -9,7 +9,6 @@
#include <stdlib.h> #include <stdlib.h>
#include "runtime.h" #include "runtime.h"
#include "go-alloc.h"
#include "go-assert.h" #include "go-assert.h"
#include "go-type.h" #include "go-type.h"

View File

@ -14,7 +14,6 @@
#include "unwind-pe.h" #include "unwind-pe.h"
#include "runtime.h" #include "runtime.h"
#include "go-alloc.h"
/* The code for a Go exception. */ /* The code for a Go exception. */

View File

@ -311,8 +311,8 @@ dumpgs(void)
uint32 i; uint32 i;
// goroutines & stacks // goroutines & stacks
for(i = 0; i < runtime_allglen; i++) { for(i = 0; i < runtime_getallglen(); i++) {
gp = runtime_allg[i]; gp = runtime_getallg(i);
switch(gp->atomicstatus){ switch(gp->atomicstatus){
default: default:
runtime_printf("unexpected G.status %d\n", gp->atomicstatus); runtime_printf("unexpected G.status %d\n", gp->atomicstatus);

View File

@ -10,7 +10,6 @@ package runtime
#include <stddef.h> #include <stddef.h>
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include "go-alloc.h"
#include "runtime.h" #include "runtime.h"
#include "arch.h" #include "arch.h"
#include "malloc.h" #include "malloc.h"
@ -308,107 +307,6 @@ runtime_profilealloc(void *v, uintptr size)
runtime_MProf_Malloc(v, size); runtime_MProf_Malloc(v, size);
} }
void*
__go_alloc(uintptr size)
{
return runtime_mallocgc(size, 0, FlagNoInvokeGC);
}
// Free the object whose base pointer is v.
void
__go_free(void *v)
{
M *m;
int32 sizeclass;
MSpan *s;
MCache *c;
uintptr size;
if(v == nil)
return;
// If you change this also change mgc0.c:/^sweep,
// which has a copy of the guts of free.
m = runtime_m();
if(m->mallocing)
runtime_throw("malloc/free - deadlock");
m->mallocing = 1;
if(!runtime_mlookup(v, nil, nil, &s)) {
runtime_printf("free %p: not an allocated block\n", v);
runtime_throw("free runtime_mlookup");
}
size = s->elemsize;
sizeclass = s->sizeclass;
// Objects that are smaller than TinySize can be allocated using tiny alloc,
// if then such object is combined with an object with finalizer, we will crash.
if(size < TinySize)
runtime_throw("freeing too small block");
if(runtime_debug.allocfreetrace)
runtime_tracefree(v, size);
// Ensure that the span is swept.
// If we free into an unswept span, we will corrupt GC bitmaps.
runtime_MSpan_EnsureSwept(s);
if(s->specials != nil)
runtime_freeallspecials(s, v, size);
c = m->mcache;
if(sizeclass == 0) {
// Large object.
s->needzero = 1;
// Must mark v freed before calling unmarkspan and MHeap_Free:
// they might coalesce v into other spans and change the bitmap further.
runtime_markfreed(v);
runtime_unmarkspan(v, 1<<PageShift);
// NOTE(rsc,dvyukov): The original implementation of efence
// in CL 22060046 used SysFree instead of SysFault, so that
// the operating system would eventually give the memory
// back to us again, so that an efence program could run
// longer without running out of memory. Unfortunately,
// calling SysFree here without any kind of adjustment of the
// heap data structures means that when the memory does
// come back to us, we have the wrong metadata for it, either in
// the MSpan structures or in the garbage collection bitmap.
// Using SysFault here means that the program will run out of
// memory fairly quickly in efence mode, but at least it won't
// have mysterious crashes due to confused memory reuse.
// It should be possible to switch back to SysFree if we also
// implement and then call some kind of MHeap_DeleteSpan.
if(runtime_debug.efence)
runtime_SysFault((void*)(s->start<<PageShift), size);
else
runtime_MHeap_Free(&runtime_mheap, s, 1);
c->local_nlargefree++;
c->local_largefree += size;
} else {
// Small object.
if(size > 2*sizeof(uintptr))
((uintptr*)v)[1] = (uintptr)0xfeedfeedfeedfeedll; // mark as "needs to be zeroed"
else if(size > sizeof(uintptr))
((uintptr*)v)[1] = 0;
// Must mark v freed before calling MCache_Free:
// it might coalesce v and other blocks into a bigger span
// and change the bitmap further.
c->local_nsmallfree[sizeclass]++;
c->local_cachealloc -= size;
if(c->alloc[sizeclass] == s) {
// We own the span, so we can just add v to the freelist
runtime_markfreed(v);
((MLink*)v)->next = s->freelist;
s->freelist = v;
s->ref--;
} else {
// Someone else owns this span. Add to free queue.
runtime_MCache_Free(c, v, sizeclass, size);
}
}
m->mallocing = 0;
}
int32 int32
runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp) runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
{ {
@ -628,9 +526,6 @@ runtime_mallocinit(void)
// Initialize the rest of the allocator. // Initialize the rest of the allocator.
runtime_MHeap_Init(&runtime_mheap); runtime_MHeap_Init(&runtime_mheap);
runtime_m()->mcache = runtime_allocmcache(); runtime_m()->mcache = runtime_allocmcache();
// See if it works.
runtime_free(runtime_malloc(TinySize));
} }
void* void*

View File

@ -1279,7 +1279,6 @@ markroot(ParFor *desc, uint32 i)
// For gccgo we use this for all the other global roots. // For gccgo we use this for all the other global roots.
enqueue1(&wbuf, (Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0});
enqueue1(&wbuf, (Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0});
enqueue1(&wbuf, (Obj){(byte*)&runtime_allg, sizeof runtime_allg, 0});
enqueue1(&wbuf, (Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0});
enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0}); enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0});
enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0}); enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0});
@ -1334,9 +1333,9 @@ markroot(ParFor *desc, uint32 i)
default: default:
// the rest is scanning goroutine stacks // the rest is scanning goroutine stacks
if(i - RootCount >= runtime_allglen) if(i - RootCount >= runtime_getallglen())
runtime_throw("markroot: bad index"); runtime_throw("markroot: bad index");
gp = runtime_allg[i - RootCount]; gp = runtime_getallg(i - RootCount);
// remember when we've first observed the G blocked // remember when we've first observed the G blocked
// needed only to output in traceback // needed only to output in traceback
if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince == 0) if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince == 0)
@ -2243,7 +2242,7 @@ gc(struct gc_args *args)
work.nwait = 0; work.nwait = 0;
work.ndone = 0; work.ndone = 0;
work.nproc = runtime_gcprocs(); work.nproc = runtime_gcprocs();
runtime_parforsetup(work.markfor, work.nproc, RootCount + runtime_allglen, false, &markroot_funcval); runtime_parforsetup(work.markfor, work.nproc, RootCount + runtime_getallglen(), false, &markroot_funcval);
if(work.nproc > 1) { if(work.nproc > 1) {
runtime_noteclear(&work.alldone); runtime_noteclear(&work.alldone);
runtime_helpgc(work.nproc); runtime_helpgc(work.nproc);

View File

@ -5,6 +5,7 @@
// Parallel for algorithm. // Parallel for algorithm.
#include "runtime.h" #include "runtime.h"
#include "malloc.h"
#include "arch.h" #include "arch.h"
struct ParForThread struct ParForThread
@ -27,7 +28,7 @@ runtime_parforalloc(uint32 nthrmax)
// The ParFor object is followed by CacheLineSize padding // The ParFor object is followed by CacheLineSize padding
// and then nthrmax ParForThread. // and then nthrmax ParForThread.
desc = (ParFor*)runtime_malloc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread)); desc = (ParFor*)runtime_mallocgc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread), 0, FlagNoInvokeGC);
desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize); desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize);
desc->nthrmax = nthrmax; desc->nthrmax = nthrmax;
return desc; return desc;

View File

@ -361,6 +361,10 @@ enum
extern Sched* runtime_getsched() __asm__ (GOSYM_PREFIX "runtime.getsched"); extern Sched* runtime_getsched() __asm__ (GOSYM_PREFIX "runtime.getsched");
extern bool* runtime_getCgoHasExtraM() extern bool* runtime_getCgoHasExtraM()
__asm__ (GOSYM_PREFIX "runtime.getCgoHasExtraM"); __asm__ (GOSYM_PREFIX "runtime.getCgoHasExtraM");
extern P** runtime_getAllP()
__asm__ (GOSYM_PREFIX "runtime.getAllP");
extern G* allocg(void)
__asm__ (GOSYM_PREFIX "runtime.allocg");
Sched* runtime_sched; Sched* runtime_sched;
int32 runtime_gomaxprocs; int32 runtime_gomaxprocs;
@ -374,11 +378,6 @@ int32 runtime_ncpu;
bool runtime_precisestack; bool runtime_precisestack;
static int32 newprocs; static int32 newprocs;
static Lock allglock; // the following vars are protected by this lock or by stoptheworld
G** runtime_allg;
uintptr runtime_allglen;
static uintptr allgcap;
bool runtime_isarchive; bool runtime_isarchive;
void* runtime_mstart(void*); void* runtime_mstart(void*);
@ -403,7 +402,6 @@ static void startlockedm(G*);
static void sysmon(void); static void sysmon(void);
static uint32 retake(int64); static uint32 retake(int64);
static void incidlelocked(int32); static void incidlelocked(int32);
static void checkdead(void);
static void exitsyscall0(G*); static void exitsyscall0(G*);
static void park0(G*); static void park0(G*);
static void goexit0(G*); static void goexit0(G*);
@ -421,6 +419,8 @@ static bool exitsyscallfast(void);
void allgadd(G*) void allgadd(G*)
__asm__(GOSYM_PREFIX "runtime.allgadd"); __asm__(GOSYM_PREFIX "runtime.allgadd");
void checkdead(void)
__asm__(GOSYM_PREFIX "runtime.checkdead");
bool runtime_isstarted; bool runtime_isstarted;
@ -482,7 +482,7 @@ runtime_schedinit(void)
n = _MaxGomaxprocs; n = _MaxGomaxprocs;
procs = n; procs = n;
} }
runtime_allp = runtime_malloc((_MaxGomaxprocs+1)*sizeof(runtime_allp[0])); runtime_allp = runtime_getAllP();
procresize(procs); procresize(procs);
// Can not enable GC until all roots are registered. // Can not enable GC until all roots are registered.
@ -586,85 +586,25 @@ runtime_main(void* dummy __attribute__((unused)))
*(int32*)0 = 0; *(int32*)0 = 0;
} }
void void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");
runtime_tracebackothers(G * volatile me)
// getTraceback stores a traceback of gp in the g's traceback field
// and then returns to me. We expect that gp's traceback is not nil.
// It works by saving me's current context, and checking gp's traceback field.
// If gp's traceback field is not nil, it starts running gp.
// In places where we call getcontext, we check the traceback field.
// If it is not nil, we collect a traceback, and then return to the
// goroutine stored in the traceback field, which is me.
void getTraceback(G* me, G* gp)
{ {
G * volatile gp;
Traceback tb;
int32 traceback;
Slice slice;
volatile uintptr i;
tb.gp = me;
traceback = runtime_gotraceback(nil);
// Show the current goroutine first, if we haven't already.
if((gp = g->m->curg) != nil && gp != me) {
runtime_printf("\n");
runtime_goroutineheader(gp);
gp->traceback = &tb;
#ifdef USING_SPLIT_STACK #ifdef USING_SPLIT_STACK
__splitstack_getcontext(&me->stackcontext[0]); __splitstack_getcontext(&me->stackcontext[0]);
#endif #endif
getcontext(ucontext_arg(&me->context[0])); getcontext(ucontext_arg(&me->stackcontext[0]));
if(gp->traceback != nil) { if (gp->traceback != nil) {
runtime_gogo(gp); runtime_gogo(gp);
}
slice.__values = &tb.locbuf[0];
slice.__count = tb.c;
slice.__capacity = tb.c;
runtime_printtrace(slice, nil);
runtime_printcreatedby(gp);
} }
runtime_lock(&allglock);
for(i = 0; i < runtime_allglen; i++) {
gp = runtime_allg[i];
if(gp == me || gp == g->m->curg || gp->atomicstatus == _Gdead)
continue;
if(gp->issystem && traceback < 2)
continue;
runtime_printf("\n");
runtime_goroutineheader(gp);
// Our only mechanism for doing a stack trace is
// _Unwind_Backtrace. And that only works for the
// current thread, not for other random goroutines.
// So we need to switch context to the goroutine, get
// the backtrace, and then switch back.
// This means that if g is running or in a syscall, we
// can't reliably print a stack trace. FIXME.
if(gp->atomicstatus == _Grunning) {
runtime_printf("\tgoroutine running on other thread; stack unavailable\n");
runtime_printcreatedby(gp);
} else if(gp->atomicstatus == _Gsyscall) {
runtime_printf("\tgoroutine in C code; stack unavailable\n");
runtime_printcreatedby(gp);
} else {
gp->traceback = &tb;
#ifdef USING_SPLIT_STACK
__splitstack_getcontext(&me->stackcontext[0]);
#endif
getcontext(ucontext_arg(&me->context[0]));
if(gp->traceback != nil) {
runtime_gogo(gp);
}
slice.__values = &tb.locbuf[0];
slice.__count = tb.c;
slice.__capacity = tb.c;
runtime_printtrace(slice, nil);
runtime_printcreatedby(gp);
}
}
runtime_unlock(&allglock);
} }
static void static void
@ -1067,22 +1007,6 @@ runtime_allocm(P *p, bool allocatestack, byte** ret_g0_stack, uintptr* ret_g0_st
return mp; return mp;
} }
static G*
allocg(void)
{
G *gp;
// static Type *gtype;
// if(gtype == nil) {
// Eface e;
// runtime_gc_g_ptr(&e);
// gtype = ((PtrType*)e.__type_descriptor)->__element_type;
// }
// gp = runtime_cnew(gtype);
gp = runtime_malloc(sizeof(G));
return gp;
}
void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext"); void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext");
// setGContext sets up a new goroutine context for the current g. // setGContext sets up a new goroutine context for the current g.
@ -2129,6 +2053,7 @@ __go_go(void (*fn)(void*), void* arg)
newg = runtime_malg(true, false, &sp, &malsize); newg = runtime_malg(true, false, &sp, &malsize);
spsize = (size_t)malsize; spsize = (size_t)malsize;
newg->atomicstatus = _Gdead;
allgadd(newg); allgadd(newg);
} }
@ -2152,31 +2077,6 @@ __go_go(void (*fn)(void*), void* arg)
return newg; return newg;
} }
void
allgadd(G *gp)
{
G **new;
uintptr cap;
runtime_lock(&allglock);
if(runtime_allglen >= allgcap) {
cap = 4096/sizeof(new[0]);
if(cap < 2*allgcap)
cap = 2*allgcap;
new = runtime_malloc(cap*sizeof(new[0]));
if(new == nil)
runtime_throw("runtime: cannot allocate memory");
if(runtime_allg != nil) {
runtime_memmove(new, runtime_allg, runtime_allglen*sizeof(new[0]));
runtime_free(runtime_allg);
}
runtime_allg = new;
allgcap = cap;
}
runtime_allg[runtime_allglen++] = gp;
runtime_unlock(&allglock);
}
// Put on gfree list. // Put on gfree list.
// If local list is too long, transfer a batch to the global list. // If local list is too long, transfer a batch to the global list.
static void static void
@ -2351,29 +2251,6 @@ runtime_lockedOSThread(void)
return g->lockedm != nil && g->m->lockedg != nil; return g->lockedm != nil && g->m->lockedg != nil;
} }
int32
runtime_gcount(void)
{
G *gp;
int32 n, s;
uintptr i;
n = 0;
runtime_lock(&allglock);
// TODO(dvyukov): runtime.NumGoroutine() is O(N).
// We do not want to increment/decrement centralized counter in newproc/goexit,
// just to make runtime.NumGoroutine() faster.
// Compromise solution is to introduce per-P counters of active goroutines.
for(i = 0; i < runtime_allglen; i++) {
gp = runtime_allg[i];
s = gp->atomicstatus;
if(s == _Grunnable || s == _Grunning || s == _Gsyscall || s == _Gwaiting)
n++;
}
runtime_unlock(&allglock);
return n;
}
int32 int32
runtime_mcount(void) runtime_mcount(void)
{ {
@ -2638,59 +2515,6 @@ incidlelocked(int32 v)
runtime_unlock(&runtime_sched->lock); runtime_unlock(&runtime_sched->lock);
} }
// Check for deadlock situation.
// The check is based on number of running M's, if 0 -> deadlock.
static void
checkdead(void)
{
G *gp;
int32 run, grunning, s;
uintptr i;
// For -buildmode=c-shared or -buildmode=c-archive it's OK if
// there are no running goroutines. The calling program is
// assumed to be running.
if(runtime_isarchive) {
return;
}
// -1 for sysmon
run = runtime_sched->mcount - runtime_sched->nmidle - runtime_sched->nmidlelocked - 1;
if(run > 0)
return;
// If we are dying because of a signal caught on an already idle thread,
// freezetheworld will cause all running threads to block.
// And runtime will essentially enter into deadlock state,
// except that there is a thread that will call runtime_exit soon.
if(runtime_panicking() > 0)
return;
if(run < 0) {
runtime_printf("runtime: checkdead: nmidle=%d nmidlelocked=%d mcount=%d\n",
runtime_sched->nmidle, runtime_sched->nmidlelocked, runtime_sched->mcount);
runtime_throw("checkdead: inconsistent counts");
}
grunning = 0;
runtime_lock(&allglock);
for(i = 0; i < runtime_allglen; i++) {
gp = runtime_allg[i];
if(gp->isbackground)
continue;
s = gp->atomicstatus;
if(s == _Gwaiting)
grunning++;
else if(s == _Grunnable || s == _Grunning || s == _Gsyscall) {
runtime_unlock(&allglock);
runtime_printf("runtime: checkdead: find g %D in status %d\n", gp->goid, s);
runtime_throw("checkdead: runnable g");
}
}
runtime_unlock(&allglock);
if(grunning == 0) // possible if main goroutine calls runtime_Goexit()
runtime_throw("no goroutines (main called runtime.Goexit) - deadlock!");
g->m->throwing = -1; // do not dump full stacks
runtime_throw("all goroutines are asleep - deadlock!");
}
static void static void
sysmon(void) sysmon(void)
{ {
@ -2832,94 +2656,6 @@ preemptall(void)
return false; return false;
} }
void
runtime_schedtrace(bool detailed)
{
static int64 starttime;
int64 now;
int64 id1, id2, id3;
int32 i, t, h;
uintptr gi;
const char *fmt;
M *mp, *lockedm;
G *gp, *lockedg;
P *p;
now = runtime_nanotime();
if(starttime == 0)
starttime = now;
runtime_lock(&runtime_sched->lock);
runtime_printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d idlethreads=%d runqueue=%d",
(now-starttime)/1000000, runtime_gomaxprocs, runtime_sched->npidle, runtime_sched->mcount,
runtime_sched->nmidle, runtime_sched->runqsize);
if(detailed) {
runtime_printf(" gcwaiting=%d nmidlelocked=%d nmspinning=%d stopwait=%d sysmonwait=%d\n",
runtime_sched->gcwaiting, runtime_sched->nmidlelocked, runtime_sched->nmspinning,
runtime_sched->stopwait, runtime_sched->sysmonwait);
}
// We must be careful while reading data from P's, M's and G's.
// Even if we hold schedlock, most data can be changed concurrently.
// E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil.
for(i = 0; i < runtime_gomaxprocs; i++) {
p = runtime_allp[i];
if(p == nil)
continue;
mp = (M*)p->m;
h = runtime_atomicload(&p->runqhead);
t = runtime_atomicload(&p->runqtail);
if(detailed)
runtime_printf(" P%d: status=%d schedtick=%d syscalltick=%d m=%d runqsize=%d gfreecnt=%d\n",
i, p->status, p->schedtick, p->syscalltick, mp ? mp->id : -1, t-h, p->gfreecnt);
else {
// In non-detailed mode format lengths of per-P run queues as:
// [len1 len2 len3 len4]
fmt = " %d";
if(runtime_gomaxprocs == 1)
fmt = " [%d]\n";
else if(i == 0)
fmt = " [%d";
else if(i == runtime_gomaxprocs-1)
fmt = " %d]\n";
runtime_printf(fmt, t-h);
}
}
if(!detailed) {
runtime_unlock(&runtime_sched->lock);
return;
}
for(mp = runtime_allm; mp; mp = mp->alllink) {
p = (P*)mp->p;
gp = mp->curg;
lockedg = mp->lockedg;
id1 = -1;
if(p)
id1 = p->id;
id2 = -1;
if(gp)
id2 = gp->goid;
id3 = -1;
if(lockedg)
id3 = lockedg->goid;
runtime_printf(" M%d: p=%D curg=%D mallocing=%d throwing=%d gcing=%d"
" locks=%d dying=%d helpgc=%d spinning=%d blocked=%d lockedg=%D\n",
mp->id, id1, id2,
mp->mallocing, mp->throwing, mp->gcing, mp->locks, mp->dying, mp->helpgc,
mp->spinning, mp->blocked, id3);
}
runtime_lock(&allglock);
for(gi = 0; gi < runtime_allglen; gi++) {
gp = runtime_allg[gi];
mp = gp->m;
lockedm = gp->lockedm;
runtime_printf(" G%D: status=%d(%S) m=%d lockedm=%d\n",
gp->goid, gp->atomicstatus, gp->waitreason, mp ? mp->id : -1,
lockedm ? lockedm->id : -1);
}
runtime_unlock(&allglock);
runtime_unlock(&runtime_sched->lock);
}
// Put mp on midle list. // Put mp on midle list.
// Sched must be locked. // Sched must be locked.
static void static void
@ -3357,20 +3093,6 @@ runtime_go_allm()
return &runtime_allm; return &runtime_allm;
} }
extern Slice runtime_go_allgs(void)
__asm__ (GOSYM_PREFIX "runtime.allgs");
Slice
runtime_go_allgs()
{
Slice s;
s.__values = runtime_allg;
s.__count = runtime_allglen;
s.__capacity = allgcap;
return s;
}
intgo NumCPU(void) __asm__ (GOSYM_PREFIX "runtime.NumCPU"); intgo NumCPU(void) __asm__ (GOSYM_PREFIX "runtime.NumCPU");
intgo intgo

View File

@ -7,6 +7,7 @@
#include "go-assert.h" #include "go-assert.h"
#include <complex.h> #include <complex.h>
#include <signal.h> #include <signal.h>
#include <stdint.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -22,8 +23,6 @@
#include <sys/mman.h> #include <sys/mman.h>
#endif #endif
#include "go-alloc.h"
#define _STRINGIFY2_(x) #x #define _STRINGIFY2_(x) #x
#define _STRINGIFY_(x) _STRINGIFY2_(x) #define _STRINGIFY_(x) _STRINGIFY2_(x)
#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__) #define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
@ -233,8 +232,10 @@ enum
*/ */
extern uintptr* runtime_getZerobase(void) extern uintptr* runtime_getZerobase(void)
__asm__(GOSYM_PREFIX "runtime.getZerobase"); __asm__(GOSYM_PREFIX "runtime.getZerobase");
extern G** runtime_allg; extern G* runtime_getallg(intgo)
extern uintptr runtime_allglen; __asm__(GOSYM_PREFIX "runtime.getallg");
extern uintptr runtime_getallglen(void)
__asm__(GOSYM_PREFIX "runtime.getallglen");
extern G* runtime_lastg; extern G* runtime_lastg;
extern M* runtime_allm; extern M* runtime_allm;
extern P** runtime_allp; extern P** runtime_allp;
@ -309,13 +310,9 @@ MCache* runtime_allocmcache(void)
void runtime_freemcache(MCache*); void runtime_freemcache(MCache*);
void runtime_mallocinit(void); void runtime_mallocinit(void);
void runtime_mprofinit(void); void runtime_mprofinit(void);
#define runtime_malloc(s) __go_alloc(s)
#define runtime_free(p) __go_free(p)
#define runtime_getcallersp(p) __builtin_frame_address(0) #define runtime_getcallersp(p) __builtin_frame_address(0)
int32 runtime_mcount(void) int32 runtime_mcount(void)
__asm__ (GOSYM_PREFIX "runtime.mcount"); __asm__ (GOSYM_PREFIX "runtime.mcount");
int32 runtime_gcount(void)
__asm__ (GOSYM_PREFIX "runtime.gcount");
void runtime_mcall(void(*)(G*)); void runtime_mcall(void(*)(G*));
uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1"); uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1");
int32 runtime_timediv(int64, int32, int32*) int32 runtime_timediv(int64, int32, int32*)

View File

@ -25,7 +25,8 @@ extern volatile intgo runtime_MemProfileRate
struct gotraceback_ret { struct gotraceback_ret {
int32 level; int32 level;
bool crash; bool all;
bool crash;
}; };
extern struct gotraceback_ret gotraceback(void) extern struct gotraceback_ret gotraceback(void)