// license that can be found in the LICENSE file.
#include <limits.h>
+#include <signal.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include "arch.h"
#include "defs.h"
#include "malloc.h"
+#include "race.h"
+#include "go-type.h"
#include "go-defer.h"
#ifdef USING_SPLIT_STACK
{
}
-# else
+#else
# if defined(__x86_64__) && defined(__sun__)
c->uc_mcontext.gregs[REG_FSBASE] = fs;
}
+# elif defined(__NetBSD__)
+
+// NetBSD has a bug: setcontext clobbers tlsbase, we need to save
+// and restore it ourselves.
+
+static __thread __greg_t tlsbase;
+
+static inline void
+initcontext(void)
+{
+ ucontext_t c;
+
+ getcontext(&c);
+ tlsbase = c.uc_mcontext._mc_tlsbase;
+}
+
+static inline void
+fixcontext(ucontext_t* c)
+{
+ c->uc_mcontext._mc_tlsbase = tlsbase;
+}
+
# else
# error unknown case for SETCONTEXT_CLOBBERS_TLS
int32 runtime_gcwaiting;
+G* runtime_allg;
+G* runtime_lastg;
+M* runtime_allm;
+
+int8* runtime_goos;
+int32 runtime_ncpu;
+
// The static TLS size. See runtime_newm.
static int tlssize;
Lock;
G *gfree; // available g's (status == Gdead)
- int32 goidgen;
+ int64 goidgen;
G *ghead; // g's waiting to run
G *gtail;
{
void (*fn)(void*);
+ if(g->traceback != nil)
+ gtraceback(g);
+
fn = (void (*)(void*))(g->entry);
fn(g->param);
runtime_goexit();
// Can not enable GC until all roots are registered.
// mstats.enablegc = 1;
m->nomemprof--;
+
+ if(raceenabled)
+ runtime_raceinit();
}
-extern void main_init(void) __asm__ ("__go_init_main");
-extern void main_main(void) __asm__ ("main.main");
+extern void main_init(void) __asm__ (GOSYM_PREFIX "__go_init_main");
+extern void main_main(void) __asm__ (GOSYM_PREFIX "main.main");
// The main goroutine.
void
setmcpumax(runtime_gomaxprocs);
runtime_sched.init = true;
scvg = __go_go(runtime_MHeap_Scavenger, nil);
+ scvg->issystem = true;
main_init();
runtime_sched.init = false;
if(!runtime_sched.lockmain)
runtime_gosched();
main_main();
+ if(raceenabled)
+ runtime_racefini();
runtime_exit(0);
for(;;)
*(int32*)0 = 0;
static void
schedunlock(void)
{
- M *m;
+ M *mp;
- m = mwakeup;
+ mp = mwakeup;
mwakeup = nil;
runtime_unlock(&runtime_sched);
- if(m != nil)
- runtime_notewakeup(&m->havenextg);
+ if(mp != nil)
+ runtime_notewakeup(&mp->havenextg);
}
void
}
void
-runtime_goroutineheader(G *g)
+runtime_goroutineheader(G *gp)
{
const char *status;
- switch(g->status) {
+ switch(gp->status) {
case Gidle:
status = "idle";
break;
status = "syscall";
break;
case Gwaiting:
- if(g->waitreason)
- status = g->waitreason;
+ if(gp->waitreason)
+ status = gp->waitreason;
else
status = "waiting";
break;
status = "???";
break;
}
- runtime_printf("goroutine %d [%s]:\n", g->goid, status);
+ runtime_printf("goroutine %D [%s]:\n", gp->goid, status);
}
void
runtime_goroutinetrailer(G *g)
{
if(g != nil && g->gopc != 0 && g->goid != 1) {
- struct __go_string fn;
- struct __go_string file;
- int line;
+ String fn;
+ String file;
+ intgo line;
if(__go_file_line(g->gopc - 1, &fn, &file, &line)) {
- runtime_printf("created by %s\n", fn.__data);
- runtime_printf("\t%s:%d\n", file.__data, line);
+ runtime_printf("created by %S\n", fn);
+ runtime_printf("\t%S:%D\n", file, (int64) line);
}
}
}
struct Traceback
{
G* gp;
- uintptr pcbuf[100];
+ Location locbuf[100];
int32 c;
};
void
runtime_tracebackothers(G * volatile me)
{
- G * volatile g;
- Traceback traceback;
+ G * volatile gp;
+ Traceback tb;
+ int32 traceback;
- traceback.gp = me;
- for(g = runtime_allg; g != nil; g = g->alllink) {
- if(g == me || g->status == Gdead)
+ tb.gp = me;
+ traceback = runtime_gotraceback();
+ for(gp = runtime_allg; gp != nil; gp = gp->alllink) {
+ if(gp == me || gp->status == Gdead)
+ continue;
+ if(gp->issystem && traceback < 2)
continue;
runtime_printf("\n");
- runtime_goroutineheader(g);
+ runtime_goroutineheader(gp);
// Our only mechanism for doing a stack trace is
// _Unwind_Backtrace. And that only works for the
// This means that if g is running or in a syscall, we
// can't reliably print a stack trace. FIXME.
- if(g->status == Gsyscall || g->status == Grunning) {
+ if(gp->status == Gsyscall || gp->status == Grunning) {
runtime_printf("no stack trace available\n");
- runtime_goroutinetrailer(g);
+ runtime_goroutinetrailer(gp);
continue;
}
- g->traceback = &traceback;
+ gp->traceback = &tb;
#ifdef USING_SPLIT_STACK
__splitstack_getcontext(&me->stack_context[0]);
#endif
getcontext(&me->context);
- if(g->traceback != nil) {
- runtime_gogo(g);
+ if(gp->traceback != nil) {
+ runtime_gogo(gp);
}
- runtime_printtrace(traceback.pcbuf, traceback.c);
- runtime_goroutinetrailer(g);
+ runtime_printtrace(tb.locbuf, tb.c, false);
+ runtime_goroutinetrailer(gp);
}
}
traceback = gp->traceback;
gp->traceback = nil;
- traceback->c = runtime_callers(1, traceback->pcbuf,
- sizeof traceback->pcbuf / sizeof traceback->pcbuf[0]);
+ traceback->c = runtime_callers(1, traceback->locbuf,
+ sizeof traceback->locbuf / sizeof traceback->locbuf[0]);
runtime_gogo(traceback->gp);
}
}
static void
-mcommoninit(M *m)
+mcommoninit(M *mp)
{
- m->id = runtime_sched.mcount++;
- m->fastrand = 0x49f6428aUL + m->id + runtime_cputicks();
+ mp->id = runtime_sched.mcount++;
+ mp->fastrand = 0x49f6428aUL + mp->id + runtime_cputicks();
- if(m->mcache == nil)
- m->mcache = runtime_allocmcache();
+ if(mp->mcache == nil)
+ mp->mcache = runtime_allocmcache();
- runtime_callers(1, m->createstack, nelem(m->createstack));
+ runtime_callers(1, mp->createstack, nelem(mp->createstack));
// Add to runtime_allm so garbage collector doesn't free m
// when it is just in a register or thread-local storage.
- m->alllink = runtime_allm;
+ mp->alllink = runtime_allm;
// runtime_NumCgoCall() iterates over allm w/o schedlock,
// so we need to publish it safely.
- runtime_atomicstorep(&runtime_allm, m);
+ runtime_atomicstorep(&runtime_allm, mp);
}
// Try to increment mcpu. Report whether succeeded.
// Put on `g' queue. Sched must be locked.
static void
-gput(G *g)
+gput(G *gp)
{
- M *m;
+ M *mp;
// If g is wired, hand it off directly.
- if((m = g->lockedm) != nil && canaddmcpu()) {
- mnextg(m, g);
+ if((mp = gp->lockedm) != nil && canaddmcpu()) {
+ mnextg(mp, gp);
return;
}
// If g is the idle goroutine for an m, hand it off.
- if(g->idlem != nil) {
- if(g->idlem->idleg != nil) {
- runtime_printf("m%d idle out of sync: g%d g%d\n",
- g->idlem->id,
- g->idlem->idleg->goid, g->goid);
+ if(gp->idlem != nil) {
+ if(gp->idlem->idleg != nil) {
+ runtime_printf("m%d idle out of sync: g%D g%D\n",
+ gp->idlem->id,
+ gp->idlem->idleg->goid, gp->goid);
runtime_throw("runtime: double idle");
}
- g->idlem->idleg = g;
+ gp->idlem->idleg = gp;
return;
}
- g->schedlink = nil;
+ gp->schedlink = nil;
if(runtime_sched.ghead == nil)
- runtime_sched.ghead = g;
+ runtime_sched.ghead = gp;
else
- runtime_sched.gtail->schedlink = g;
- runtime_sched.gtail = g;
+ runtime_sched.gtail->schedlink = gp;
+ runtime_sched.gtail = gp;
// increment gwait.
// if it transitions to nonzero, set atomic gwaiting bit.
static G*
gget(void)
{
- G *g;
+ G *gp;
- g = runtime_sched.ghead;
- if(g){
- runtime_sched.ghead = g->schedlink;
+ gp = runtime_sched.ghead;
+ if(gp) {
+ runtime_sched.ghead = gp->schedlink;
if(runtime_sched.ghead == nil)
runtime_sched.gtail = nil;
// decrement gwait.
if(--runtime_sched.gwait == 0)
runtime_xadd(&runtime_sched.atomic, -1<<gwaitingShift);
} else if(m->idleg != nil) {
- g = m->idleg;
+ gp = m->idleg;
m->idleg = nil;
}
- return g;
+ return gp;
}
// Put on `m' list. Sched must be locked.
static void
-mput(M *m)
+mput(M *mp)
{
- m->schedlink = runtime_sched.mhead;
- runtime_sched.mhead = m;
+ mp->schedlink = runtime_sched.mhead;
+ runtime_sched.mhead = mp;
runtime_sched.mwait++;
}
// Get an `m' to run `g'. Sched must be locked.
static M*
-mget(G *g)
+mget(G *gp)
{
- M *m;
+ M *mp;
// if g has its own m, use it.
- if(g && (m = g->lockedm) != nil)
- return m;
+ if(gp && (mp = gp->lockedm) != nil)
+ return mp;
// otherwise use general m pool.
- if((m = runtime_sched.mhead) != nil){
- runtime_sched.mhead = m->schedlink;
+ if((mp = runtime_sched.mhead) != nil) {
+ runtime_sched.mhead = mp->schedlink;
runtime_sched.mwait--;
}
- return m;
+ return mp;
}
// Mark g ready to run.
void
-runtime_ready(G *g)
+runtime_ready(G *gp)
{
schedlock();
- readylocked(g);
+ readylocked(gp);
schedunlock();
}
// G might be running already and about to stop.
// The sched lock protects g->status from changing underfoot.
static void
-readylocked(G *g)
+readylocked(G *gp)
{
- if(g->m){
+ if(gp->m) {
// Running on another machine.
// Ready it when it stops.
- g->readyonstop = 1;
+ gp->readyonstop = 1;
return;
}
// Mark runnable.
- if(g->status == Grunnable || g->status == Grunning) {
- runtime_printf("goroutine %d has status %d\n", g->goid, g->status);
+ if(gp->status == Grunnable || gp->status == Grunning) {
+ runtime_printf("goroutine %D has status %d\n", gp->goid, gp->status);
runtime_throw("bad g->status in ready");
}
- g->status = Grunnable;
+ gp->status = Grunnable;
- gput(g);
+ gput(gp);
matchmg();
}
// debuggers can set a breakpoint here and catch all
// new goroutines.
static void
-newprocreadylocked(G *g)
+newprocreadylocked(G *gp)
{
- readylocked(g);
+ readylocked(gp);
}
// Pass g to m for running.
// Caller has already incremented mcpu.
static void
-mnextg(M *m, G *g)
+mnextg(M *mp, G *gp)
{
runtime_sched.grunning++;
- m->nextg = g;
- if(m->waitnextg) {
- m->waitnextg = 0;
+ mp->nextg = gp;
+ if(mp->waitnextg) {
+ mp->waitnextg = 0;
if(mwakeup != nil)
runtime_notewakeup(&mwakeup->havenextg);
- mwakeup = m;
+ mwakeup = mp;
}
}
if((scvg == nil && runtime_sched.grunning == 0) ||
(scvg != nil && runtime_sched.grunning == 1 && runtime_sched.gwait == 0 &&
(scvg->status == Grunning || scvg->status == Gsyscall))) {
+ m->throwing = -1; // do not dump full stacks
runtime_throw("all goroutines are asleep - deadlock!");
}
}
int32
-runtime_helpgc(bool *extra)
+runtime_gcprocs(void)
{
- M *mp;
- int32 n, max;
-
- // Figure out how many CPUs to use.
+ int32 n;
+
+ // Figure out how many CPUs to use during GC.
// Limited by gomaxprocs, number of actual CPUs, and MaxGcproc.
- max = runtime_gomaxprocs;
- if(max > runtime_ncpu)
- max = runtime_ncpu > 0 ? runtime_ncpu : 1;
- if(max > MaxGcproc)
- max = MaxGcproc;
+ n = runtime_gomaxprocs;
+ if(n > runtime_ncpu)
+ n = runtime_ncpu > 0 ? runtime_ncpu : 1;
+ if(n > MaxGcproc)
+ n = MaxGcproc;
+ if(n > runtime_sched.mwait+1) // one M is currently running
+ n = runtime_sched.mwait+1;
+ return n;
+}
- // We're going to use one CPU no matter what.
- // Figure out the max number of additional CPUs.
- max--;
+void
+runtime_helpgc(int32 nproc)
+{
+ M *mp;
+ int32 n;
runtime_lock(&runtime_sched);
- n = 0;
- while(n < max && (mp = mget(nil)) != nil) {
- n++;
+ for(n = 1; n < nproc; n++) { // one M is currently running
+ mp = mget(nil);
+ if(mp == nil)
+ runtime_throw("runtime_gcprocs inconsistency");
mp->helpgc = 1;
mp->waitnextg = 0;
runtime_notewakeup(&mp->havenextg);
}
runtime_unlock(&runtime_sched);
- if(extra)
- *extra = n != max;
- return n;
}
void
}
void
-runtime_starttheworld(bool extra)
+runtime_starttheworld(void)
{
- M *m;
+ M *mp;
+ int32 max;
+
+ // Figure out how many CPUs GC could possibly use.
+ max = runtime_gomaxprocs;
+ if(max > runtime_ncpu)
+ max = runtime_ncpu > 0 ? runtime_ncpu : 1;
+ if(max > MaxGcproc)
+ max = MaxGcproc;
schedlock();
runtime_gcwaiting = 0;
setmcpumax(runtime_gomaxprocs);
matchmg();
- if(extra && canaddmcpu()) {
- // Start a new m that will (we hope) be idle
- // and so available to help when the next
- // garbage collection happens.
+ if(runtime_gcprocs() < max && canaddmcpu()) {
+ // If GC could have used another helper proc, start one now,
+ // in the hope that it will be available next time.
+ // It would have been even better to start it before the collection,
+ // but doing so requires allocating memory, so it's tricky to
+ // coordinate. This lazy approach works out in practice:
+ // we don't mind if the first couple gc rounds don't have quite
+ // the maximum number of procs.
// canaddmcpu above did mcpu++
// (necessary, because m will be doing various
// initialization work so is definitely running),
// but m is not running a specific goroutine,
// so set the helpgc flag as a signal to m's
// first schedule(nil) to mcpu-- and grunning--.
- m = runtime_newm();
- m->helpgc = 1;
+ mp = runtime_newm();
+ mp->helpgc = 1;
runtime_sched.grunning++;
}
schedunlock();
runtime_initsig();
schedule(nil);
+
+ // TODO(brainman): This point is never reached, because scheduler
+ // does not release os threads at the moment. But once this path
+ // is enabled, we must remove our seh here.
+
return nil;
}
M*
runtime_newm(void)
{
- M *m;
+ M *mp;
pthread_attr_t attr;
pthread_t tid;
size_t stacksize;
+ sigset_t clear;
+ sigset_t old;
+ int ret;
+
+#if 0
+ static const Type *mtype; // The Go type M
+ if(mtype == nil) {
+ Eface e;
+ runtime_gc_m_ptr(&e);
+ mtype = ((const PtrType*)e.__type_descriptor)->__element_type;
+ }
+#endif
- m = runtime_malloc(sizeof(M));
- mcommoninit(m);
- m->g0 = runtime_malg(-1, nil, nil);
+ mp = runtime_mal(sizeof *mp);
+ mcommoninit(mp);
+ mp->g0 = runtime_malg(-1, nil, nil);
if(pthread_attr_init(&attr) != 0)
runtime_throw("pthread_attr_init");
if(pthread_attr_setstacksize(&attr, stacksize) != 0)
runtime_throw("pthread_attr_setstacksize");
- if(pthread_create(&tid, &attr, runtime_mstart, m) != 0)
+ // Block signals during pthread_create so that the new thread
+ // starts with signals disabled. It will enable them in minit.
+ sigfillset(&clear);
+
+#ifdef SIGTRAP
+ // Blocking SIGTRAP reportedly breaks gdb on Alpha GNU/Linux.
+ sigdelset(&clear, SIGTRAP);
+#endif
+
+ sigemptyset(&old);
+ sigprocmask(SIG_BLOCK, &clear, &old);
+ ret = pthread_create(&tid, &attr, runtime_mstart, mp);
+ sigprocmask(SIG_SETMASK, &old, nil);
+
+ if (ret != 0)
runtime_throw("pthread_create");
- return m;
+ return mp;
}
// One round of scheduler: find a goroutine and run it.
if(atomic_mcpu(v) > maxgomaxprocs)
runtime_throw("negative mcpu in scheduler");
- switch(gp->status){
+ switch(gp->status) {
case Grunnable:
case Gdead:
// Shouldn't have been running!
gput(gp);
break;
case Gmoribund:
+ if(raceenabled)
+ runtime_racegoend(gp->goid);
gp->status = Gdead;
if(gp->lockedm) {
gp->lockedm = nil;
runtime_exit(0);
break;
}
- if(gp->readyonstop){
+ if(gp->readyonstop) {
gp->readyonstop = 0;
readylocked(gp);
}
runtime_mcall(schedule);
}
+// Puts the current goroutine into a waiting state and unlocks the lock.
+// The goroutine can be made runnable again by calling runtime_ready(gp).
+void
+runtime_park(void (*unlockf)(Lock*), Lock *lock, const char *reason)
+{
+ g->status = Gwaiting;
+ g->waitreason = reason;
+ if(unlockf)
+ unlockf(lock);
+ runtime_gosched();
+}
+
// The goroutine g is about to enter a system call.
// Record that it's not using the cpu anymore.
// This is called only from the go syscall library and cgocall,
/* For runtime package testing. */
void runtime_testing_entersyscall(void)
- __asm__("runtime.entersyscall");
+ __asm__ (GOSYM_PREFIX "runtime.entersyscall");
void
runtime_testing_entersyscall()
}
void runtime_testing_exitsyscall(void)
- __asm__("runtime.exitsyscall");
+ __asm__ (GOSYM_PREFIX "runtime.exitsyscall");
void
runtime_testing_exitsyscall()
byte *sp;
size_t spsize;
G *newg;
+ int64 goid;
+
+ goid = runtime_xadd64((uint64*)&runtime_sched.goidgen, 1);
+ if(raceenabled)
+ runtime_racegostart(goid, runtime_getcallerpc(&fn));
schedlock();
- if((newg = gfget()) != nil){
+ if((newg = gfget()) != nil) {
#ifdef USING_SPLIT_STACK
int dont_block_signals = 0;
newg->gopc = (uintptr)__builtin_return_address(0);
runtime_sched.gcount++;
- runtime_sched.goidgen++;
- newg->goid = runtime_sched.goidgen;
+ newg->goid = goid;
if(sp == nil)
runtime_throw("nil g->stack0");
// Put on gfree list. Sched must be locked.
static void
-gfput(G *g)
+gfput(G *gp)
{
- g->schedlink = runtime_sched.gfree;
- runtime_sched.gfree = g;
+ gp->schedlink = runtime_sched.gfree;
+ runtime_sched.gfree = gp;
}
// Get from gfree list. Sched must be locked.
static G*
gfget(void)
{
- G *g;
-
- g = runtime_sched.gfree;
- if(g)
- runtime_sched.gfree = g->schedlink;
- return g;
-}
-
-// Run all deferred functions for the current goroutine.
-static void
-rundefer(void)
-{
- Defer *d;
-
- while((d = g->defer) != nil) {
- void (*pfn)(void*);
-
- pfn = d->__pfn;
- d->__pfn = nil;
- if (pfn != nil)
- (*pfn)(d->__arg);
- g->defer = d->__next;
- runtime_free(d);
- }
-}
-
-void runtime_Goexit (void) asm ("runtime.Goexit");
+ G *gp;
-void
-runtime_Goexit(void)
-{
- rundefer();
- runtime_goexit();
+ gp = runtime_sched.gfree;
+ if(gp)
+ runtime_sched.gfree = gp->schedlink;
+ return gp;
}
-void runtime_Gosched (void) asm ("runtime.Gosched");
+void runtime_Gosched (void) __asm__ (GOSYM_PREFIX "runtime.Gosched");
void
runtime_Gosched(void)
// for testing of callbacks
_Bool runtime_golockedOSThread(void)
- asm("runtime.golockedOSThread");
+ __asm__ (GOSYM_PREFIX "runtime.golockedOSThread");
_Bool
runtime_golockedOSThread(void)
return m->id;
}
-int32 runtime_NumGoroutine (void)
- __asm__ ("runtime.NumGoroutine");
+intgo runtime_NumGoroutine (void)
+ __asm__ (GOSYM_PREFIX "runtime.NumGoroutine");
-int32
+intgo
runtime_NumGoroutine()
{
return runtime_sched.gcount;
void (*fn)(uintptr*, int32);
int32 hz;
uintptr pcbuf[100];
+ Location locbuf[100];
} prof;
// Called if we receive a SIGPROF signal.
void
runtime_sigprof()
{
- int32 n;
+ int32 n, i;
if(prof.fn == nil || prof.hz == 0)
return;
runtime_unlock(&prof);
return;
}
- n = runtime_callers(0, prof.pcbuf, nelem(prof.pcbuf));
+ n = runtime_callers(0, prof.locbuf, nelem(prof.locbuf));
+ for(i = 0; i < n; i++)
+ prof.pcbuf[i] = prof.locbuf[i].pc;
if(n > 0)
prof.fn(prof.pcbuf, n);
runtime_unlock(&prof);