aboutsummaryrefslogtreecommitdiff
path: root/libgo/runtime/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/runtime/proc.c')
-rw-r--r--libgo/runtime/proc.c1058
1 files changed, 118 insertions, 940 deletions
diff --git a/libgo/runtime/proc.c b/libgo/runtime/proc.c
index 06a9c2a..cc56642 100644
--- a/libgo/runtime/proc.c
+++ b/libgo/runtime/proc.c
@@ -18,7 +18,6 @@
#include "runtime.h"
#include "arch.h"
#include "defs.h"
-#include "malloc.h"
#include "go-type.h"
#ifdef USING_SPLIT_STACK
@@ -55,7 +54,8 @@ extern void __splitstack_block_signals_context (void *context[10], int *,
uintptr runtime_stacks_sys;
-static void gtraceback(G*);
+void gtraceback(G*)
+ __asm__(GOSYM_PREFIX "runtime.gtraceback");
#ifdef __rtems__
#define __thread
@@ -149,6 +149,21 @@ fixcontext(ucontext_t *c)
asm ("st %%g7, %0" : "=m"(c->uc_mcontext.gregs[REG_G7]));
}
+# elif defined(_AIX)
+
+static inline void
+initcontext(void)
+{
+}
+
+static inline void
+fixcontext(ucontext_t* c)
+{
+ // Thread pointer is in r13, per 64-bit ABI.
+ if (sizeof (c->uc_mcontext.jmp_context.gpr[13]) == 8)
+ asm ("std 13, %0" : "=m"(c->uc_mcontext.jmp_context.gpr[13]));
+}
+
# else
# error unknown case for SETCONTEXT_CLOBBERS_TLS
@@ -164,7 +179,7 @@ fixcontext(ucontext_t *c)
// So we make the field larger in runtime2.go and pick an appropriate
// offset within the field here.
static ucontext_t*
-ucontext_arg(void** go_ucontext)
+ucontext_arg(uintptr* go_ucontext)
{
uintptr_t p = (uintptr_t)go_ucontext;
size_t align = __alignof__(ucontext_t);
@@ -210,8 +225,11 @@ runtime_setg(G* gp)
g = gp;
}
+void runtime_newosproc(M *)
+ __asm__(GOSYM_PREFIX "runtime.newosproc");
+
// Start a new thread.
-static void
+void
runtime_newosproc(M *mp)
{
pthread_attr_t attr;
@@ -253,31 +271,13 @@ runtime_newosproc(M *mp)
}
}
-// First function run by a new goroutine. This replaces gogocall.
-static void
-kickoff(void)
-{
- void (*fn)(void*);
- void *param;
-
- if(g->traceback != nil)
- gtraceback(g);
-
- fn = (void (*)(void*))(g->entry);
- param = g->param;
- g->entry = nil;
- g->param = nil;
- fn(param);
- runtime_goexit1();
-}
-
// Switch context to a different goroutine. This is like longjmp.
void runtime_gogo(G*) __attribute__ ((noinline));
void
runtime_gogo(G* newg)
{
#ifdef USING_SPLIT_STACK
- __splitstack_setcontext(&newg->stackcontext[0]);
+ __splitstack_setcontext((void*)(&newg->stackcontext[0]));
#endif
g = newg;
newg->fromgogo = true;
@@ -290,9 +290,9 @@ runtime_gogo(G* newg)
// setjmp. Because getcontext always returns 0, unlike setjmp, we use
// g->fromgogo as a code. It will be true if we got here via
// setcontext. g == nil the first time this is called in a new m.
-void runtime_mcall(void (*)(G*)) __attribute__ ((noinline));
+void runtime_mcall(FuncVal *) __attribute__ ((noinline));
void
-runtime_mcall(void (*pfn)(G*))
+runtime_mcall(FuncVal *fv)
{
M *mp;
G *gp;
@@ -312,7 +312,7 @@ runtime_mcall(void (*pfn)(G*))
if(gp != nil) {
#ifdef USING_SPLIT_STACK
- __splitstack_getcontext(&g->stackcontext[0]);
+ __splitstack_getcontext((void*)(&g->stackcontext[0]));
#else
// We have to point to an address on the stack that is
// below the saved registers.
@@ -336,9 +336,9 @@ runtime_mcall(void (*pfn)(G*))
}
if (gp == nil || !gp->fromgogo) {
#ifdef USING_SPLIT_STACK
- __splitstack_setcontext(&mp->g0->stackcontext[0]);
+ __splitstack_setcontext((void*)(&mp->g0->stackcontext[0]));
#endif
- mp->g0->entry = (byte*)pfn;
+ mp->g0->entry = fv;
mp->g0->param = gp;
// It's OK to set g directly here because this case
@@ -364,52 +364,20 @@ runtime_mcall(void (*pfn)(G*))
//
// Design doc at http://golang.org/s/go11sched.
-enum
-{
- // Number of goroutine ids to grab from runtime_sched->goidgen to local per-P cache at once.
- // 16 seems to provide enough amortization, but other than that it's mostly arbitrary number.
- GoidCacheBatch = 16,
-};
-
-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");
-extern bool needaddgcproc(void)
- __asm__ (GOSYM_PREFIX "runtime.needaddgcproc");
-extern void startm(P*, bool)
- __asm__(GOSYM_PREFIX "runtime.startm");
-extern void newm(void(*)(void), P*)
- __asm__(GOSYM_PREFIX "runtime.newm");
Sched* runtime_sched;
-M runtime_m0;
-G runtime_g0; // idle goroutine for m0
-G* runtime_lastg;
-P** runtime_allp;
-int8* runtime_goos;
int32 runtime_ncpu;
-bool runtime_precisestack;
bool runtime_isarchive;
-void* runtime_mstart(void*);
-static void exitsyscall0(G*);
-static void park0(G*);
-static void goexit0(G*);
-static bool exitsyscallfast(void);
-
-extern void setncpu(int32)
- __asm__(GOSYM_PREFIX "runtime.setncpu");
-extern void setpagesize(uintptr_t)
- __asm__(GOSYM_PREFIX "runtime.setpagesize");
-extern void allgadd(G*)
- __asm__(GOSYM_PREFIX "runtime.allgadd");
-extern void mcommoninit(M*)
- __asm__(GOSYM_PREFIX "runtime.mcommoninit");
+extern void kickoff(void)
+ __asm__(GOSYM_PREFIX "runtime.kickoff");
+extern void mstart1(void)
+ __asm__(GOSYM_PREFIX "runtime.mstart1");
extern void stopm(void)
__asm__(GOSYM_PREFIX "runtime.stopm");
extern void handoffp(P*)
@@ -422,107 +390,25 @@ extern void schedule(void)
__asm__(GOSYM_PREFIX "runtime.schedule");
extern void execute(G*, bool)
__asm__(GOSYM_PREFIX "runtime.execute");
-extern void gfput(P*, G*)
- __asm__(GOSYM_PREFIX "runtime.gfput");
+extern void reentersyscall(uintptr, uintptr)
+ __asm__(GOSYM_PREFIX "runtime.reentersyscall");
+extern void reentersyscallblock(uintptr, uintptr)
+ __asm__(GOSYM_PREFIX "runtime.reentersyscallblock");
extern G* gfget(P*)
__asm__(GOSYM_PREFIX "runtime.gfget");
-extern void procresize(int32)
- __asm__(GOSYM_PREFIX "runtime.procresize");
extern void acquirep(P*)
__asm__(GOSYM_PREFIX "runtime.acquirep");
extern P* releasep(void)
__asm__(GOSYM_PREFIX "runtime.releasep");
extern void incidlelocked(int32)
__asm__(GOSYM_PREFIX "runtime.incidlelocked");
-extern void checkdead(void)
- __asm__(GOSYM_PREFIX "runtime.checkdead");
-extern void sysmon(void)
- __asm__(GOSYM_PREFIX "runtime.sysmon");
-extern void mput(M*)
- __asm__(GOSYM_PREFIX "runtime.mput");
-extern M* mget(void)
- __asm__(GOSYM_PREFIX "runtime.mget");
extern void globrunqput(G*)
__asm__(GOSYM_PREFIX "runtime.globrunqput");
extern P* pidleget(void)
__asm__(GOSYM_PREFIX "runtime.pidleget");
-extern bool runqempty(P*)
- __asm__(GOSYM_PREFIX "runtime.runqempty");
-extern void runqput(P*, G*, bool)
- __asm__(GOSYM_PREFIX "runtime.runqput");
bool runtime_isstarted;
-// The bootstrap sequence is:
-//
-// call osinit
-// call schedinit
-// make & queue new G
-// call runtime_mstart
-//
-// The new G calls runtime_main.
-void
-runtime_schedinit(void)
-{
- M *m;
- int32 n, procs;
- String s;
- const byte *p;
- Eface i;
-
- setncpu(runtime_ncpu);
- setpagesize(getpagesize());
- runtime_sched = runtime_getsched();
-
- m = &runtime_m0;
- g = &runtime_g0;
- m->g0 = g;
- m->curg = g;
- g->m = m;
-
- initcontext();
-
- runtime_sched->maxmcount = 10000;
- runtime_precisestack = 0;
-
- // runtime_symtabinit();
- runtime_mallocinit();
- mcommoninit(m);
- runtime_alginit(); // maps must not be used before this call
-
- // Initialize the itable value for newErrorCString,
- // so that the next time it gets called, possibly
- // in a fault during a garbage collection, it will not
- // need to allocated memory.
- runtime_newErrorCString(0, &i);
-
- // Initialize the cached gotraceback value, since
- // gotraceback calls getenv, which mallocs on Plan 9.
- runtime_gotraceback(nil);
-
- runtime_goargs();
- runtime_goenvs();
- runtime_parsedebugvars();
-
- runtime_sched->lastpoll = runtime_nanotime();
- procs = 1;
- s = runtime_getenv("GOMAXPROCS");
- p = s.str;
- if(p != nil && (n = runtime_atoi(p, s.len)) > 0) {
- if(n > _MaxGomaxprocs)
- n = _MaxGomaxprocs;
- procs = n;
- }
- runtime_allp = runtime_getAllP();
- procresize(procs);
-
- // Can not enable GC until all roots are registered.
- // mstats()->enablegc = 1;
-}
-
-extern void main_init(void) __asm__ (GOSYM_PREFIX "__go_init_main");
-extern void main_main(void) __asm__ (GOSYM_PREFIX "main.main");
-
// Used to determine the field alignment.
struct field_align
@@ -531,92 +417,6 @@ struct field_align
Hchan *p;
};
-static void
-initDone(void *arg __attribute__ ((unused))) {
- runtime_unlockOSThread();
-};
-
-// The main goroutine.
-// Note: C frames in general are not copyable during stack growth, for two reasons:
-// 1) We don't know where in a frame to find pointers to other stack locations.
-// 2) There's no guarantee that globals or heap values do not point into the frame.
-//
-// The C frame for runtime.main is copyable, because:
-// 1) There are no pointers to other stack locations in the frame
-// (d.fn points at a global, d.link is nil, d.argp is -1).
-// 2) The only pointer into this frame is from the defer chain,
-// which is explicitly handled during stack copying.
-void
-runtime_main(void* dummy __attribute__((unused)))
-{
- Defer d;
- _Bool frame;
-
- newm(sysmon, nil);
-
- // Lock the main goroutine onto this, the main OS thread,
- // during initialization. Most programs won't care, but a few
- // do require certain calls to be made by the main thread.
- // Those can arrange for main.main to run in the main thread
- // by calling runtime.LockOSThread during initialization
- // to preserve the lock.
- runtime_lockOSThread();
-
- // Defer unlock so that runtime.Goexit during init does the unlock too.
- d.pfn = (uintptr)(void*)initDone;
- d.link = g->_defer;
- d.arg = (void*)-1;
- d._panic = g->_panic;
- d.retaddr = 0;
- d.makefunccanrecover = 0;
- d.frame = &frame;
- d.special = true;
- g->_defer = &d;
-
- if(g->m != &runtime_m0)
- runtime_throw("runtime_main not on m0");
- __go_go(runtime_MHeap_Scavenger, nil);
-
- makeMainInitDone();
-
- _cgo_notify_runtime_init_done();
-
- main_init();
-
- closeMainInitDone();
-
- if(g->_defer != &d || (void*)d.pfn != initDone)
- runtime_throw("runtime: bad defer entry after init");
- g->_defer = d.link;
- runtime_unlockOSThread();
-
- // For gccgo we have to wait until after main is initialized
- // to enable GC, because initializing main registers the GC
- // roots.
- mstats()->enablegc = 1;
-
- if(runtime_isarchive) {
- // This is not a complete program, but is instead a
- // library built using -buildmode=c-archive or
- // c-shared. Now that we are initialized, there is
- // nothing further to do.
- return;
- }
-
- main_main();
-
- // Make racy client program work: if panicking on
- // another goroutine at the same time as main returns,
- // let the other goroutine finish printing the panic trace.
- // Once it does, it will exit. See issue 3934.
- if(runtime_panicking())
- runtime_park(nil, nil, "panicwait");
-
- runtime_exit(0);
- for(;;)
- *(int32*)0 = 0;
-}
-
void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");
// getTraceback stores a traceback of gp in the g's traceback field
@@ -629,7 +429,7 @@ void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");
void getTraceback(G* me, G* gp)
{
#ifdef USING_SPLIT_STACK
- __splitstack_getcontext(&me->stackcontext[0]);
+ __splitstack_getcontext((void*)(&me->stackcontext[0]));
#endif
getcontext(ucontext_arg(&me->context[0]));
@@ -639,68 +439,80 @@ void getTraceback(G* me, G* gp)
}
// Do a stack trace of gp, and then restore the context to
-// gp->dotraceback.
+// gp->traceback->gp.
-static void
+void
gtraceback(G* gp)
{
Traceback* traceback;
+ M* holdm;
traceback = gp->traceback;
gp->traceback = nil;
- if(gp->m != nil)
+ holdm = gp->m;
+ if(holdm != nil && holdm != g->m)
runtime_throw("gtraceback: m is not nil");
gp->m = traceback->gp->m;
traceback->c = runtime_callers(1, traceback->locbuf,
sizeof traceback->locbuf / sizeof traceback->locbuf[0], false);
- gp->m = nil;
+ gp->m = holdm;
runtime_gogo(traceback->gp);
}
-// Called to start an M.
+// Called by pthread_create to start an M.
void*
-runtime_mstart(void* mp)
+runtime_mstart(void *arg)
{
- M *m;
- G *gp;
+ M* mp;
+ G* gp;
- m = (M*)mp;
- g = m->g0;
- g->m = m;
- gp = g;
+ mp = (M*)(arg);
+ gp = mp->g0;
+ gp->m = mp;
- initcontext();
+ g = gp;
gp->entry = nil;
gp->param = nil;
+ initcontext();
+
// Record top of stack for use by mcall.
// Once we call schedule we're never coming back,
// so other calls can reuse this stack space.
#ifdef USING_SPLIT_STACK
- __splitstack_getcontext(&g->stackcontext[0]);
+ __splitstack_getcontext((void*)(&gp->stackcontext[0]));
#else
- gp->gcinitialsp = ∓
+ gp->gcinitialsp = &arg;
// Setting gcstacksize to 0 is a marker meaning that gcinitialsp
// is the top of the stack, not the bottom.
gp->gcstacksize = 0;
- gp->gcnextsp = ∓
+ gp->gcnextsp = &arg;
#endif
+
+ // Save the currently active context. This will return
+ // multiple times via the setcontext call in mcall.
getcontext(ucontext_arg(&gp->context[0]));
- if(gp->traceback != nil)
+ if(gp->traceback != nil) {
+ // Got here from getTraceback.
+ // I'm not sure this ever actually happens--getTraceback
+ // may always go to the getcontext call in mcall.
gtraceback(gp);
+ }
if(gp->entry != nil) {
// Got here from mcall.
- void (*pfn)(G*) = (void (*)(G*))gp->entry;
+ FuncVal *fv = gp->entry;
+ void (*pfn)(G*) = (void (*)(G*))fv->fn;
G* gp1 = (G*)gp->param;
gp->entry = nil;
gp->param = nil;
- pfn(gp1);
+ __builtin_call_with_static_chain(pfn(gp1), fv);
*(int*)0x21 = 0x21;
}
- runtime_minit();
+
+ // Initial call to getcontext--starting thread.
#ifdef USING_SPLIT_STACK
{
@@ -709,35 +521,10 @@ runtime_mstart(void* mp)
}
#endif
- // Install signal handlers; after minit so that minit can
- // prepare the thread to be able to handle the signals.
- if(m == &runtime_m0) {
- if(runtime_iscgo) {
- bool* cgoHasExtraM = runtime_getCgoHasExtraM();
- if(!*cgoHasExtraM) {
- *cgoHasExtraM = true;
- runtime_newextram();
- }
- }
- runtime_initsig(false);
- }
-
- if(m->mstartfn)
- ((void (*)(void))m->mstartfn)();
-
- if(m->helpgc) {
- m->helpgc = 0;
- stopm();
- } else if(m != &runtime_m0) {
- acquirep((P*)m->nextp);
- m->nextp = 0;
- }
- schedule();
-
- // 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.
+ mstart1();
+ // mstart1 does not return, but we need a return statement
+ // here to avoid a compiler warning.
return nil;
}
@@ -750,39 +537,6 @@ struct CgoThreadStart
void (*fn)(void);
};
-M* runtime_allocm(P*, bool, byte**, uintptr*)
- __asm__(GOSYM_PREFIX "runtime.allocm");
-
-// Allocate a new m unassociated with any thread.
-// Can use p for allocation context if needed.
-M*
-runtime_allocm(P *p, bool allocatestack, byte** ret_g0_stack, uintptr* ret_g0_stacksize)
-{
- M *mp;
-
- g->m->locks++; // disable GC because it can be called from sysmon
- if(g->m->p == 0)
- acquirep(p); // temporarily borrow p for mallocs in this function
-#if 0
- if(mtype == nil) {
- Eface e;
- runtime_gc_m_ptr(&e);
- mtype = ((const PtrType*)e.__type_descriptor)->__element_type;
- }
-#endif
-
- mp = runtime_mal(sizeof *mp);
- mcommoninit(mp);
- mp->g0 = runtime_malg(allocatestack, false, ret_g0_stack, ret_g0_stacksize);
- mp->g0->m = mp;
-
- if(p == (P*)g->m->p)
- releasep();
- g->m->locks--;
-
- return mp;
-}
-
void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext");
// setGContext sets up a new goroutine context for the current g.
@@ -797,7 +551,7 @@ setGContext()
gp->entry = nil;
gp->param = nil;
#ifdef USING_SPLIT_STACK
- __splitstack_getcontext(&gp->stackcontext[0]);
+ __splitstack_getcontext((void*)(&gp->stackcontext[0]));
val = 0;
__splitstack_block_signals(&val, nil);
#else
@@ -810,11 +564,12 @@ setGContext()
if(gp->entry != nil) {
// Got here from mcall.
- void (*pfn)(G*) = (void (*)(G*))gp->entry;
+ FuncVal *fv = gp->entry;
+ void (*pfn)(G*) = (void (*)(G*))fv->fn;
G* gp1 = (G*)gp->param;
gp->entry = nil;
gp->param = nil;
- pfn(gp1);
+ __builtin_call_with_static_chain(pfn(gp1), fv);
*(int*)0x22 = 0x22;
}
}
@@ -834,224 +589,6 @@ makeGContext(G* gp, byte* sp, uintptr spsize) {
makecontext(uc, kickoff, 0);
}
-// Create a new m. It will start off with a call to fn, or else the scheduler.
-void
-newm(void(*fn)(void), P *p)
-{
- M *mp;
-
- mp = runtime_allocm(p, false, nil, nil);
- mp->nextp = (uintptr)p;
- mp->mstartfn = (uintptr)(void*)fn;
-
- runtime_newosproc(mp);
-}
-
-static void
-mspinning(void)
-{
- g->m->spinning = true;
-}
-
-// Schedules some M to run the p (creates an M if necessary).
-// If p==nil, tries to get an idle P, if no idle P's does nothing.
-void
-startm(P *p, bool spinning)
-{
- M *mp;
- void (*fn)(void);
-
- runtime_lock(&runtime_sched->lock);
- if(p == nil) {
- p = pidleget();
- if(p == nil) {
- runtime_unlock(&runtime_sched->lock);
- if(spinning)
- runtime_xadd(&runtime_sched->nmspinning, -1);
- return;
- }
- }
- mp = mget();
- runtime_unlock(&runtime_sched->lock);
- if(mp == nil) {
- fn = nil;
- if(spinning)
- fn = mspinning;
- newm(fn, p);
- return;
- }
- if(mp->spinning)
- runtime_throw("startm: m is spinning");
- if(mp->nextp)
- runtime_throw("startm: m has p");
- if(spinning && !runqempty(p)) {
- runtime_throw("startm: p has runnable gs");
- }
- mp->spinning = spinning;
- mp->nextp = (uintptr)p;
- runtime_notewakeup(&mp->park);
-}
-
-// Puts the current goroutine into a waiting state and calls unlockf.
-// If unlockf returns false, the goroutine is resumed.
-void
-runtime_park(bool(*unlockf)(G*, void*), void *lock, const char *reason)
-{
- if(g->atomicstatus != _Grunning)
- runtime_throw("bad g status");
- g->m->waitlock = lock;
- g->m->waitunlockf = unlockf;
- g->waitreason = runtime_gostringnocopy((const byte*)reason);
- runtime_mcall(park0);
-}
-
-void gopark(FuncVal *, void *, String, byte, int)
- __asm__ ("runtime.gopark");
-
-void
-gopark(FuncVal *unlockf, void *lock, String reason,
- byte traceEv __attribute__ ((unused)),
- int traceskip __attribute__ ((unused)))
-{
- if(g->atomicstatus != _Grunning)
- runtime_throw("bad g status");
- g->m->waitlock = lock;
- g->m->waitunlockf = unlockf == nil ? nil : (void*)unlockf->fn;
- g->waitreason = reason;
- runtime_mcall(park0);
-}
-
-static bool
-parkunlock(G *gp, void *lock)
-{
- USED(gp);
- runtime_unlock(lock);
- return true;
-}
-
-// 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_parkunlock(Lock *lock, const char *reason)
-{
- runtime_park(parkunlock, lock, reason);
-}
-
-void goparkunlock(Lock *, String, byte, int)
- __asm__ (GOSYM_PREFIX "runtime.goparkunlock");
-
-void
-goparkunlock(Lock *lock, String reason, byte traceEv __attribute__ ((unused)),
- int traceskip __attribute__ ((unused)))
-{
- if(g->atomicstatus != _Grunning)
- runtime_throw("bad g status");
- g->m->waitlock = lock;
- g->m->waitunlockf = parkunlock;
- g->waitreason = reason;
- runtime_mcall(park0);
-}
-
-// runtime_park continuation on g0.
-static void
-park0(G *gp)
-{
- M *m;
- bool ok;
-
- m = g->m;
- gp->atomicstatus = _Gwaiting;
- gp->m = nil;
- m->curg = nil;
- if(m->waitunlockf) {
- ok = ((bool (*)(G*, void*))m->waitunlockf)(gp, m->waitlock);
- m->waitunlockf = nil;
- m->waitlock = nil;
- if(!ok) {
- gp->atomicstatus = _Grunnable;
- execute(gp, true); // Schedule it back, never returns.
- }
- }
- if(m->lockedg) {
- stoplockedm();
- execute(gp, true); // Never returns.
- }
- schedule();
-}
-
-// Scheduler yield.
-void
-runtime_gosched(void)
-{
- if(g->atomicstatus != _Grunning)
- runtime_throw("bad g status");
- runtime_mcall(runtime_gosched0);
-}
-
-// runtime_gosched continuation on g0.
-void
-runtime_gosched0(G *gp)
-{
- M *m;
-
- m = g->m;
- gp->atomicstatus = _Grunnable;
- gp->m = nil;
- m->curg = nil;
- runtime_lock(&runtime_sched->lock);
- globrunqput(gp);
- runtime_unlock(&runtime_sched->lock);
- if(m->lockedg) {
- stoplockedm();
- execute(gp, true); // Never returns.
- }
- schedule();
-}
-
-// Finishes execution of the current goroutine.
-// Need to mark it as nosplit, because it runs with sp > stackbase (as runtime_lessstack).
-// Since it does not return it does not matter. But if it is preempted
-// at the split stack check, GC will complain about inconsistent sp.
-void runtime_goexit1(void) __attribute__ ((noinline));
-void
-runtime_goexit1(void)
-{
- if(g->atomicstatus != _Grunning)
- runtime_throw("bad g status");
- runtime_mcall(goexit0);
-}
-
-// runtime_goexit1 continuation on g0.
-static void
-goexit0(G *gp)
-{
- M *m;
-
- m = g->m;
- gp->atomicstatus = _Gdead;
- gp->entry = nil;
- gp->m = nil;
- gp->lockedm = nil;
- gp->paniconfault = 0;
- gp->_defer = nil; // should be true already but just in case.
- gp->_panic = nil; // non-nil for Goexit during panic. points at stack-allocated data.
- gp->writebuf.__values = nil;
- gp->writebuf.__count = 0;
- gp->writebuf.__capacity = 0;
- gp->waitreason = runtime_gostringnocopy(nil);
- gp->param = nil;
- m->curg->m = nil;
- m->curg = nil;
- m->lockedg = nil;
- if(m->locked & ~_LockExternal) {
- runtime_printf("invalid m->locked = %d\n", m->locked);
- runtime_throw("internal lockOSThread error");
- }
- m->locked = 0;
- gfput((P*)m->p, gp);
- schedule();
-}
-
// 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,
@@ -1072,11 +609,8 @@ runtime_entersyscall(int32 dummy __attribute__ ((unused)))
// held in registers will be seen by the garbage collector.
getcontext(ucontext_arg(&g->gcregs[0]));
- // Do the work in a separate function, so that this function
- // doesn't save any registers on its own stack. If this
- // function does save any registers, we might store the wrong
- // value in the call to getcontext.
- //
+ // Note that if this function does save any registers itself,
+ // we might store the wrong value in the call to getcontext.
// FIXME: This assumes that we do not need to save any
// callee-saved registers to access the TLS variable g. We
// don't want to put the ucontext_t on the stack because it is
@@ -1088,10 +622,6 @@ runtime_entersyscall(int32 dummy __attribute__ ((unused)))
static void
doentersyscall(uintptr pc, uintptr sp)
{
- // Disable preemption because during this function g is in _Gsyscall status,
- // but can have inconsistent g->sched, do not let GC observe it.
- g->m->locks++;
-
// Leave SP around for GC and traceback.
#ifdef USING_SPLIT_STACK
{
@@ -1109,43 +639,28 @@ doentersyscall(uintptr pc, uintptr sp)
}
#endif
- g->syscallsp = sp;
- g->syscallpc = pc;
-
- g->atomicstatus = _Gsyscall;
-
- if(runtime_atomicload(&runtime_sched->sysmonwait)) { // TODO: fast atomic
- runtime_lock(&runtime_sched->lock);
- if(runtime_atomicload(&runtime_sched->sysmonwait)) {
- runtime_atomicstore(&runtime_sched->sysmonwait, 0);
- runtime_notewakeup(&runtime_sched->sysmonnote);
- }
- runtime_unlock(&runtime_sched->lock);
- }
-
- g->m->mcache = nil;
- ((P*)(g->m->p))->m = 0;
- runtime_atomicstore(&((P*)g->m->p)->status, _Psyscall);
- if(runtime_atomicload(&runtime_sched->gcwaiting)) {
- runtime_lock(&runtime_sched->lock);
- if (runtime_sched->stopwait > 0 && runtime_cas(&((P*)g->m->p)->status, _Psyscall, _Pgcstop)) {
- if(--runtime_sched->stopwait == 0)
- runtime_notewakeup(&runtime_sched->stopnote);
- }
- runtime_unlock(&runtime_sched->lock);
- }
-
- g->m->locks--;
+ reentersyscall(pc, sp);
}
+static void doentersyscallblock(uintptr, uintptr)
+ __attribute__ ((no_split_stack, noinline));
+
// The same as runtime_entersyscall(), but with a hint that the syscall is blocking.
void
runtime_entersyscallblock(int32 dummy __attribute__ ((unused)))
{
- P *p;
+ // Save the registers in the g structure so that any pointers
+ // held in registers will be seen by the garbage collector.
+ getcontext(ucontext_arg(&g->gcregs[0]));
- g->m->locks++; // see comment in entersyscall
+ // See comment in runtime_entersyscall.
+ doentersyscallblock((uintptr)runtime_getcallerpc(&dummy),
+ (uintptr)runtime_getcallersp(&dummy));
+}
+static void
+doentersyscallblock(uintptr pc, uintptr sp)
+{
// Leave SP around for GC and traceback.
#ifdef USING_SPLIT_STACK
{
@@ -1156,175 +671,14 @@ runtime_entersyscallblock(int32 dummy __attribute__ ((unused)))
g->gcstacksize = (uintptr)gcstacksize;
}
#else
- g->gcnextsp = (byte *) &p;
-#endif
-
- // Save the registers in the g structure so that any pointers
- // held in registers will be seen by the garbage collector.
- getcontext(ucontext_arg(&g->gcregs[0]));
-
- g->syscallpc = (uintptr)runtime_getcallerpc(&dummy);
- g->syscallsp = (uintptr)runtime_getcallersp(&dummy);
-
- g->atomicstatus = _Gsyscall;
-
- p = releasep();
- handoffp(p);
- if(g->isbackground) // do not consider blocked scavenger for deadlock detection
- incidlelocked(1);
-
- g->m->locks--;
-}
-
-// The goroutine g exited its system call.
-// Arrange for it to run on a cpu again.
-// This is called only from the go syscall library, not
-// from the low-level system calls used by the runtime.
-void
-runtime_exitsyscall(int32 dummy __attribute__ ((unused)))
-{
- G *gp;
+ {
+ void *v;
- gp = g;
- gp->m->locks++; // see comment in entersyscall
-
- if(gp->isbackground) // do not consider blocked scavenger for deadlock detection
- incidlelocked(-1);
-
- gp->waitsince = 0;
- if(exitsyscallfast()) {
- // There's a cpu for us, so we can run.
- ((P*)gp->m->p)->syscalltick++;
- gp->atomicstatus = _Grunning;
- // Garbage collector isn't running (since we are),
- // so okay to clear gcstack and gcsp.
-#ifdef USING_SPLIT_STACK
- gp->gcstack = nil;
-#endif
- gp->gcnextsp = nil;
- runtime_memclr(&gp->gcregs[0], sizeof gp->gcregs);
- gp->syscallsp = 0;
- gp->m->locks--;
- return;
+ g->gcnextsp = (byte *) &v;
}
-
- gp->m->locks--;
-
- // Call the scheduler.
- runtime_mcall(exitsyscall0);
-
- // Scheduler returned, so we're allowed to run now.
- // Delete the gcstack information that we left for
- // the garbage collector during the system call.
- // Must wait until now because until gosched returns
- // we don't know for sure that the garbage collector
- // is not running.
-#ifdef USING_SPLIT_STACK
- gp->gcstack = nil;
#endif
- gp->gcnextsp = nil;
- runtime_memclr(&gp->gcregs[0], sizeof gp->gcregs);
-
- gp->syscallsp = 0;
- // Note that this gp->m might be different than the earlier
- // gp->m after returning from runtime_mcall.
- ((P*)gp->m->p)->syscalltick++;
-}
-
-static bool
-exitsyscallfast(void)
-{
- G *gp;
- P *p;
-
- gp = g;
-
- // Freezetheworld sets stopwait but does not retake P's.
- if(runtime_sched->stopwait) {
- gp->m->p = 0;
- return false;
- }
-
- // Try to re-acquire the last P.
- if(gp->m->p && ((P*)gp->m->p)->status == _Psyscall && runtime_cas(&((P*)gp->m->p)->status, _Psyscall, _Prunning)) {
- // There's a cpu for us, so we can run.
- gp->m->mcache = ((P*)gp->m->p)->mcache;
- ((P*)gp->m->p)->m = (uintptr)gp->m;
- return true;
- }
- // Try to get any other idle P.
- gp->m->p = 0;
- if(runtime_sched->pidle) {
- runtime_lock(&runtime_sched->lock);
- p = pidleget();
- if(p && runtime_atomicload(&runtime_sched->sysmonwait)) {
- runtime_atomicstore(&runtime_sched->sysmonwait, 0);
- runtime_notewakeup(&runtime_sched->sysmonnote);
- }
- runtime_unlock(&runtime_sched->lock);
- if(p) {
- acquirep(p);
- return true;
- }
- }
- return false;
-}
-
-// runtime_exitsyscall slow path on g0.
-// Failed to acquire P, enqueue gp as runnable.
-static void
-exitsyscall0(G *gp)
-{
- M *m;
- P *p;
-
- m = g->m;
- gp->atomicstatus = _Grunnable;
- gp->m = nil;
- m->curg = nil;
- runtime_lock(&runtime_sched->lock);
- p = pidleget();
- if(p == nil)
- globrunqput(gp);
- else if(runtime_atomicload(&runtime_sched->sysmonwait)) {
- runtime_atomicstore(&runtime_sched->sysmonwait, 0);
- runtime_notewakeup(&runtime_sched->sysmonnote);
- }
- runtime_unlock(&runtime_sched->lock);
- if(p) {
- acquirep(p);
- execute(gp, false); // Never returns.
- }
- if(m->lockedg) {
- // Wait until another thread schedules gp and so m again.
- stoplockedm();
- execute(gp, false); // Never returns.
- }
- stopm();
- schedule(); // Never returns.
-}
-
-void syscall_entersyscall(void)
- __asm__(GOSYM_PREFIX "syscall.Entersyscall");
-
-void syscall_entersyscall(void) __attribute__ ((no_split_stack));
-
-void
-syscall_entersyscall()
-{
- runtime_entersyscall(0);
-}
-
-void syscall_exitsyscall(void)
- __asm__(GOSYM_PREFIX "syscall.Exitsyscall");
-
-void syscall_exitsyscall(void) __attribute__ ((no_split_stack));
-
-void
-syscall_exitsyscall()
-{
- runtime_exitsyscall(0);
+ reentersyscallblock(pc, sp);
}
// Allocate a new g, with a stack big enough for stacksize bytes.
@@ -1359,10 +713,10 @@ runtime_malg(bool allocatestack, bool signalstack, byte** ret_stack, uintptr* re
#if USING_SPLIT_STACK
*ret_stack = __splitstack_makecontext(stacksize,
- &newg->stackcontext[0],
+ (void*)(&newg->stackcontext[0]),
&ss_stacksize);
*ret_stacksize = (uintptr)ss_stacksize;
- __splitstack_block_signals_context(&newg->stackcontext[0],
+ __splitstack_block_signals_context((void*)(&newg->stackcontext[0]),
&dont_block_signals, nil);
#else
// In 64-bit mode, the maximum Go allocation space is
@@ -1372,12 +726,12 @@ runtime_malg(bool allocatestack, bool signalstack, byte** ret_stack, uintptr* re
// 32-bit mode, the Go allocation space is all of
// memory anyhow.
if(sizeof(void*) == 8) {
- void *p = runtime_SysAlloc(stacksize, &mstats()->other_sys);
+ void *p = runtime_sysAlloc(stacksize, &mstats()->other_sys);
if(p == nil)
runtime_throw("runtime: cannot allocate memory for goroutine stack");
*ret_stack = (byte*)p;
} else {
- *ret_stack = runtime_mallocgc(stacksize, 0, FlagNoProfiling|FlagNoGC);
+ *ret_stack = runtime_mallocgc(stacksize, nil, false);
runtime_xadd(&runtime_stacks_sys, stacksize);
}
*ret_stacksize = (uintptr)stacksize;
@@ -1388,187 +742,29 @@ runtime_malg(bool allocatestack, bool signalstack, byte** ret_stack, uintptr* re
return newg;
}
-G*
-__go_go(void (*fn)(void*), void* arg)
-{
- byte *sp;
- size_t spsize;
- G *newg;
- P *p;
-
-//runtime_printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret);
- if(fn == nil) {
- g->m->throwing = -1; // do not dump full stacks
- runtime_throw("go of nil func value");
- }
- g->m->locks++; // disable preemption because it can be holding p in a local var
+void resetNewG(G*, void **, uintptr*)
+ __asm__(GOSYM_PREFIX "runtime.resetNewG");
- p = (P*)g->m->p;
- if((newg = gfget(p)) != nil) {
+// Reset stack information for g pulled out of the cache to start a
+// new goroutine.
+void
+resetNewG(G *newg, void **sp, uintptr *spsize)
+{
#ifdef USING_SPLIT_STACK
- int dont_block_signals = 0;
+ int dont_block_signals = 0;
+ size_t ss_spsize;
- sp = __splitstack_resetcontext(&newg->stackcontext[0],
- &spsize);
- __splitstack_block_signals_context(&newg->stackcontext[0],
- &dont_block_signals, nil);
+ *sp = __splitstack_resetcontext((void*)(&newg->stackcontext[0]), &ss_spsize);
+ *spsize = ss_spsize;
+ __splitstack_block_signals_context((void*)(&newg->stackcontext[0]),
+ &dont_block_signals, nil);
#else
- sp = newg->gcinitialsp;
- spsize = newg->gcstacksize;
- if(spsize == 0)
- runtime_throw("bad spsize in __go_go");
- newg->gcnextsp = sp;
+ *sp = newg->gcinitialsp;
+ *spsize = newg->gcstacksize;
+ if(*spsize == 0)
+ runtime_throw("bad spsize in resetNewG");
+ newg->gcnextsp = *sp;
#endif
- newg->traceback = nil;
- } else {
- uintptr malsize;
-
- newg = runtime_malg(true, false, &sp, &malsize);
- spsize = (size_t)malsize;
- newg->atomicstatus = _Gdead;
- allgadd(newg);
- }
-
- newg->entry = (byte*)fn;
- newg->param = arg;
- newg->gopc = (uintptr)__builtin_return_address(0);
- newg->atomicstatus = _Grunnable;
- if(p->goidcache == p->goidcacheend) {
- p->goidcache = runtime_xadd64(&runtime_sched->goidgen, GoidCacheBatch);
- p->goidcacheend = p->goidcache + GoidCacheBatch;
- }
- newg->goid = p->goidcache++;
-
- makeGContext(newg, sp, (uintptr)spsize);
-
- runqput(p, newg, true);
-
- if(runtime_atomicload(&runtime_sched->npidle) != 0 && runtime_atomicload(&runtime_sched->nmspinning) == 0 && fn != runtime_main) // TODO: fast atomic
- wakep();
- g->m->locks--;
- return newg;
-}
-
-void
-runtime_Breakpoint(void)
-{
- runtime_breakpoint();
-}
-
-void runtime_Gosched (void) __asm__ (GOSYM_PREFIX "runtime.Gosched");
-
-void
-runtime_Gosched(void)
-{
- runtime_gosched();
-}
-
-static struct {
- uint32 lock;
- int32 hz;
-} prof;
-
-static void System(void) {}
-static void GC(void) {}
-
-// Called if we receive a SIGPROF signal.
-void
-runtime_sigprof()
-{
- M *mp = g->m;
- int32 n, i;
- bool traceback;
- uintptr pcbuf[TracebackMaxFrames];
- Location locbuf[TracebackMaxFrames];
- Slice stk;
-
- if(prof.hz == 0)
- return;
-
- if(mp == nil)
- return;
-
- // Profiling runs concurrently with GC, so it must not allocate.
- mp->mallocing++;
-
- traceback = true;
-
- if(mp->mcache == nil)
- traceback = false;
-
- n = 0;
-
- if(runtime_atomicload(&runtime_in_callers) > 0) {
- // If SIGPROF arrived while already fetching runtime
- // callers we can have trouble on older systems
- // because the unwind library calls dl_iterate_phdr
- // which was not recursive in the past.
- traceback = false;
- }
-
- if(traceback) {
- n = runtime_callers(0, locbuf, nelem(locbuf), false);
- for(i = 0; i < n; i++)
- pcbuf[i] = locbuf[i].pc;
- }
- if(!traceback || n <= 0) {
- n = 2;
- pcbuf[0] = (uintptr)runtime_getcallerpc(&n);
- if(mp->gcing || mp->helpgc)
- pcbuf[1] = (uintptr)GC;
- else
- pcbuf[1] = (uintptr)System;
- }
-
- if (prof.hz != 0) {
- stk.__values = &pcbuf[0];
- stk.__count = n;
- stk.__capacity = n;
-
- // Simple cas-lock to coordinate with setcpuprofilerate.
- while (!runtime_cas(&prof.lock, 0, 1)) {
- runtime_osyield();
- }
- if (prof.hz != 0) {
- runtime_cpuprofAdd(stk);
- }
- runtime_atomicstore(&prof.lock, 0);
- }
-
- mp->mallocing--;
-}
-
-// Arrange to call fn with a traceback hz times a second.
-void
-runtime_setcpuprofilerate_m(int32 hz)
-{
- // Force sane arguments.
- if(hz < 0)
- hz = 0;
-
- // Disable preemption, otherwise we can be rescheduled to another thread
- // that has profiling enabled.
- g->m->locks++;
-
- // Stop profiler on this thread so that it is safe to lock prof.
- // if a profiling signal came in while we had prof locked,
- // it would deadlock.
- runtime_resetcpuprofiler(0);
-
- while (!runtime_cas(&prof.lock, 0, 1)) {
- runtime_osyield();
- }
- prof.hz = hz;
- runtime_atomicstore(&prof.lock, 0);
-
- runtime_lock(&runtime_sched->lock);
- runtime_sched->profilehz = hz;
- runtime_unlock(&runtime_sched->lock);
-
- if(hz != 0)
- runtime_resetcpuprofiler(hz);
-
- g->m->locks--;
}
// Return whether we are waiting for a GC. This gc toolchain uses
@@ -1578,21 +774,3 @@ runtime_gcwaiting(void)
{
return runtime_sched->gcwaiting;
}
-
-// os_beforeExit is called from os.Exit(0).
-//go:linkname os_beforeExit os.runtime_beforeExit
-
-extern void os_beforeExit() __asm__ (GOSYM_PREFIX "os.runtime_beforeExit");
-
-void
-os_beforeExit()
-{
-}
-
-intgo NumCPU(void) __asm__ (GOSYM_PREFIX "runtime.NumCPU");
-
-intgo
-NumCPU()
-{
- return (intgo)(runtime_ncpu);
-}