runtime: remove __go_alloc and __go_free
authorIan Lance Taylor <ian@gcc.gnu.org>
Tue, 3 Jan 2017 22:58:48 +0000 (22:58 +0000)
committerIan Lance Taylor <ian@gcc.gnu.org>
Tue, 3 Jan 2017 22:58:48 +0000 (22:58 +0000)
    Move allocg and handling of allgs slice from C to Go.

    Reviewed-on: https://go-review.googlesource.com/34797

From-SVN: r244036

18 files changed:
gcc/go/gofrontend/MERGE
libgo/go/runtime/mprof.go
libgo/go/runtime/proc.go
libgo/go/runtime/runtime2.go
libgo/go/runtime/stubs.go
libgo/go/runtime/traceback_gccgo.go
libgo/runtime/go-libmain.c
libgo/runtime/go-main.c
libgo/runtime/go-new.c
libgo/runtime/go-reflect-call.c
libgo/runtime/go-unwind.c
libgo/runtime/heapdump.c
libgo/runtime/malloc.goc
libgo/runtime/mgc0.c
libgo/runtime/parfor.c
libgo/runtime/proc.c
libgo/runtime/runtime.h
libgo/runtime/runtime_c.c

index a48719637f45158f7ca6478bc9a2ae16772e51af..1efd7ee659c51e48942ffe8fa20f2f1e75555493 100644 (file)
@@ -1,4 +1,4 @@
-eac28020ee4b2532d4cd43f448fe612e84e0a108
+dfe446c5a54ca0febabb81b542cc4e634c6f5c30
 
 The first line of this file holds the git revision number of the last
 merge done from the gofrontend repository.
index a2701e32f76912fbe1d40b0df084220667bc69e1..cc0a673c8b412f17d71338d94fcbbfa1f8ce383c 100644 (file)
@@ -556,7 +556,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
        stopTheWorld("profile")
 
        n = 1
-       for _, gp1 := range allgs() {
+       for _, gp1 := range allgs {
                if isOK(gp1) {
                        n++
                }
@@ -571,7 +571,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
                r = r[1:]
 
                // Save other goroutines.
-               for _, gp1 := range allgs() {
+               for _, gp1 := range allgs {
                        if isOK(gp1) {
                                if len(r) == 0 {
                                        // Should be impossible, but better to return a
index fa90a282866f2013bbe0113e56cbeab8a9a23766..78cc6ee7d8a1113e571b82eaa126144b79fd930a 100644 (file)
@@ -11,15 +11,18 @@ import (
 
 // Functions temporarily called by C code.
 //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.
 func allocm(*p, bool, *unsafe.Pointer, *uintptr) *m
 func malg(bool, bool, *unsafe.Pointer, *uintptr) *g
-func allgadd(*g)
 
 // C functions for ucontext management.
 func setGContext()
 func makeGContext(*g, unsafe.Pointer, uintptr)
+func getTraceback(me, gp *g)
 
 // main_init_done is a signal used by cgocallbackg that initialization
 // 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.
 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
 // and casfrom_Gscanstatus instead.
 // 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) {
        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)
+}
index 6686e1f29b3df53265965e9d28714034181b6f1e..571972c1a83400b7a3c517346c2a63b3839d0dab 100644 (file)
@@ -755,9 +755,13 @@ const _TracebackMaxFrames = 100
 
 var (
        //      emptystring string
-       //      allglen     uintptr
+
+       allglen uintptr
+
        //      allm        *m
-       //      allp        [_MaxGomaxprocs + 1]*p
+
+       allp [_MaxGomaxprocs + 1]*p
+
        //      gomaxprocs  int32
 
        panicking uint32
index c299ae0e8eb858dd6b542aecc6ee0f63e466d994..3d184083d55009442b13bb319a4cee917a35aa30 100644 (file)
@@ -476,12 +476,6 @@ func UnlockOSThread()
 func lockOSThread()
 func unlockOSThread()
 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
 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
 func setprofilebucket(p unsafe.Pointer, b *bucket)
 
-// Currently in proc.c.
-func tracebackothers(*g)
-
 // Temporary for gccgo until we port mgc.go.
 func setgcpercent(int32) int32
 
@@ -530,9 +521,7 @@ func getZerobase() *uintptr {
 // Temporary for gccgo until we port proc.go.
 func sigprof()
 func mcount() int32
-func gcount() int32
 func goexit1()
-func schedtrace(bool)
 func freezetheworld()
 
 // Get signal trampoline, written in C.
@@ -562,6 +551,30 @@ func getCgoHasExtraM() *bool {
        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.
 func throwException()
 func rethrowException()
@@ -579,3 +592,27 @@ func getPanicking() uint32 {
 
 // Temporary for gccgo until we port mcache.go.
 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
+}
index f61f9a0f2ddd895437a126421b06a6058d8a3590..611aba91a4d5717c399ce49738a2fbc163aa964a 100644 (file)
@@ -171,3 +171,58 @@ func isSystemGoroutine(gp *g) bool {
        // FIXME.
        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)
+}
index a404eb9b68173f02123966e5b57f083962beb53f..8e07e90178c0a26b82ff976a3f8deeaf4eda13a7 100644 (file)
@@ -13,7 +13,6 @@
 #include <unistd.h>
 
 #include "runtime.h"
-#include "go-alloc.h"
 #include "array.h"
 #include "arch.h"
 #include "malloc.h"
index b8c9af1e79b987de74971efcd85c6a9c30bd41a3..dba808526038b22b331992c0910b017842e761b8 100644 (file)
@@ -15,7 +15,6 @@
 #endif
 
 #include "runtime.h"
-#include "go-alloc.h"
 #include "array.h"
 #include "arch.h"
 #include "malloc.h"
index 01bc2af31211056dd1d0386a2dc293648e68aa62..da44074a5d5428f4ccf13602114e91992198f437 100644 (file)
@@ -4,7 +4,6 @@
    Use of this source code is governed by a BSD-style
    license that can be found in the LICENSE file.  */
 
-#include "go-alloc.h"
 #include "runtime.h"
 #include "arch.h"
 #include "malloc.h"
index dc2f60575ecee148ffac0d5afa9b49199fcee3b8..6a9a7f35a1a66891e055c81efed762f535dea270 100644 (file)
@@ -9,7 +9,6 @@
 #include <stdlib.h>
 
 #include "runtime.h"
-#include "go-alloc.h"
 #include "go-assert.h"
 #include "go-type.h"
 
index 9e85b4b81475ec012ae3a6cc779d6b4c6399522d..4c9fb49c9942e5ff3c0aa394585a25e4ca59c024 100644 (file)
@@ -14,7 +14,6 @@
 #include "unwind-pe.h"
 
 #include "runtime.h"
-#include "go-alloc.h"
 
 /* The code for a Go exception.  */
 
index 80d2b7bf85006c2a78da57785e5be89a6fc018b9..c050541db9d8ec91a0d3599ba72fe05a33e15950 100644 (file)
@@ -311,8 +311,8 @@ dumpgs(void)
        uint32 i;
 
        // goroutines & stacks
-       for(i = 0; i < runtime_allglen; i++) {
-               gp = runtime_allg[i];
+       for(i = 0; i < runtime_getallglen(); i++) {
+               gp = runtime_getallg(i);
                switch(gp->atomicstatus){
                default:
                        runtime_printf("unexpected G.status %d\n", gp->atomicstatus);
index 5cbdc4632fbdc801bcf88f6f8975a9493e22d911..1e6704c6290a124052bc6c0e25017d0e773d9815 100644 (file)
@@ -10,7 +10,6 @@ package runtime
 #include <stddef.h>
 #include <errno.h>
 #include <stdlib.h>
-#include "go-alloc.h"
 #include "runtime.h"
 #include "arch.h"
 #include "malloc.h"
@@ -308,107 +307,6 @@ runtime_profilealloc(void *v, uintptr 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
 runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
 {
@@ -628,9 +526,6 @@ runtime_mallocinit(void)
        // Initialize the rest of the allocator.        
        runtime_MHeap_Init(&runtime_mheap);
        runtime_m()->mcache = runtime_allocmcache();
-
-       // See if it works.
-       runtime_free(runtime_malloc(TinySize));
 }
 
 void*
index aa8404e216741eeb4247d5ac58bb23f810501cc1..5d6275a63570e7aafd66b4bb22900f1a440b7aea 100644 (file)
@@ -1279,7 +1279,6 @@ markroot(ParFor *desc, uint32 i)
                // 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_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_allp, sizeof runtime_allp, 0});
                enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0});
@@ -1334,9 +1333,9 @@ markroot(ParFor *desc, uint32 i)
 
        default:
                // the rest is scanning goroutine stacks
-               if(i - RootCount >= runtime_allglen)
+               if(i - RootCount >= runtime_getallglen())
                        runtime_throw("markroot: bad index");
-               gp = runtime_allg[i - RootCount];
+               gp = runtime_getallg(i - RootCount);
                // remember when we've first observed the G blocked
                // needed only to output in traceback
                if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince == 0)
@@ -2243,7 +2242,7 @@ gc(struct gc_args *args)
        work.nwait = 0;
        work.ndone = 0;
        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) {
                runtime_noteclear(&work.alldone);
                runtime_helpgc(work.nproc);
index ede921b9aaa4046b19b3ce14729257cf8c285076..c2f10b9a987f025ccf7d3d6a05d8e2256842ff47 100644 (file)
@@ -5,6 +5,7 @@
 // Parallel for algorithm.
 
 #include "runtime.h"
+#include "malloc.h"
 #include "arch.h"
 
 struct ParForThread
@@ -27,7 +28,7 @@ runtime_parforalloc(uint32 nthrmax)
 
        // The ParFor object is followed by CacheLineSize padding
        // 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->nthrmax = nthrmax;
        return desc;
index 586a6323e6dc769862673400c74a4ddae8aac191..8a7a2d76ae6b283e5afd7f4bdc43fcacdb2f01f0 100644 (file)
@@ -361,6 +361,10 @@ enum
 extern Sched* runtime_getsched() __asm__ (GOSYM_PREFIX "runtime.getsched");
 extern bool* 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;
 int32  runtime_gomaxprocs;
@@ -374,11 +378,6 @@ int32      runtime_ncpu;
 bool   runtime_precisestack;
 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;
 
 void* runtime_mstart(void*);
@@ -403,7 +402,6 @@ static void startlockedm(G*);
 static void sysmon(void);
 static uint32 retake(int64);
 static void incidlelocked(int32);
-static void checkdead(void);
 static void exitsyscall0(G*);
 static void park0(G*);
 static void goexit0(G*);
@@ -421,6 +419,8 @@ static bool exitsyscallfast(void);
 
 void allgadd(G*)
   __asm__(GOSYM_PREFIX "runtime.allgadd");
+void checkdead(void)
+  __asm__(GOSYM_PREFIX "runtime.checkdead");
 
 bool runtime_isstarted;
 
@@ -482,7 +482,7 @@ runtime_schedinit(void)
                        n = _MaxGomaxprocs;
                procs = n;
        }
-       runtime_allp = runtime_malloc((_MaxGomaxprocs+1)*sizeof(runtime_allp[0]));
+       runtime_allp = runtime_getAllP();
        procresize(procs);
 
        // Can not enable GC until all roots are registered.
@@ -586,85 +586,25 @@ runtime_main(void* dummy __attribute__((unused)))
                *(int32*)0 = 0;
 }
 
-void
-runtime_tracebackothers(G * volatile me)
-{
-       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
-               __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_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;
+void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");
 
+// 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)
+{
 #ifdef USING_SPLIT_STACK
-                       __splitstack_getcontext(&me->stackcontext[0]);
+       __splitstack_getcontext(&me->stackcontext[0]);
 #endif
-                       getcontext(ucontext_arg(&me->context[0]));
+       getcontext(ucontext_arg(&me->stackcontext[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);
-               }
+       if (gp->traceback != nil) {
+               runtime_gogo(gp);
        }
-       runtime_unlock(&allglock);
 }
 
 static void
@@ -1067,22 +1007,6 @@ runtime_allocm(P *p, bool allocatestack, byte** ret_g0_stack, uintptr* ret_g0_st
        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");
 
 // 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);
                spsize = (size_t)malsize;
+               newg->atomicstatus = _Gdead;
                allgadd(newg);
        }
 
@@ -2152,31 +2077,6 @@ __go_go(void (*fn)(void*), void* arg)
        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.
 // If local list is too long, transfer a batch to the global list.
 static void
@@ -2351,29 +2251,6 @@ runtime_lockedOSThread(void)
        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
 runtime_mcount(void)
 {
@@ -2638,59 +2515,6 @@ incidlelocked(int32 v)
        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
 sysmon(void)
 {
@@ -2832,94 +2656,6 @@ preemptall(void)
        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.
 // Sched must be locked.
 static void
@@ -3357,20 +3093,6 @@ runtime_go_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
index 54bdcf8ce72581ee6c967b1d2b776c8b26febfab..c8f490f20de7d25b342bea6c567b8fd119aa4ace 100644 (file)
@@ -7,6 +7,7 @@
 #include "go-assert.h"
 #include <complex.h>
 #include <signal.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -22,8 +23,6 @@
 #include <sys/mman.h>
 #endif
 
-#include "go-alloc.h"
-
 #define _STRINGIFY2_(x) #x
 #define _STRINGIFY_(x) _STRINGIFY2_(x)
 #define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
@@ -233,8 +232,10 @@ enum
  */
 extern uintptr* runtime_getZerobase(void)
   __asm__(GOSYM_PREFIX "runtime.getZerobase");
-extern G**     runtime_allg;
-extern uintptr runtime_allglen;
+extern G* runtime_getallg(intgo)
+  __asm__(GOSYM_PREFIX "runtime.getallg");
+extern uintptr runtime_getallglen(void)
+  __asm__(GOSYM_PREFIX "runtime.getallglen");
 extern G*      runtime_lastg;
 extern M*      runtime_allm;
 extern P**     runtime_allp;
@@ -309,13 +310,9 @@ MCache*    runtime_allocmcache(void)
 void   runtime_freemcache(MCache*);
 void   runtime_mallocinit(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)
 int32  runtime_mcount(void)
   __asm__ (GOSYM_PREFIX "runtime.mcount");
-int32  runtime_gcount(void)
-  __asm__ (GOSYM_PREFIX "runtime.gcount");
 void   runtime_mcall(void(*)(G*));
 uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1");
 int32  runtime_timediv(int64, int32, int32*)
index 464531263f50b47c2a94802e00036d0e60841be2..67d5ee2a424b8650fb496ce8e818e950cad06315 100644 (file)
@@ -25,7 +25,8 @@ extern volatile intgo runtime_MemProfileRate
 
 struct gotraceback_ret {
        int32 level;
-       bool crash;
+       bool  all;
+       bool  crash;
 };
 
 extern struct gotraceback_ret gotraceback(void)