summaryrefslogtreecommitdiff
path: root/src/pkg/runtime/proc.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pkg/runtime/proc.c')
-rw-r--r--src/pkg/runtime/proc.c114
1 files changed, 87 insertions, 27 deletions
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 26c1f13a4..db6072b5c 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -7,6 +7,7 @@
#include "defs.h"
#include "malloc.h"
#include "os.h"
+#include "stack.h"
bool runtime·iscgo;
@@ -63,7 +64,6 @@ struct Sched {
int32 mcount; // number of ms that have been created
int32 mcpu; // number of ms executing on cpu
int32 mcpumax; // max number of ms allowed on cpu
- int32 gomaxprocs;
int32 msyscall; // number of ms in system calls
int32 predawn; // running initialization, don't run new gs.
@@ -73,6 +73,7 @@ struct Sched {
};
Sched runtime·sched;
+int32 gomaxprocs;
// Scheduling helpers. Sched must be locked.
static void gput(G*); // put/get on ghead/gtail
@@ -116,13 +117,13 @@ runtime·schedinit(void)
// For debugging:
// Allocate internal symbol table representation now,
// so that we don't need to call malloc when we crash.
- // findfunc(0);
+ // runtime·findfunc(0);
- runtime·sched.gomaxprocs = 1;
+ runtime·gomaxprocs = 1;
p = runtime·getenv("GOMAXPROCS");
if(p != nil && (n = runtime·atoi(p)) != 0)
- runtime·sched.gomaxprocs = n;
- runtime·sched.mcpumax = runtime·sched.gomaxprocs;
+ runtime·gomaxprocs = n;
+ runtime·sched.mcpumax = runtime·gomaxprocs;
runtime·sched.mcount = 1;
runtime·sched.predawn = 1;
@@ -165,6 +166,18 @@ runtime·tracebackothers(G *me)
}
}
+// Mark this g as m's idle goroutine.
+// This functionality might be used in environments where programs
+// are limited to a single thread, to simulate a select-driven
+// network server. It is not exposed via the standard runtime API.
+void
+runtime·idlegoroutine(void)
+{
+ if(g->idlem != nil)
+ runtime·throw("g is already an idle goroutine");
+ g->idlem = m;
+}
+
// Put on `g' queue. Sched must be locked.
static void
gput(G *g)
@@ -176,6 +189,18 @@ gput(G *g)
mnextg(m, g);
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);
+ runtime·throw("runtime: double idle");
+ }
+ g->idlem->idleg = g;
+ return;
+ }
g->schedlink = nil;
if(runtime·sched.ghead == nil)
@@ -198,6 +223,9 @@ gget(void)
if(runtime·sched.ghead == nil)
runtime·sched.gtail = nil;
runtime·sched.gwait--;
+ } else if(m->idleg != nil) {
+ g = m->idleg;
+ m->idleg = nil;
}
return g;
}
@@ -252,8 +280,10 @@ readylocked(G *g)
}
// Mark runnable.
- if(g->status == Grunnable || g->status == Grunning || g->status == Grecovery)
+ if(g->status == Grunnable || g->status == Grunning || g->status == Grecovery || g->status == Gstackalloc) {
+ runtime·printf("goroutine %d has status %d\n", g->goid, g->status);
runtime·throw("bad g->status in ready");
+ }
g->status = Grunnable;
gput(g);
@@ -376,7 +406,7 @@ runtime·starttheworld(void)
{
runtime·lock(&runtime·sched);
runtime·gcwaiting = 0;
- runtime·sched.mcpumax = runtime·sched.gomaxprocs;
+ runtime·sched.mcpumax = runtime·gomaxprocs;
matchmg();
runtime·unlock(&runtime·sched);
}
@@ -491,6 +521,13 @@ scheduler(void)
runtime·free(d);
runtime·gogo(&gp->sched, 1);
}
+
+ if(gp->status == Gstackalloc) {
+ // switched to scheduler stack to call stackalloc.
+ gp->param = runtime·stackalloc((uintptr)gp->param);
+ gp->status = Grunning;
+ runtime·gogo(&gp->sched, 1);
+ }
// Jumped here via runtime·gosave/gogo, so didn't
// execute lock(&runtime·sched) above.
@@ -508,6 +545,8 @@ scheduler(void)
switch(gp->status){
case Grunnable:
case Gdead:
+ case Grecovery:
+ case Gstackalloc:
// Shouldn't have been running!
runtime·throw("bad gp->status in sched");
case Grunning:
@@ -520,6 +559,7 @@ scheduler(void)
gp->lockedm = nil;
m->lockedg = nil;
}
+ gp->idlem = nil;
unwindstack(gp, nil);
gfput(gp);
if(--runtime·sched.gcount == 0)
@@ -701,7 +741,7 @@ runtime·oldstack(void)
goid = old.gobuf.g->goid; // fault if g is bad, before gogo
if(old.free != 0)
- runtime·stackfree(g1->stackguard - StackGuard, old.free);
+ runtime·stackfree(g1->stackguard - StackGuard - StackSystem, old.free);
g1->stackbase = old.stackbase;
g1->stackguard = old.stackguard;
@@ -739,14 +779,15 @@ runtime·newstack(void)
// the new Stktop* is necessary to unwind, but
// we don't need to create a new segment.
top = (Stktop*)(m->morebuf.sp - sizeof(*top));
- stk = g1->stackguard - StackGuard;
+ stk = g1->stackguard - StackGuard - StackSystem;
free = 0;
} else {
// allocate new segment.
framesize += argsize;
- if(framesize < StackBig)
- framesize = StackBig;
framesize += StackExtra; // room for more functions, Stktop.
+ if(framesize < StackMin)
+ framesize = StackMin;
+ framesize += StackSystem;
stk = runtime·stackalloc(framesize);
top = (Stktop*)(stk+framesize-sizeof(*top));
free = framesize;
@@ -767,7 +808,7 @@ runtime·newstack(void)
g1->ispanic = false;
g1->stackbase = (byte*)top;
- g1->stackguard = stk + StackGuard;
+ g1->stackguard = stk + StackGuard + StackSystem;
sp = (byte*)top;
if(argsize > 0) {
@@ -793,18 +834,35 @@ runtime·newstack(void)
G*
runtime·malg(int32 stacksize)
{
- G *g;
+ G *newg;
byte *stk;
+ int32 oldstatus;
- g = runtime·malloc(sizeof(G));
+ newg = runtime·malloc(sizeof(G));
if(stacksize >= 0) {
- stk = runtime·stackalloc(stacksize + StackGuard);
- g->stack0 = stk;
- g->stackguard = stk + StackGuard;
- g->stackbase = stk + StackGuard + stacksize - sizeof(Stktop);
- runtime·memclr(g->stackbase, sizeof(Stktop));
+ if(g == m->g0) {
+ // running on scheduler stack already.
+ stk = runtime·stackalloc(StackSystem + stacksize);
+ } else {
+ // have to call stackalloc on scheduler stack.
+ oldstatus = g->status;
+ g->param = (void*)(StackSystem + stacksize);
+ g->status = Gstackalloc;
+ // next two lines are runtime·gosched without the check
+ // of m->locks. we're almost certainly holding a lock,
+ // but this is not a real rescheduling so it's okay.
+ if(runtime·gosave(&g->sched) == 0)
+ runtime·gogo(&m->sched, 1);
+ stk = g->param;
+ g->param = nil;
+ g->status = oldstatus;
+ }
+ newg->stack0 = stk;
+ newg->stackguard = stk + StackSystem + StackGuard;
+ newg->stackbase = stk + StackSystem + stacksize - sizeof(Stktop);
+ runtime·memclr(newg->stackbase, sizeof(Stktop));
}
- return g;
+ return newg;
}
/*
@@ -826,11 +884,11 @@ runtime·newproc(int32 siz, byte* fn, ...)
argp = (byte*)(&fn+2); // skip caller's saved LR
else
argp = (byte*)(&fn+1);
- runtime·newproc1(fn, argp, siz, 0);
+ runtime·newproc1(fn, argp, siz, 0, runtime·getcallerpc(&siz));
}
G*
-runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret)
+runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
{
byte *sp;
G *newg;
@@ -846,10 +904,10 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret)
if((newg = gfget()) != nil){
newg->status = Gwaiting;
- if(newg->stackguard - StackGuard != newg->stack0)
+ if(newg->stackguard - StackGuard - StackSystem != newg->stack0)
runtime·throw("invalid stack in newg");
} else {
- newg = runtime·malg(StackBig);
+ newg = runtime·malg(StackMin);
newg->status = Gwaiting;
newg->alllink = runtime·allg;
runtime·allg = newg;
@@ -868,6 +926,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret)
newg->sched.pc = (byte*)runtime·goexit;
newg->sched.g = newg;
newg->entry = fn;
+ newg->gopc = (uintptr)callerpc;
runtime·sched.gcount++;
runtime·goidgen++;
@@ -1019,6 +1078,7 @@ runtime·panic(Eface e)
}
// ran out of deferred calls - old-school panic now
+ runtime·startpanic();
printpanics(g->panic);
runtime·dopanic(0);
}
@@ -1098,7 +1158,7 @@ nomatch:
static void
gfput(G *g)
{
- if(g->stackguard - StackGuard != g->stack0)
+ if(g->stackguard - StackGuard - StackSystem != g->stack0)
runtime·throw("invalid stack in gfput");
g->schedlink = runtime·sched.gfree;
runtime·sched.gfree = g;
@@ -1151,10 +1211,10 @@ runtime·gomaxprocsfunc(int32 n)
int32 ret;
runtime·lock(&runtime·sched);
- ret = runtime·sched.gomaxprocs;
+ ret = runtime·gomaxprocs;
if (n <= 0)
n = ret;
- runtime·sched.gomaxprocs = n;
+ runtime·gomaxprocs = n;
runtime·sched.mcpumax = n;
// handle fewer procs?
if(runtime·sched.mcpu > runtime·sched.mcpumax) {