Commit 0f2a6e84 by Ian Lance Taylor

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
parent c65f76af
eac28020ee4b2532d4cd43f448fe612e84e0a108
dfe446c5a54ca0febabb81b542cc4e634c6f5c30
The first line of this file holds the git revision number of the last
merge done from the gofrontend repository.
......@@ -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
......
......@@ -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)
}
......@@ -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
......
......@@ -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
}
......@@ -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)
}
......@@ -13,7 +13,6 @@
#include <unistd.h>
#include "runtime.h"
#include "go-alloc.h"
#include "array.h"
#include "arch.h"
#include "malloc.h"
......
......@@ -15,7 +15,6 @@
#endif
#include "runtime.h"
#include "go-alloc.h"
#include "array.h"
#include "arch.h"
#include "malloc.h"
......
......@@ -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"
......
......@@ -9,7 +9,6 @@
#include <stdlib.h>
#include "runtime.h"
#include "go-alloc.h"
#include "go-assert.h"
#include "go-type.h"
......
......@@ -14,7 +14,6 @@
#include "unwind-pe.h"
#include "runtime.h"
#include "go-alloc.h"
/* The code for a Go exception. */
......
......@@ -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);
......
......@@ -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*
......
......@@ -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);
......
......@@ -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;
......
......@@ -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*)
......
......@@ -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)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment