Commit 6184f46e authored by Rick Hudson's avatar Rick Hudson

[dev.garbage] runtime: Concurrent scan code

Routines and logic to preform a concurrent stack scan of go-routines.
This CL excersizes most of the functionality needed. The
major exception being that it does not scan running goroutines.
After doing the scans it relies on a STW to finish the GC, including
rescanning the stacks. It is intended to achieve correctness,
performance will follow.

LGTM=rsc
R=golang-codereviews, rsc
CC=dvyukov, golang-codereviews
https://golang.org/cl/156580043
parent 62a4359e
......@@ -438,7 +438,15 @@ func gogc(force int32) {
mp = acquirem()
mp.gcing = 1
releasem(mp)
onM(stoptheworld)
onM(finishsweep_m) // finish sweep before we start concurrent scan.
onM(starttheworld)
// Do a concurrent heap scan before we stop the world.
onM(gcscan_m)
onM(stoptheworld)
if mp != acquirem() {
gothrow("gogc: rescheduled")
}
......
......@@ -343,11 +343,6 @@ struct MCache
StackFreeList stackcache[NumStackOrders];
SudoG* sudogcache;
// Cached P local buffer holding grey objects (marked by not yet scanned)
// Used by mutator for write barrier work.
// GC uses the mcache of the P it is running on for stack and global scanning
// work as well marking.
Workbuf* gcworkbuf;
// Local allocator stats, flushed during GC.
uintptr local_nlookup; // number of pointer lookups
......
......@@ -39,12 +39,12 @@ runtime·allocmcache(void)
return c;
}
// mheap.lock needs to be held to release the gcworkbuf.
static void
freemcache(MCache *c)
{
runtime·MCache_ReleaseAll(c);
runtime·stackcache_clear(c);
runtime·gcworkbuffree(c->gcworkbuf);
runtime·lock(&runtime·mheap.lock);
runtime·purgecachedstats(c);
runtime·FixAlloc_Free(&runtime·mheap.cachealloc, c);
......
This diff is collapsed.
......@@ -423,13 +423,7 @@ runtime·casgstatus(G *gp, uint32 oldval, uint32 newval)
// loop if gp->atomicstatus is in a scan state giving
// GC time to finish and change the state to oldval.
while(!runtime·cas(&gp->atomicstatus, oldval, newval)) {
// Help GC if needed.
if(gp->preemptscan && !gp->gcworkdone && (oldval == Grunning || oldval == Gsyscall)) {
gp->preemptscan = false;
g->m->ptrarg[0] = gp;
fn = helpcasgstatus;
runtime·onM(&fn);
}
}
}
......@@ -504,6 +498,13 @@ runtime·stopg(G *gp)
return false;
case Grunning:
if(runtime·gcphase == GCscan) {
gp->gcworkdone = true;
return false;
// Running routines not scanned during
// GCscan phase, we only scan non-running routines.
}
// Claim goroutine, so we aren't racing with a status
// transition away from Grunning.
if(!runtime·castogscanstatus(gp, Grunning, Gscanrunning))
......@@ -1918,6 +1919,7 @@ exitsyscallfast(void)
// Freezetheworld sets stopwait but does not retake P's.
if(runtime·sched.stopwait) {
g->m->mcache = nil;
g->m->p = nil;
return false;
}
......@@ -1930,6 +1932,7 @@ exitsyscallfast(void)
return true;
}
// Try to get any other idle P.
g->m->mcache = nil;
g->m->p = nil;
if(runtime·sched.pidle) {
fn = exitsyscallfast_pidle;
......@@ -2617,6 +2620,8 @@ runtime·setcpuprofilerate_m(void)
P *runtime·newP(void);
// Change number of processors. The world is stopped, sched is locked.
// gcworkbufs are not being modified by either the GC or
// the write barrier code.
static void
procresize(int32 new)
{
......
......@@ -649,6 +649,7 @@ struct ForceGCState
};
extern uint32 runtime·gcphase;
extern Mutex runtime·allglock;
/*
* defined macros
......@@ -677,6 +678,7 @@ enum {
uint32 runtime·readgstatus(G*);
void runtime·casgstatus(G*, uint32, uint32);
bool runtime·castogscanstatus(G*, uint32, uint32);
void runtime·quiesce(G*);
bool runtime·stopg(G*);
void runtime·restartg(G*);
......
......@@ -587,13 +587,13 @@ adjustsudogs(G *gp, AdjustInfo *adjinfo)
}
// Copies gp's stack to a new stack of a different size.
// Caller must have changed gp status to Gcopystack.
static void
copystack(G *gp, uintptr newsize)
{
Stack old, new;
uintptr used;
AdjustInfo adjinfo;
uint32 oldstatus;
bool (*cb)(Stkframe*, void*);
byte *p, *ep;
......@@ -637,20 +637,11 @@ copystack(G *gp, uintptr newsize)
}
runtime·memmove((byte*)new.hi - used, (byte*)old.hi - 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
gp->stack = new;
gp->stackguard0 = new.lo + StackGuard; // NOTE: might clobber a preempt request
gp->sched.sp = new.hi - used;
runtime·casgstatus(gp, Gcopystack, oldstatus); // oldstatus is Gwaiting or Grunnable
// free old stack
if(StackPoisonCopy) {
p = (byte*)old.lo;
......@@ -700,6 +691,7 @@ void
runtime·newstack(void)
{
int32 oldsize, newsize;
uint32 oldstatus;
uintptr sp;
G *gp;
Gobuf morebuf;
......@@ -789,12 +781,15 @@ runtime·newstack(void)
runtime·throw("stack overflow");
}
// 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.
oldstatus = runtime·readgstatus(gp);
oldstatus &= ~Gscan;
runtime·casgstatus(gp, oldstatus, Gcopystack); // oldstatus is Gwaiting or Grunnable
// The concurrent GC will not scan the stack while we are doing the copy since
// the gp is in a Gcopystack status.
copystack(gp, newsize);
if(StackDebug >= 1)
runtime·printf("stack grow done\n");
runtime·casgstatus(gp, Gwaiting, Grunning);
runtime·casgstatus(gp, Gcopystack, Grunning);
runtime·gogo(&gp->sched);
}
......@@ -825,6 +820,7 @@ void
runtime·shrinkstack(G *gp)
{
uintptr used, oldsize, newsize;
uint32 oldstatus;
if(runtime·readgstatus(gp) == Gdead) {
if(gp->stack.lo != 0) {
......@@ -858,8 +854,19 @@ runtime·shrinkstack(G *gp)
#endif
if(StackDebug > 0)
runtime·printf("shrinking stack %D->%D\n", (uint64)oldsize, (uint64)newsize);
// This is being done in a Gscan state and was initiated by the GC so no need to move to
// the Gcopystate.
// The world is stopped, so the goroutine must be Gwaiting or Grunnable,
// and what it is is not changing underfoot.
oldstatus = runtime·readgstatus(gp);
oldstatus &= ~Gscan;
if(oldstatus != Gwaiting && oldstatus != Grunnable)
runtime·throw("status is not Gwaiting or Grunnable");
runtime·casgstatus(gp, oldstatus, Gcopystack);
copystack(gp, newsize);
}
runtime·casgstatus(gp, Gcopystack, oldstatus);
}
// Do any delayed stack freeing that was queued up during GC.
void
......
......@@ -106,6 +106,8 @@ func recovery_m(*g)
func mcacheRefill_m()
func largeAlloc_m()
func gc_m()
func gcscan_m()
func finishsweep_m()
func scavenge_m()
func setFinalizer_m()
func removeFinalizer_m()
......
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