Commit 0a7c7ac8 authored by Rick Hudson's avatar Rick Hudson

runtime: changes to g->atomicstatus (nee status) to support concurrent GC

Every change to g->atomicstatus is now done atomically so that we can
ensure that all gs pass through a gc safepoint on demand. This allows
the GC to move from one phase to the next safely. In some phases the
stack will be scanned. This CL only deals with the infrastructure that
allows g->atomicstatus to go from one state to another. Future CLs
will deal with scanning and monitoring what phase the GC is in.

The major change was to moving to using a Gscan bit to indicate that
the status is in a scan state. The only bug fix was in oldstack where
I wasn't moving to a Gcopystack state in order to block scanning until
the new stack was in place. The proc.go file is waiting for an atomic
load instruction.

LGTM=rsc
R=golang-codereviews, dvyukov, josharian, rsc
CC=golang-codereviews, khr
https://golang.org/cl/132960044
parent 56f8b297
...@@ -396,7 +396,7 @@ dumpgoroutine(G *gp) ...@@ -396,7 +396,7 @@ dumpgoroutine(G *gp)
dumpint((uintptr)sp); dumpint((uintptr)sp);
dumpint(gp->goid); dumpint(gp->goid);
dumpint(gp->gopc); dumpint(gp->gopc);
dumpint(gp->status); dumpint(runtime·readgstatus(gp));
dumpbool(gp->issystem); dumpbool(gp->issystem);
dumpbool(false); // isbackground dumpbool(false); // isbackground
dumpint(gp->waitsince); dumpint(gp->waitsince);
...@@ -442,14 +442,16 @@ dumpgs(void) ...@@ -442,14 +442,16 @@ dumpgs(void)
{ {
G *gp; G *gp;
uint32 i; uint32 i;
uint32 status;
// goroutines & stacks // goroutines & stacks
for(i = 0; i < runtime·allglen; i++) { for(i = 0; i < runtime·allglen; i++) {
gp = runtime·allg[i]; gp = runtime·allg[i];
switch(gp->status){ status = runtime·readgstatus(gp); // The world is stopped so gp will not be in a scan state.
switch(status){
default: default:
runtime·printf("unexpected G.status %d\n", gp->status); runtime·printf("runtime: unexpected G.status %d\n", status);
runtime·throw("mark - bad status"); runtime·throw("dumpgs in STW - bad status");
case Gdead: case Gdead:
break; break;
case Grunnable: case Grunnable:
...@@ -730,7 +732,7 @@ mdump(G *gp) ...@@ -730,7 +732,7 @@ mdump(G *gp)
flush(); flush();
gp->param = nil; gp->param = nil;
gp->status = Grunning; runtime·casgstatus(gp, Gwaiting, Grunning);
runtime·gogo(&gp->sched); runtime·gogo(&gp->sched);
} }
...@@ -751,7 +753,7 @@ runtime∕debug·WriteHeapDump(uintptr fd) ...@@ -751,7 +753,7 @@ runtime∕debug·WriteHeapDump(uintptr fd)
dumpfd = fd; dumpfd = fd;
// Call dump routine on M stack. // Call dump routine on M stack.
g->status = Gwaiting; runtime·casgstatus(g, Grunning, Gwaiting);
g->waitreason = runtime·gostringnocopy((byte*)"dumping heap"); g->waitreason = runtime·gostringnocopy((byte*)"dumping heap");
runtime·mcall(mdump); runtime·mcall(mdump);
......
...@@ -484,6 +484,7 @@ markroot(ParFor *desc, uint32 i) ...@@ -484,6 +484,7 @@ markroot(ParFor *desc, uint32 i)
uint32 spanidx, sg; uint32 spanidx, sg;
G *gp; G *gp;
void *p; void *p;
uint32 status;
USED(&desc); USED(&desc);
// Note: if you add a case here, please also update heapdump.c:dumproots. // Note: if you add a case here, please also update heapdump.c:dumproots.
...@@ -540,7 +541,8 @@ markroot(ParFor *desc, uint32 i) ...@@ -540,7 +541,8 @@ markroot(ParFor *desc, uint32 i)
gp = runtime·allg[i - RootCount]; gp = runtime·allg[i - RootCount];
// remember when we've first observed the G blocked // remember when we've first observed the G blocked
// needed only to output in traceback // needed only to output in traceback
if((gp->status == Gwaiting || gp->status == Gsyscall) && gp->waitsince == 0) status = runtime·readgstatus(gp);
if((status == Gwaiting || status == Gsyscall) && gp->waitsince == 0)
gp->waitsince = work.tstart; gp->waitsince = work.tstart;
// Shrink a stack if not much of it is being used. // Shrink a stack if not much of it is being used.
runtime·shrinkstack(gp); runtime·shrinkstack(gp);
...@@ -737,13 +739,14 @@ scanstack(G *gp) ...@@ -737,13 +739,14 @@ scanstack(G *gp)
Stktop *stk; Stktop *stk;
uintptr sp, guard; uintptr sp, guard;
switch(gp->status){ switch(runtime·readgstatus(gp)) {
default: default:
runtime·printf("unexpected G.status %d (goroutine %p %D)\n", gp->status, gp, gp->goid); runtime·printf("runtime: gp=%p, goid=%D, gp->atomicstatus=%d\n", gp, gp->goid, runtime·readgstatus(gp));
runtime·throw("mark - bad status"); runtime·throw("mark - bad status");
case Gdead: case Gdead:
return; return;
case Grunning: case Grunning:
runtime·printf("runtime: gp=%p, goid=%D, gp->atomicstatus=%d\n", gp, gp->goid, runtime·readgstatus(gp));
runtime·throw("mark - world not stopped"); runtime·throw("mark - world not stopped");
case Grunnable: case Grunnable:
case Gsyscall: case Gsyscall:
...@@ -1349,7 +1352,7 @@ runtime·gc(int32 force) ...@@ -1349,7 +1352,7 @@ runtime·gc(int32 force)
a.start_time = runtime·nanotime(); a.start_time = runtime·nanotime();
// switch to g0, call gc(&a), then switch back // switch to g0, call gc(&a), then switch back
g->param = &a; g->param = &a;
g->status = Gwaiting; runtime·casgstatus(g, Grunning, Gwaiting);
g->waitreason = runtime·gostringnocopy((byte*)"garbage collection"); g->waitreason = runtime·gostringnocopy((byte*)"garbage collection");
runtime·mcall(mgc); runtime·mcall(mgc);
} }
...@@ -1373,7 +1376,7 @@ mgc(G *gp) ...@@ -1373,7 +1376,7 @@ mgc(G *gp)
{ {
gc(gp->param); gc(gp->param);
gp->param = nil; gp->param = nil;
gp->status = Grunning; runtime·casgstatus(gp, Gwaiting, Grunning);
runtime·gogo(&gp->sched); runtime·gogo(&gp->sched);
} }
...@@ -1384,14 +1387,14 @@ runtime·gc_m(void) ...@@ -1384,14 +1387,14 @@ runtime·gc_m(void)
G *gp; G *gp;
gp = g->m->curg; gp = g->m->curg;
gp->status = Gwaiting; runtime·casgstatus(gp, Grunning, Gwaiting);
gp->waitreason = runtime·gostringnocopy((byte*)"garbage collection"); gp->waitreason = runtime·gostringnocopy((byte*)"garbage collection");
a.start_time = (uint64)(g->m->scalararg[0]) | ((uint64)(g->m->scalararg[1]) << 32); a.start_time = (uint64)(g->m->scalararg[0]) | ((uint64)(g->m->scalararg[1]) << 32);
a.eagersweep = g->m->scalararg[2]; a.eagersweep = g->m->scalararg[2];
gc(&a); gc(&a);
gp->status = Grunning; runtime·casgstatus(gp, Gwaiting, Grunning);
} }
static void static void
......
...@@ -288,7 +288,7 @@ func GoroutineProfile(b Slice) (n int, ok bool) { ...@@ -288,7 +288,7 @@ func GoroutineProfile(b Slice) (n int, ok bool) {
saveg(pc, sp, g, r++); saveg(pc, sp, g, r++);
for(i = 0; i < runtime·allglen; i++) { for(i = 0; i < runtime·allglen; i++) {
gp = runtime·allg[i]; gp = runtime·allg[i];
if(gp == g || gp->status == Gdead) if(gp == g || runtime·readgstatus(gp) == Gdead)
continue; continue;
saveg(~(uintptr)0, ~(uintptr)0, gp, r++); saveg(~(uintptr)0, ~(uintptr)0, gp, r++);
} }
......
...@@ -477,6 +477,7 @@ bool ...@@ -477,6 +477,7 @@ bool
runtime·canpanic(G *gp) runtime·canpanic(G *gp)
{ {
M *m; M *m;
uint32 status;
// Note that g is m->gsignal, different from gp. // Note that g is m->gsignal, different from gp.
// Note also that g->m can change at preemption, so m can go stale // Note also that g->m can change at preemption, so m can go stale
...@@ -490,7 +491,8 @@ runtime·canpanic(G *gp) ...@@ -490,7 +491,8 @@ runtime·canpanic(G *gp)
return false; return false;
if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0) if(m->locks-m->softfloat != 0 || m->mallocing != 0 || m->throwing != 0 || m->gcing != 0 || m->dying != 0)
return false; return false;
if(gp->status != Grunning || gp->syscallsp != 0) status = runtime·readgstatus(gp);
if((status&~Gscan) != Grunning || gp->syscallsp != 0)
return false; return false;
#ifdef GOOS_windows #ifdef GOOS_windows
if(m->libcallsp != 0) if(m->libcallsp != 0)
......
...@@ -284,8 +284,10 @@ runtime·goroutineheader(G *gp) ...@@ -284,8 +284,10 @@ runtime·goroutineheader(G *gp)
{ {
String status; String status;
int64 waitfor; int64 waitfor;
uint32 gpstatus;
switch(gp->status) { gpstatus = runtime·readgstatus(gp);
switch(gpstatus) {
case Gidle: case Gidle:
status = runtime·gostringnocopy((byte*)"idle"); status = runtime·gostringnocopy((byte*)"idle");
break; break;
...@@ -304,6 +306,30 @@ runtime·goroutineheader(G *gp) ...@@ -304,6 +306,30 @@ runtime·goroutineheader(G *gp)
else else
status = runtime·gostringnocopy((byte*)"waiting"); status = runtime·gostringnocopy((byte*)"waiting");
break; break;
case Gscan:
status = runtime·gostringnocopy((byte*)"scan");
break;
case Gscanrunnable:
status = runtime·gostringnocopy((byte*)"scanrunnable");
break;
case Gscanrunning:
status = runtime·gostringnocopy((byte*)"scanrunning");
break;
case Gscansyscall:
status = runtime·gostringnocopy((byte*)"scansyscall");
break;
case Gscanenqueue:
status = runtime·gostringnocopy((byte*)"scanenqueue");
break;
case Gscanwaiting:
if(gp->waitreason.str != nil)
status = gp->waitreason;
else
status = runtime·gostringnocopy((byte*)"scanwaiting");
break;
case Gcopystack:
status = runtime·gostringnocopy((byte*)"copystack");
break;
default: default:
status = runtime·gostringnocopy((byte*)"???"); status = runtime·gostringnocopy((byte*)"???");
break; break;
...@@ -311,7 +337,8 @@ runtime·goroutineheader(G *gp) ...@@ -311,7 +337,8 @@ runtime·goroutineheader(G *gp)
// approx time the G is blocked, in minutes // approx time the G is blocked, in minutes
waitfor = 0; waitfor = 0;
if((gp->status == Gwaiting || gp->status == Gsyscall) && gp->waitsince != 0) gpstatus = gpstatus&~Gscan; // drop the scan bit
if((gpstatus == Gwaiting || gpstatus == Gsyscall) && gp->waitsince != 0)
waitfor = (runtime·nanotime() - gp->waitsince) / (60LL*1000*1000*1000); waitfor = (runtime·nanotime() - gp->waitsince) / (60LL*1000*1000*1000);
runtime·printf("goroutine %D [%S", gp->goid, status); runtime·printf("goroutine %D [%S", gp->goid, status);
...@@ -322,12 +349,19 @@ runtime·goroutineheader(G *gp) ...@@ -322,12 +349,19 @@ runtime·goroutineheader(G *gp)
runtime·printf("]:\n"); runtime·printf("]:\n");
} }
static void
dumpgstatus(G* gp)
{
runtime·printf("runtime: gp=%p, goid=%D, gp->atomicstatus=%d\n", gp, gp->goid, runtime·readgstatus(gp));
}
void void
runtime·tracebackothers(G *me) runtime·tracebackothers(G *me)
{ {
G *gp; G *gp;
int32 traceback; int32 traceback;
uintptr i; uintptr i;
uint32 status;
traceback = runtime·gotraceback(nil); traceback = runtime·gotraceback(nil);
...@@ -341,13 +375,14 @@ runtime·tracebackothers(G *me) ...@@ -341,13 +375,14 @@ runtime·tracebackothers(G *me)
runtime·lock(&allglock); runtime·lock(&allglock);
for(i = 0; i < runtime·allglen; i++) { for(i = 0; i < runtime·allglen; i++) {
gp = runtime·allg[i]; gp = runtime·allg[i];
if(gp == me || gp == g->m->curg || gp->status == Gdead) if(gp == me || gp == g->m->curg || runtime·readgstatus(gp) == Gdead)
continue; continue;
if(gp->issystem && traceback < 2) if(gp->issystem && traceback < 2)
continue; continue;
runtime·printf("\n"); runtime·printf("\n");
runtime·goroutineheader(gp); runtime·goroutineheader(gp);
if(gp->status == Grunning) { status = runtime·readgstatus(gp);
if((status&~Gscan) == Grunning){
runtime·printf("\tgoroutine running on other thread; stack unavailable\n"); runtime·printf("\tgoroutine running on other thread; stack unavailable\n");
runtime·printcreatedby(gp); runtime·printcreatedby(gp);
} else } else
...@@ -360,7 +395,7 @@ static void ...@@ -360,7 +395,7 @@ static void
checkmcount(void) checkmcount(void)
{ {
// sched lock is held // sched lock is held
if(runtime·sched.mcount > runtime·sched.maxmcount) { if(runtime·sched.mcount > runtime·sched.maxmcount){
runtime·printf("runtime: program exceeds %d-thread limit\n", runtime·sched.maxmcount); runtime·printf("runtime: program exceeds %d-thread limit\n", runtime·sched.maxmcount);
runtime·throw("thread exhaustion"); runtime·throw("thread exhaustion");
} }
...@@ -393,13 +428,17 @@ mcommoninit(M *mp) ...@@ -393,13 +428,17 @@ mcommoninit(M *mp)
void void
runtime·ready(G *gp) runtime·ready(G *gp)
{ {
uint32 status;
status = runtime·readgstatus(gp);
// Mark runnable. // Mark runnable.
g->m->locks++; // disable preemption because it can be holding p in a local var g->m->locks++; // disable preemption because it can be holding p in a local var
if(gp->status != Gwaiting) { if((status&~Gscan) != Gwaiting){
runtime·printf("goroutine %D has status %d\n", gp->goid, gp->status); dumpgstatus(gp);
runtime·throw("bad g->status in ready"); runtime·throw("bad g->status in ready");
} }
gp->status = Grunnable; // status is Gwaiting or Gscanwaiting, make Grunnable and put on runq
runtime·casgstatus(gp, Gwaiting, Grunnable);
runqput(g->m->p, gp); runqput(g->m->p, gp);
if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0) // TODO: fast atomic if(runtime·atomicload(&runtime·sched.npidle) != 0 && runtime·atomicload(&runtime·sched.nmspinning) == 0) // TODO: fast atomic
wakep(); wakep();
...@@ -503,6 +542,97 @@ runtime·freezetheworld(void) ...@@ -503,6 +542,97 @@ runtime·freezetheworld(void)
runtime·usleep(1000); runtime·usleep(1000);
} }
static bool
isscanstatus(uint32 status)
{
if(status == Gscan)
runtime·throw("isscanstatus: Bad status Gscan");
return (status&Gscan) == Gscan;
}
// All reads and writes of g's status go through readgstatus, casgstatus
// castogscanstatus, casfromgscanstatus.
uint32
runtime·readgstatus(G *gp)
{
return runtime·atomicload(&gp->atomicstatus);
}
// The Gscanstatuses are acting like locks and this releases them.
// If it proves to be a performance hit we should be able to make these
// simple atomic stores but for now we are going to throw if
// we see an inconsistent state.
void
runtime·casfromgscanstatus(G *gp, uint32 oldval, uint32 newval)
{
bool success = false;
// Check that transition is valid.
switch(oldval) {
case Gscanrunnable:
case Gscanwaiting:
case Gscanrunning:
case Gscansyscall:
if(newval == (oldval&~Gscan))
success = runtime·cas(&gp->atomicstatus, oldval, newval);
break;
case Gscanenqueue:
if(newval == Gwaiting)
success = runtime·cas(&gp->atomicstatus, oldval, newval);
break;
}
if(!success){
runtime·printf("runtime: casfromgscanstatus failed gp=%p, oldval=%d, newval=%d\n",
gp, oldval, newval);
dumpgstatus(gp);
runtime·throw("casfromgscanstatus: gp->status is not in scan state");
}
}
// This will return false if the gp is not in the expected status and the cas fails.
// This acts like a lock acquire while the casfromgstatus acts like a lock release.
bool
runtime·castogscanstatus(G *gp, uint32 oldval, uint32 newval)
{
switch(oldval) {
case Grunnable:
case Gwaiting:
case Gsyscall:
if(newval == (oldval|Gscan))
return runtime·cas(&gp->atomicstatus, oldval, newval);
break;
case Grunning:
if(newval == Gscanrunning || newval == Gscanenqueue)
return runtime·cas(&gp->atomicstatus, oldval, newval);
break;
}
runtime·printf("runtime: castogscanstatus oldval=%d newval=%d\n", oldval, newval);
runtime·throw("castogscanstatus");
return false; // not reached
}
// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
// and casfromgscanstatus instead.
// casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that
// put it in the Gscan state is finished.
void
runtime·casgstatus(G *gp, uint32 oldval, uint32 newval)
{
if(isscanstatus(oldval) || isscanstatus(newval) || oldval == newval) {
runtime·printf("casgstatus: oldval=%d, newval=%d\n", oldval, newval);
runtime·throw("casgstatus: bad incoming values");
}
while(!runtime·cas(&gp->atomicstatus, oldval, newval)) {
// loop if gp->atomicstatus is in a scan state giving
// GC time to finish and change the state to oldval.
}
}
// This is used by the GC as well as the routines that do stack dumps. In the case
// of GC all the routines can be reliably stopped. This is not always the case
// when the system is in panic or being exited.
void void
runtime·stoptheworld(void) runtime·stoptheworld(void)
{ {
...@@ -524,7 +654,7 @@ runtime·stoptheworld(void) ...@@ -524,7 +654,7 @@ runtime·stoptheworld(void)
runtime·atomicstore((uint32*)&runtime·sched.gcwaiting, 1); runtime·atomicstore((uint32*)&runtime·sched.gcwaiting, 1);
preemptall(); preemptall();
// stop current P // stop current P
g->m->p->status = Pgcstop; g->m->p->status = Pgcstop; // Pgcstop is only diagnostic.
runtime·sched.stopwait--; runtime·sched.stopwait--;
// try to retake all P's in Psyscall status // try to retake all P's in Psyscall status
for(i = 0; i < runtime·gomaxprocs; i++) { for(i = 0; i < runtime·gomaxprocs; i++) {
...@@ -845,7 +975,9 @@ runtime·newextram(void) ...@@ -845,7 +975,9 @@ runtime·newextram(void)
gp->syscallsp = gp->sched.sp; gp->syscallsp = gp->sched.sp;
gp->syscallstack = gp->stackbase; gp->syscallstack = gp->stackbase;
gp->syscallguard = gp->stackguard; gp->syscallguard = gp->stackguard;
gp->status = Gsyscall; // malg returns status as Gidle, change to Gsyscall before adding to allg
// where GC will see it.
runtime·casgstatus(gp, Gidle, Gsyscall);
gp->m = mp; gp->m = mp;
mp->curg = gp; mp->curg = gp;
mp->locked = LockInternal; mp->locked = LockInternal;
...@@ -1055,7 +1187,7 @@ handoffp(P *p) ...@@ -1055,7 +1187,7 @@ handoffp(P *p)
// no local work, check that there are no spinning/idle M's, // no local work, check that there are no spinning/idle M's,
// otherwise our help is not required // otherwise our help is not required
if(runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) == 0 && // TODO: fast atomic if(runtime·atomicload(&runtime·sched.nmspinning) + runtime·atomicload(&runtime·sched.npidle) == 0 && // TODO: fast atomic
runtime·cas(&runtime·sched.nmspinning, 0, 1)) { runtime·cas(&runtime·sched.nmspinning, 0, 1)){
startm(p, true); startm(p, true);
return; return;
} }
...@@ -1100,6 +1232,7 @@ static void ...@@ -1100,6 +1232,7 @@ static void
stoplockedm(void) stoplockedm(void)
{ {
P *p; P *p;
uint32 status;
if(g->m->lockedg == nil || g->m->lockedg->lockedm != g->m) if(g->m->lockedg == nil || g->m->lockedg->lockedm != g->m)
runtime·throw("stoplockedm: inconsistent locking"); runtime·throw("stoplockedm: inconsistent locking");
...@@ -1112,8 +1245,12 @@ stoplockedm(void) ...@@ -1112,8 +1245,12 @@ stoplockedm(void)
// Wait until another thread schedules lockedg again. // Wait until another thread schedules lockedg again.
runtime·notesleep(&g->m->park); runtime·notesleep(&g->m->park);
runtime·noteclear(&g->m->park); runtime·noteclear(&g->m->park);
if(g->m->lockedg->status != Grunnable) status = runtime·readgstatus(g->m->lockedg);
if((status&~Gscan) != Grunnable){
runtime·printf("runtime:stoplockedm: g is not Grunnable or Gscanrunnable");
dumpgstatus(g);
runtime·throw("stoplockedm: not runnable"); runtime·throw("stoplockedm: not runnable");
}
acquirep(g->m->nextp); acquirep(g->m->nextp);
g->m->nextp = nil; g->m->nextp = nil;
} }
...@@ -1167,11 +1304,7 @@ execute(G *gp) ...@@ -1167,11 +1304,7 @@ execute(G *gp)
{ {
int32 hz; int32 hz;
if(gp->status != Grunnable) { runtime·casgstatus(gp, Grunnable, Grunning);
runtime·printf("execute: bad g status %d\n", gp->status);
runtime·throw("execute: bad g status");
}
gp->status = Grunning;
gp->waitsince = 0; gp->waitsince = 0;
gp->preempt = false; gp->preempt = false;
gp->stackguard0 = gp->stackguard; gp->stackguard0 = gp->stackguard;
...@@ -1219,7 +1352,7 @@ top: ...@@ -1219,7 +1352,7 @@ top:
gp = runtime·netpoll(false); // non-blocking gp = runtime·netpoll(false); // non-blocking
if(gp) { if(gp) {
injectglist(gp->schedlink); injectglist(gp->schedlink);
gp->status = Grunnable; runtime·casgstatus(gp, Gwaiting, Grunnable);
return gp; return gp;
} }
// If number of spinning M's >= number of busy P's, block. // If number of spinning M's >= number of busy P's, block.
...@@ -1291,7 +1424,7 @@ stop: ...@@ -1291,7 +1424,7 @@ stop:
if(p) { if(p) {
acquirep(p); acquirep(p);
injectglist(gp->schedlink); injectglist(gp->schedlink);
gp->status = Grunnable; runtime·casgstatus(gp, Gwaiting, Grunnable);
return gp; return gp;
} }
injectglist(gp); injectglist(gp);
...@@ -1334,7 +1467,7 @@ injectglist(G *glist) ...@@ -1334,7 +1467,7 @@ injectglist(G *glist)
for(n = 0; glist; n++) { for(n = 0; glist; n++) {
gp = glist; gp = glist;
glist = gp->schedlink; glist = gp->schedlink;
gp->status = Grunnable; runtime·casgstatus(gp, Gwaiting, Grunnable);
globrunqput(gp); globrunqput(gp);
} }
runtime·unlock(&runtime·sched.lock); runtime·unlock(&runtime·sched.lock);
...@@ -1420,8 +1553,6 @@ dropg(void) ...@@ -1420,8 +1553,6 @@ dropg(void)
void void
runtime·park(bool(*unlockf)(G*, void*), void *lock, String reason) runtime·park(bool(*unlockf)(G*, void*), void *lock, String reason)
{ {
if(g->status != Grunning)
runtime·throw("bad g status");
g->m->waitlock = lock; g->m->waitlock = lock;
g->m->waitunlockf = unlockf; g->m->waitunlockf = unlockf;
g->waitreason = reason; g->waitreason = reason;
...@@ -1450,7 +1581,7 @@ runtime·park_m(G *gp) ...@@ -1450,7 +1581,7 @@ runtime·park_m(G *gp)
{ {
bool ok; bool ok;
gp->status = Gwaiting; runtime·casgstatus(gp, Grunning, Gwaiting);
dropg(); dropg();
if(g->m->waitunlockf) { if(g->m->waitunlockf) {
...@@ -1458,7 +1589,7 @@ runtime·park_m(G *gp) ...@@ -1458,7 +1589,7 @@ runtime·park_m(G *gp)
g->m->waitunlockf = nil; g->m->waitunlockf = nil;
g->m->waitlock = nil; g->m->waitlock = nil;
if(!ok) { if(!ok) {
gp->status = Grunnable; runtime·casgstatus(gp, Gwaiting, Grunnable);
execute(gp); // Schedule it back, never returns. execute(gp); // Schedule it back, never returns.
} }
} }
...@@ -1477,9 +1608,14 @@ runtime·gosched(void) ...@@ -1477,9 +1608,14 @@ runtime·gosched(void)
void void
runtime·gosched_m(G *gp) runtime·gosched_m(G *gp)
{ {
if(gp->status != Grunning) uint32 status;
status = runtime·readgstatus(gp);
if ((status&~Gscan) != Grunning){
dumpgstatus(gp);
runtime·throw("bad g status"); runtime·throw("bad g status");
gp->status = Grunnable; }
runtime·casgstatus(gp, Grunning, Grunnable);
dropg(); dropg();
runtime·lock(&runtime·sched.lock); runtime·lock(&runtime·sched.lock);
globrunqput(gp); globrunqput(gp);
...@@ -1496,8 +1632,6 @@ runtime·gosched_m(G *gp) ...@@ -1496,8 +1632,6 @@ runtime·gosched_m(G *gp)
void void
runtime·goexit(void) runtime·goexit(void)
{ {
if(g->status != Grunning)
runtime·throw("bad g status");
if(raceenabled) if(raceenabled)
runtime·racegoend(); runtime·racegoend();
runtime·mcall(goexit0); runtime·mcall(goexit0);
...@@ -1507,7 +1641,7 @@ runtime·goexit(void) ...@@ -1507,7 +1641,7 @@ runtime·goexit(void)
static void static void
goexit0(G *gp) goexit0(G *gp)
{ {
gp->status = Gdead; runtime·casgstatus(gp, Grunning, Gdead);
gp->m = nil; gp->m = nil;
gp->lockedm = nil; gp->lockedm = nil;
g->m->lockedg = nil; g->m->lockedg = nil;
...@@ -1566,7 +1700,7 @@ void ...@@ -1566,7 +1700,7 @@ void
g->syscallpc = g->sched.pc; g->syscallpc = g->sched.pc;
g->syscallstack = g->stackbase; g->syscallstack = g->stackbase;
g->syscallguard = g->stackguard; g->syscallguard = g->stackguard;
g->status = Gsyscall; runtime·casgstatus(g, Grunning, Gsyscall);
if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) { if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) {
// runtime·printf("entersyscall inconsistent %p [%p,%p]\n", // runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
// g->syscallsp, g->syscallguard-StackGuard, g->syscallstack); // g->syscallsp, g->syscallguard-StackGuard, g->syscallstack);
...@@ -1618,7 +1752,7 @@ void ...@@ -1618,7 +1752,7 @@ void
g->syscallpc = g->sched.pc; g->syscallpc = g->sched.pc;
g->syscallstack = g->stackbase; g->syscallstack = g->stackbase;
g->syscallguard = g->stackguard; g->syscallguard = g->stackguard;
g->status = Gsyscall; runtime·casgstatus(g, Grunning, Gsyscall);
if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) { if(g->syscallsp < g->syscallguard-StackGuard || g->syscallstack < g->syscallsp) {
// runtime·printf("entersyscall inconsistent %p [%p,%p]\n", // runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
// g->syscallsp, g->syscallguard-StackGuard, g->syscallstack); // g->syscallsp, g->syscallguard-StackGuard, g->syscallstack);
...@@ -1650,7 +1784,7 @@ runtime·entersyscallblock_m(void) ...@@ -1650,7 +1784,7 @@ runtime·entersyscallblock_m(void)
gp->syscallpc = gp->sched.pc; gp->syscallpc = gp->sched.pc;
gp->syscallstack = gp->stackbase; gp->syscallstack = gp->stackbase;
gp->syscallguard = gp->stackguard; gp->syscallguard = gp->stackguard;
gp->status = Gsyscall; runtime·casgstatus(gp, Grunning, Gsyscall);
if(gp->syscallsp < gp->syscallguard-StackGuard || gp->syscallstack < gp->syscallsp) { if(gp->syscallsp < gp->syscallguard-StackGuard || gp->syscallstack < gp->syscallsp) {
// runtime·printf("entersyscall inconsistent %p [%p,%p]\n", // runtime·printf("entersyscall inconsistent %p [%p,%p]\n",
// gp->syscallsp, gp->syscallguard-StackGuard, gp->syscallstack); // gp->syscallsp, gp->syscallguard-StackGuard, gp->syscallstack);
...@@ -1674,7 +1808,9 @@ runtime·exitsyscall(void) ...@@ -1674,7 +1808,9 @@ runtime·exitsyscall(void)
if(exitsyscallfast()) { if(exitsyscallfast()) {
// There's a cpu for us, so we can run. // There's a cpu for us, so we can run.
g->m->p->syscalltick++; g->m->p->syscalltick++;
g->status = Grunning; // We need to cas the status and scan before resuming...
runtime·casgstatus(g, Gsyscall, Grunning);
// Garbage collector isn't running (since we are), // Garbage collector isn't running (since we are),
// so okay to clear gcstack and gcsp. // so okay to clear gcstack and gcsp.
g->syscallstack = (uintptr)nil; g->syscallstack = (uintptr)nil;
...@@ -1750,7 +1886,7 @@ exitsyscall0(G *gp) ...@@ -1750,7 +1886,7 @@ exitsyscall0(G *gp)
{ {
P *p; P *p;
gp->status = Grunnable; runtime·casgstatus(gp, Gsyscall, Grunnable);
dropg(); dropg();
runtime·lock(&runtime·sched.lock); runtime·lock(&runtime·sched.lock);
p = pidleget(); p = pidleget();
...@@ -1919,7 +2055,6 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp ...@@ -1919,7 +2055,6 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
P *p; P *p;
int32 siz; int32 siz;
//runtime·printf("newproc1 %p %p narg=%d nret=%d\n", fn->fn, argp, narg, nret);
if(fn == nil) { if(fn == nil) {
g->m->throwing = -1; // do not dump full stacks g->m->throwing = -1; // do not dump full stacks
runtime·throw("go of nil func value"); runtime·throw("go of nil func value");
...@@ -1941,9 +2076,13 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp ...@@ -1941,9 +2076,13 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
runtime·throw("invalid stack in newg"); runtime·throw("invalid stack in newg");
} else { } else {
newg = runtime·malg(StackMin); newg = runtime·malg(StackMin);
allgadd(newg); runtime·casgstatus(newg, Gidle, Gdead);
allgadd(newg); // publishes with a g->status of Gdead so GC scanner doesn't look at uninitialized stack.
} }
if(runtime·readgstatus(newg) != Gdead)
runtime·throw("newproc1: new g is not Gdead");
sp = (byte*)newg->stackbase; sp = (byte*)newg->stackbase;
sp -= siz; sp -= siz;
runtime·memmove(sp, argp, narg); runtime·memmove(sp, argp, narg);
...@@ -1959,7 +2098,8 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp ...@@ -1959,7 +2098,8 @@ runtime·newproc1(FuncVal *fn, byte *argp, int32 narg, int32 nret, void *callerp
newg->sched.g = newg; newg->sched.g = newg;
runtime·gostartcallfn(&newg->sched, fn); runtime·gostartcallfn(&newg->sched, fn);
newg->gopc = (uintptr)callerpc; newg->gopc = (uintptr)callerpc;
newg->status = Grunnable; runtime·casgstatus(newg, Gdead, Grunnable);
if(p->goidcache == p->goidcacheend) { if(p->goidcache == p->goidcacheend) {
// Sched.goidgen is the last allocated id, // Sched.goidgen is the last allocated id,
// this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch]. // this batch must be [sched.goidgen+1, sched.goidgen+GoidCacheBatch].
...@@ -1988,6 +2128,9 @@ allgadd(G *gp) ...@@ -1988,6 +2128,9 @@ allgadd(G *gp)
G **new; G **new;
uintptr cap; uintptr cap;
if (runtime·readgstatus(gp) == Gidle)
runtime·throw("allgadd: bad status Gidle");
runtime·lock(&allglock); runtime·lock(&allglock);
if(runtime·allglen >= allgcap) { if(runtime·allglen >= allgcap) {
cap = 4096/sizeof(new[0]); cap = 4096/sizeof(new[0]);
...@@ -2013,6 +2156,9 @@ gfput(P *p, G *gp) ...@@ -2013,6 +2156,9 @@ gfput(P *p, G *gp)
uintptr stksize; uintptr stksize;
Stktop *top; Stktop *top;
if (runtime·readgstatus(gp) != Gdead)
runtime·throw("gfput: bad status (not Gdead)");
if(gp->stackguard - StackGuard != gp->stack0) if(gp->stackguard - StackGuard != gp->stack0)
runtime·throw("invalid stack in gfput"); runtime·throw("invalid stack in gfput");
stksize = gp->stackbase + sizeof(Stktop) - gp->stack0; stksize = gp->stackbase + sizeof(Stktop) - gp->stack0;
...@@ -2607,13 +2753,18 @@ checkdead(void) ...@@ -2607,13 +2753,18 @@ checkdead(void)
gp = runtime·allg[i]; gp = runtime·allg[i];
if(gp->issystem) if(gp->issystem)
continue; continue;
s = gp->status; s = runtime·readgstatus(gp);
if(s == Gwaiting) switch(s&~Gscan) {
case Gwaiting:
grunning++; grunning++;
else if(s == Grunnable || s == Grunning || s == Gsyscall) { break;
case Grunnable:
case Grunning:
case Gsyscall:
runtime·unlock(&allglock); runtime·unlock(&allglock);
runtime·printf("runtime: checkdead: find g %D in status %d\n", gp->goid, s); runtime·printf("runtime: checkdead: find g %D in status %d\n", gp->goid, s);
runtime·throw("checkdead: runnable g"); runtime·throw("checkdead: runnable g");
break;
} }
} }
runtime·unlock(&allglock); runtime·unlock(&allglock);
...@@ -2837,6 +2988,9 @@ preemptall(void) ...@@ -2837,6 +2988,9 @@ preemptall(void)
// simultaneously executing runtime·newstack. // simultaneously executing runtime·newstack.
// No lock needs to be held. // No lock needs to be held.
// Returns true if preemption request was issued. // Returns true if preemption request was issued.
// The actual preemption will happen at some point in the future
// and will be indicated by the gp->status no longer being
// Grunning
static bool static bool
preemptone(P *p) preemptone(P *p)
{ {
...@@ -2850,6 +3004,10 @@ preemptone(P *p) ...@@ -2850,6 +3004,10 @@ preemptone(P *p)
if(gp == nil || gp == mp->g0) if(gp == nil || gp == mp->g0)
return false; return false;
gp->preempt = true; gp->preempt = true;
// Every call in a go routine checks for stack overflow by
// comparing the current stack pointer to gp->stackguard0.
// Setting gp->stackguard0 to StackPreempt folds
// preemption into the normal stack overflow check.
gp->stackguard0 = StackPreempt; gp->stackguard0 = StackPreempt;
return true; return true;
} }
...@@ -2935,7 +3093,7 @@ runtime·schedtrace(bool detailed) ...@@ -2935,7 +3093,7 @@ runtime·schedtrace(bool detailed)
mp = gp->m; mp = gp->m;
lockedm = gp->lockedm; lockedm = gp->lockedm;
runtime·printf(" G%D: status=%d(%S) m=%d lockedm=%d\n", runtime·printf(" G%D: status=%d(%S) m=%d lockedm=%d\n",
gp->goid, gp->status, gp->waitreason, mp ? mp->id : -1, gp->goid, runtime·readgstatus(gp), gp->waitreason, mp ? mp->id : -1,
lockedm ? lockedm->id : -1); lockedm ? lockedm->id : -1);
} }
runtime·unlock(&allglock); runtime·unlock(&allglock);
......
...@@ -6,6 +6,9 @@ package runtime ...@@ -6,6 +6,9 @@ package runtime
import "unsafe" import "unsafe"
// This is not mechanically generated
// so be very careful and refer to runtime.h
// for the definitive enum.
const ( const (
gStatusidle = iota gStatusidle = iota
gStatusRunnable gStatusRunnable
...@@ -14,6 +17,14 @@ const ( ...@@ -14,6 +17,14 @@ const (
gStatusWaiting gStatusWaiting
gStatusMoribundUnused gStatusMoribundUnused
gStatusDead gStatusDead
gStatusEnqueue
gStatusCopystack
gStatusScan = 0x1000
gStatusScanRunnable = gStatusScan + gStatusRunnable
gStatusScanRunning = gStatusScan + gStatusRunning
gStatusScanSyscall = gStatusScan + gStatusSyscall
gStatusScanWaiting = gStatusScan + gStatusWaiting
gStatusScanEnqueue = gStatusScan + gStatusEnqueue
) )
var parkunlock_c byte var parkunlock_c byte
...@@ -24,12 +35,18 @@ func Gosched() { ...@@ -24,12 +35,18 @@ func Gosched() {
mcall(&gosched_m) mcall(&gosched_m)
} }
func readgStatus(gp *g) uint32 {
//return atomic.LoadUint32(&gp.atomicstatus) // TODO: add bootstrap code to provide.
return gp.atomicstatus
}
// Puts the current goroutine into a waiting state and calls unlockf. // Puts the current goroutine into a waiting state and calls unlockf.
// If unlockf returns false, the goroutine is resumed. // If unlockf returns false, the goroutine is resumed.
func gopark(unlockf unsafe.Pointer, lock unsafe.Pointer, reason string) { func gopark(unlockf unsafe.Pointer, lock unsafe.Pointer, reason string) {
mp := acquirem() mp := acquirem()
gp := mp.curg gp := mp.curg
if gp.status != gStatusRunning { status := readgStatus(gp)
if status != gStatusRunning && status != gStatusScanRunning {
gothrow("gopark: bad g status") gothrow("gopark: bad g status")
} }
mp.waitlock = lock mp.waitlock = lock
......
...@@ -126,13 +126,25 @@ enum ...@@ -126,13 +126,25 @@ enum
// If you add to this list, add to the list // If you add to this list, add to the list
// of "okay during garbage collection" status // of "okay during garbage collection" status
// in mgc0.c too. // in mgc0.c too.
Gidle, Gidle, // 0
Grunnable, Grunnable, // 1 runnable and on a run queue
Grunning, Grunning, // 2
Gsyscall, Gsyscall, // 3
Gwaiting, Gwaiting, // 4
Gmoribund_unused, // currently unused, but hardcoded in gdb scripts Gmoribund_unused, // 5 currently unused, but hardcoded in gdb scripts
Gdead, Gdead, // 6
Genqueue, // 7 Only the Gscanenqueue is used.
Gcopystack, // 8 in this state when newstack is moving the stack
// the following encode that the GC is scanning the stack and what to do when it is done
Gscan = 0x1000, // atomicstatus&~Gscan = the non-scan state,
// Gscanidle = Gscan + Gidle, // Not used. Gidle only used with newly malloced gs
Gscanrunnable = Gscan + Grunnable, // 0x1001 When scanning complets make Grunnable (it is already on run queue)
Gscanrunning = Gscan + Grunning, // 0x1002 Used to tell preemption newstack routine to scan preempted stack.
Gscansyscall = Gscan + Gsyscall, // 0x1003 When scanning completes make is Gsyscall
Gscanwaiting = Gscan + Gwaiting, // 0x1004 When scanning completes make it Gwaiting
// Gscanmoribund_unused, // not possible
// Gscandead, // not possible
Gscanenqueue = Gscan + Genqueue, // When scanning completes make it Grunnable and put on runqueue
}; };
enum enum
{ {
...@@ -276,7 +288,7 @@ struct G ...@@ -276,7 +288,7 @@ struct G
uintptr stack0; uintptr stack0;
uintptr stacksize; uintptr stacksize;
void* param; // passed parameter on wakeup void* param; // passed parameter on wakeup
int16 status; uint32 atomicstatus;
int64 goid; int64 goid;
int64 waitsince; // approx time when the G become blocked int64 waitsince; // approx time when the G become blocked
String waitreason; // if status==Gwaiting String waitreason; // if status==Gwaiting
...@@ -285,6 +297,8 @@ struct G ...@@ -285,6 +297,8 @@ struct G
bool issystem; // do not output in stack dump, ignore in deadlock detector bool issystem; // do not output in stack dump, ignore in deadlock detector
bool preempt; // preemption signal, duplicates stackguard0 = StackPreempt bool preempt; // preemption signal, duplicates stackguard0 = StackPreempt
bool paniconfault; // panic (instead of crash) on unexpected fault address bool paniconfault; // panic (instead of crash) on unexpected fault address
bool preemptscan; // preempted g does scan for GC
bool scancheck; // debug: cleared at begining of scan cycle, set by scan, tested at end of cycle
int8 raceignore; // ignore race detection events int8 raceignore; // ignore race detection events
M* m; // for debuggers, but offset not hard-coded M* m; // for debuggers, but offset not hard-coded
M* lockedm; M* lockedm;
...@@ -681,6 +695,9 @@ void runtime·algslicecopy(uintptr, void*, void*); ...@@ -681,6 +695,9 @@ void runtime·algslicecopy(uintptr, void*, void*);
void runtime·intercopy(uintptr, void*, void*); void runtime·intercopy(uintptr, void*, void*);
void runtime·nilintercopy(uintptr, void*, void*); void runtime·nilintercopy(uintptr, void*, void*);
uint32 runtime·readgstatus(G *gp);
void runtime·casgstatus(G*, uint32, uint32);
/* /*
* deferred subroutine calls * deferred subroutine calls
*/ */
......
...@@ -337,14 +337,19 @@ runtime·oldstack(void) ...@@ -337,14 +337,19 @@ runtime·oldstack(void)
top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, (uintptr)g->m->cret, (uintptr)argsize); top->gobuf.pc, top->gobuf.sp, top->gobuf.lr, (uintptr)g->m->cret, (uintptr)argsize);
} }
// gp->status is usually Grunning, but it could be Gsyscall if a stack overflow
// happens during a function call inside entersyscall.
oldstatus = gp->status;
gp->sched = top->gobuf; gp->sched = top->gobuf;
gp->sched.ret = g->m->cret; gp->sched.ret = g->m->cret;
g->m->cret = 0; // drop reference g->m->cret = 0; // drop reference
gp->status = Gwaiting; // gp->status is usually Grunning, but it could be Gsyscall if a stack overflow
// happens during a function call inside entersyscall.
oldstatus = runtime·readgstatus(gp);
oldstatus &= ~Gscan;
if(oldstatus != Grunning && oldstatus != Gsyscall) {
runtime·printf("runtime: oldstack status=%d\n", oldstatus);
runtime·throw("oldstack");
}
runtime·casgstatus(gp, oldstatus, Gcopystack);
gp->waitreason = runtime·gostringnocopy((byte*)"stack unsplit"); gp->waitreason = runtime·gostringnocopy((byte*)"stack unsplit");
if(argsize > 0) { if(argsize > 0) {
...@@ -363,8 +368,7 @@ runtime·oldstack(void) ...@@ -363,8 +368,7 @@ runtime·oldstack(void)
gp->stackguard0 = gp->stackguard; gp->stackguard0 = gp->stackguard;
gp->panicwrap = top->panicwrap; gp->panicwrap = top->panicwrap;
runtime·stackfree(gp, old, top); runtime·stackfree(gp, old, top);
runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Grunning or Gsyscall
gp->status = oldstatus;
runtime·gogo(&gp->sched); runtime·gogo(&gp->sched);
} }
...@@ -768,6 +772,7 @@ copystack(G *gp, uintptr nframes, uintptr newsize) ...@@ -768,6 +772,7 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
uintptr oldsize, used; uintptr oldsize, used;
AdjustInfo adjinfo; AdjustInfo adjinfo;
Stktop *oldtop, *newtop; Stktop *oldtop, *newtop;
uint32 oldstatus;
if(gp->syscallstack != 0) if(gp->syscallstack != 0)
runtime·throw("can't handle stack copy in syscall yet"); runtime·throw("can't handle stack copy in syscall yet");
...@@ -801,7 +806,12 @@ copystack(G *gp, uintptr nframes, uintptr newsize) ...@@ -801,7 +806,12 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
// copy the stack (including Stktop) to the new location // copy the stack (including Stktop) to the new location
runtime·memmove(newbase - used, oldbase - used, used); runtime·memmove(newbase - used, oldbase - used, used);
oldstatus = runtime·readgstatus(gp);
oldstatus &= ~Gscan;
if (oldstatus == Gwaiting || oldstatus == Grunnable)
runtime·casgstatus(gp, oldstatus, Gcopystack); // oldstatus is Gwaiting or Grunnable
else
runtime·throw("copystack: bad status, not Gwaiting or Grunnable");
// Swap out old stack for new one // Swap out old stack for new one
gp->stackbase = (uintptr)newtop; gp->stackbase = (uintptr)newtop;
gp->stackguard = (uintptr)newstk + StackGuard; gp->stackguard = (uintptr)newstk + StackGuard;
...@@ -810,6 +820,8 @@ copystack(G *gp, uintptr nframes, uintptr newsize) ...@@ -810,6 +820,8 @@ copystack(G *gp, uintptr nframes, uintptr newsize)
gp->stack0 = (uintptr)newstk; gp->stack0 = (uintptr)newstk;
gp->sched.sp = (uintptr)(newbase - used); gp->sched.sp = (uintptr)(newbase - used);
runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Gwaiting or Grunnable
// free old stack // free old stack
runtime·stackfree(gp, oldstk, oldtop); runtime·stackfree(gp, oldstk, oldtop);
} }
...@@ -831,6 +843,9 @@ runtime·round2(int32 x) ...@@ -831,6 +843,9 @@ runtime·round2(int32 x)
// m->moreframesize bytes, copy m->moreargsize bytes to the new frame, // m->moreframesize bytes, copy m->moreargsize bytes to the new frame,
// and then act as though runtime·lessstack called the function at // and then act as though runtime·lessstack called the function at
// m->morepc. // m->morepc.
//
// g->atomicstatus will be Grunning, Gsyscall or Gscanrunning, Gscansyscall upon entry.
// If the GC is trying to stop this g then it will set preemptscan to true.
void void
runtime·newstack(void) runtime·newstack(void)
{ {
...@@ -853,11 +868,13 @@ runtime·newstack(void) ...@@ -853,11 +868,13 @@ runtime·newstack(void)
runtime·throw("runtime: wrong goroutine in newstack"); runtime·throw("runtime: wrong goroutine in newstack");
} }
// The goroutine must be executing in order to call newstack, so the possible states are
// Grunning and Gsyscall (and, due to GC, also Gscanrunning and Gscansyscall).
// gp->status is usually Grunning, but it could be Gsyscall if a stack overflow // gp->status is usually Grunning, but it could be Gsyscall if a stack overflow
// happens during a function call inside entersyscall. // happens during a function call inside entersyscall.
gp = g->m->curg; gp = g->m->curg;
oldstatus = gp->status; oldstatus = runtime·readgstatus(gp) & ~Gscan;
framesize = g->m->moreframesize; framesize = g->m->moreframesize;
argsize = g->m->moreargsize; argsize = g->m->moreargsize;
moreargp = g->m->moreargp; moreargp = g->m->moreargp;
...@@ -866,7 +883,8 @@ runtime·newstack(void) ...@@ -866,7 +883,8 @@ runtime·newstack(void)
g->m->morebuf.pc = (uintptr)nil; g->m->morebuf.pc = (uintptr)nil;
g->m->morebuf.lr = (uintptr)nil; g->m->morebuf.lr = (uintptr)nil;
g->m->morebuf.sp = (uintptr)nil; g->m->morebuf.sp = (uintptr)nil;
gp->status = Gwaiting;
runtime·casgstatus(gp, oldstatus, Gwaiting); // oldstatus is not in a Gscan status
gp->waitreason = runtime·gostringnocopy((byte*)"stack growth"); gp->waitreason = runtime·gostringnocopy((byte*)"stack growth");
newstackcall = framesize==1; newstackcall = framesize==1;
if(newstackcall) if(newstackcall)
...@@ -892,6 +910,7 @@ runtime·newstack(void) ...@@ -892,6 +910,7 @@ runtime·newstack(void)
gp->sched.pc, gp->sched.sp, gp->sched.lr, gp->sched.ctxt); gp->sched.pc, gp->sched.sp, gp->sched.lr, gp->sched.ctxt);
} }
if(sp < gp->stackguard - StackGuard) { if(sp < gp->stackguard - StackGuard) {
runtime·printf("runtime: gp=%p, gp->status=%d, oldstatus=%d\n ", (void*)gp, runtime·readgstatus(gp), oldstatus);
runtime·printf("runtime: split stack overflow: %p < %p\n", sp, gp->stackguard - StackGuard); runtime·printf("runtime: split stack overflow: %p < %p\n", sp, gp->stackguard - StackGuard);
runtime·throw("runtime: split stack overflow"); runtime·throw("runtime: split stack overflow");
} }
...@@ -908,17 +927,18 @@ runtime·newstack(void) ...@@ -908,17 +927,18 @@ runtime·newstack(void)
runtime·throw("runtime: g is running but p is not"); runtime·throw("runtime: g is running but p is not");
if(oldstatus == Gsyscall && g->m->locks == 0) if(oldstatus == Gsyscall && g->m->locks == 0)
runtime·throw("runtime: stack growth during syscall"); runtime·throw("runtime: stack growth during syscall");
// Be conservative about where we preempt. // Be conservative about where we preempt.
// We are interested in preempting user Go code, not runtime code. // We are interested in preempting user Go code, not runtime code.
if(oldstatus != Grunning || g->m->locks || g->m->mallocing || g->m->gcing || g->m->p->status != Prunning) { if(oldstatus != Grunning || g->m->locks || g->m->mallocing || g->m->gcing || g->m->p->status != Prunning) {
// Let the goroutine keep running for now. // Let the goroutine keep running for now.
// gp->preempt is set, so it will be preempted next time. // gp->preempt is set, so it will be preempted next time.
gp->stackguard0 = gp->stackguard; gp->stackguard0 = gp->stackguard;
gp->status = oldstatus; runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Gsyscall or Grunning
runtime·gogo(&gp->sched); // never return runtime·gogo(&gp->sched); // never return
} }
// Act like goroutine called runtime.Gosched. // Act like goroutine called runtime.Gosched.
gp->status = oldstatus; runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Gsyscall or Grunning
runtime·gosched_m(gp); // never return runtime·gosched_m(gp); // never return
} }
...@@ -933,6 +953,8 @@ runtime·newstack(void) ...@@ -933,6 +953,8 @@ runtime·newstack(void)
oldbase = (byte*)gp->stackbase + sizeof(Stktop); oldbase = (byte*)gp->stackbase + sizeof(Stktop);
oldsize = oldbase - oldstk; oldsize = oldbase - oldstk;
newsize = oldsize * 2; newsize = oldsize * 2;
// Note that the concurrent GC might be scanning the stack as we try to replace it.
// copystack takes care of the appropriate coordination with the stack scanner.
copystack(gp, nframes, newsize); copystack(gp, nframes, newsize);
if(StackDebug >= 1) if(StackDebug >= 1)
runtime·printf("stack grow done\n"); runtime·printf("stack grow done\n");
...@@ -940,7 +962,7 @@ runtime·newstack(void) ...@@ -940,7 +962,7 @@ runtime·newstack(void)
runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize); runtime·printf("runtime: goroutine stack exceeds %D-byte limit\n", (uint64)runtime·maxstacksize);
runtime·throw("stack overflow"); runtime·throw("stack overflow");
} }
gp->status = oldstatus; runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Gsyscall or Grunning
runtime·gogo(&gp->sched); runtime·gogo(&gp->sched);
} }
// TODO: if stack is uncopyable because we're in C code, patch return value at // TODO: if stack is uncopyable because we're in C code, patch return value at
...@@ -1017,7 +1039,7 @@ runtime·newstack(void) ...@@ -1017,7 +1039,7 @@ runtime·newstack(void)
runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt); runtime·gostartcall(&label, (void(*)(void))gp->sched.pc, gp->sched.ctxt);
gp->sched.ctxt = nil; gp->sched.ctxt = nil;
} }
gp->status = oldstatus; runtime·casgstatus(gp, Gwaiting, oldstatus); // oldstatus is Grunning or Gsyscall
runtime·gogo(&label); runtime·gogo(&label);
*(int32*)345 = 123; // never return *(int32*)345 = 123; // never return
...@@ -1055,7 +1077,7 @@ runtime·shrinkstack(G *gp) ...@@ -1055,7 +1077,7 @@ runtime·shrinkstack(G *gp)
if(!runtime·copystack) if(!runtime·copystack)
return; return;
if(gp->status == Gdead) if(runtime·readgstatus(gp) == Gdead)
return; return;
if(gp->stackbase == 0) if(gp->stackbase == 0)
runtime·throw("stackbase == 0"); runtime·throw("stackbase == 0");
......
...@@ -327,7 +327,7 @@ runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp) ...@@ -327,7 +327,7 @@ runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
{ {
int32 n; int32 n;
if(gp->status == Gsyscall) { if((runtime·readgstatus(gp)&~Gscan) == Gsyscall){
// Override signal registers if blocked in system call. // Override signal registers if blocked in system call.
pc = gp->syscallpc; pc = gp->syscallpc;
sp = gp->syscallsp; sp = gp->syscallsp;
......
...@@ -402,7 +402,7 @@ runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp) ...@@ -402,7 +402,7 @@ runtime·traceback(uintptr pc, uintptr sp, uintptr lr, G *gp)
USED(lr); USED(lr);
if(gp->status == Gsyscall) { if((runtime·readgstatus(gp)&~Gscan) == Gsyscall){
// Override signal registers if blocked in system call. // Override signal registers if blocked in system call.
pc = gp->syscallpc; pc = gp->syscallpc;
sp = gp->syscallsp; sp = gp->syscallsp;
......
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