mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
runtime: preempt goroutines for GC
The last patch for preemptive scheduler, with this change stoptheworld issues preemption requests every 100us. Update #543. R=golang-dev, daniel.morsing, rsc CC=golang-dev https://golang.org/cl/10264044
This commit is contained in:
parent
ab1270bcfc
commit
1e112cd59f
5 changed files with 71 additions and 15 deletions
|
|
@ -99,7 +99,6 @@ static void inclocked(int32);
|
||||||
static void checkdead(void);
|
static void checkdead(void);
|
||||||
static void exitsyscall0(G*);
|
static void exitsyscall0(G*);
|
||||||
static void park0(G*);
|
static void park0(G*);
|
||||||
static void gosched0(G*);
|
|
||||||
static void goexit0(G*);
|
static void goexit0(G*);
|
||||||
static void gfput(P*, G*);
|
static void gfput(P*, G*);
|
||||||
static G* gfget(P*);
|
static G* gfget(P*);
|
||||||
|
|
@ -364,6 +363,7 @@ runtime·stoptheworld(void)
|
||||||
runtime·lock(&runtime·sched);
|
runtime·lock(&runtime·sched);
|
||||||
runtime·sched.stopwait = runtime·gomaxprocs;
|
runtime·sched.stopwait = runtime·gomaxprocs;
|
||||||
runtime·atomicstore((uint32*)&runtime·gcwaiting, 1);
|
runtime·atomicstore((uint32*)&runtime·gcwaiting, 1);
|
||||||
|
preemptall();
|
||||||
// stop current P
|
// stop current P
|
||||||
m->p->status = Pgcstop;
|
m->p->status = Pgcstop;
|
||||||
runtime·sched.stopwait--;
|
runtime·sched.stopwait--;
|
||||||
|
|
@ -382,10 +382,16 @@ runtime·stoptheworld(void)
|
||||||
wait = runtime·sched.stopwait > 0;
|
wait = runtime·sched.stopwait > 0;
|
||||||
runtime·unlock(&runtime·sched);
|
runtime·unlock(&runtime·sched);
|
||||||
|
|
||||||
// wait for remaining P's to stop voluntary
|
// wait for remaining P's to stop voluntarily
|
||||||
if(wait) {
|
if(wait) {
|
||||||
runtime·notesleep(&runtime·sched.stopnote);
|
for(;;) {
|
||||||
runtime·noteclear(&runtime·sched.stopnote);
|
// wait for 100us, then try to re-preempt in case of any races
|
||||||
|
if(runtime·notetsleep(&runtime·sched.stopnote, 100*1000)) {
|
||||||
|
runtime·noteclear(&runtime·sched.stopnote);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
preemptall();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(runtime·sched.stopwait)
|
if(runtime·sched.stopwait)
|
||||||
runtime·throw("stoptheworld: not stopped");
|
runtime·throw("stoptheworld: not stopped");
|
||||||
|
|
@ -1240,12 +1246,12 @@ park0(G *gp)
|
||||||
void
|
void
|
||||||
runtime·gosched(void)
|
runtime·gosched(void)
|
||||||
{
|
{
|
||||||
runtime·mcall(gosched0);
|
runtime·mcall(runtime·gosched0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// runtime·gosched continuation on g0.
|
// runtime·gosched continuation on g0.
|
||||||
static void
|
void
|
||||||
gosched0(G *gp)
|
runtime·gosched0(G *gp)
|
||||||
{
|
{
|
||||||
gp->status = Grunnable;
|
gp->status = Grunnable;
|
||||||
gp->m = nil;
|
gp->m = nil;
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,36 @@ func TestTimerFairness2(t *testing.T) {
|
||||||
<-done
|
<-done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The function is used to test preemption at split stack checks.
|
||||||
|
// Declaring a var avoids inlining at the call site.
|
||||||
|
var preempt = func() int {
|
||||||
|
var a [128]int
|
||||||
|
sum := 0
|
||||||
|
for _, v := range a {
|
||||||
|
sum += v
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPreemptionGC(t *testing.T) {
|
||||||
|
// Test that pending GC preempts running goroutines.
|
||||||
|
const P = 5
|
||||||
|
defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(P + 1))
|
||||||
|
var stop uint32
|
||||||
|
for i := 0; i < P; i++ {
|
||||||
|
go func() {
|
||||||
|
for atomic.LoadUint32(&stop) == 0 {
|
||||||
|
preempt()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
runtime.Gosched()
|
||||||
|
runtime.GC()
|
||||||
|
}
|
||||||
|
atomic.StoreUint32(&stop, 1)
|
||||||
|
}
|
||||||
|
|
||||||
func stackGrowthRecursive(i int) {
|
func stackGrowthRecursive(i int) {
|
||||||
var pad [128]uint64
|
var pad [128]uint64
|
||||||
if i != 0 && pad[0] == 0 {
|
if i != 0 && pad[0] == 0 {
|
||||||
|
|
|
||||||
|
|
@ -809,6 +809,7 @@ void runtime·newextram(void);
|
||||||
void runtime·exit(int32);
|
void runtime·exit(int32);
|
||||||
void runtime·breakpoint(void);
|
void runtime·breakpoint(void);
|
||||||
void runtime·gosched(void);
|
void runtime·gosched(void);
|
||||||
|
void runtime·gosched0(G*);
|
||||||
void runtime·park(void(*)(Lock*), Lock*, int8*);
|
void runtime·park(void(*)(Lock*), Lock*, int8*);
|
||||||
void runtime·tsleep(int64, int8*);
|
void runtime·tsleep(int64, int8*);
|
||||||
M* runtime·newm(void);
|
M* runtime·newm(void);
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,10 @@ runtime·newstack(void)
|
||||||
gp->status = Gwaiting;
|
gp->status = Gwaiting;
|
||||||
gp->waitreason = "stack split";
|
gp->waitreason = "stack split";
|
||||||
reflectcall = framesize==1;
|
reflectcall = framesize==1;
|
||||||
|
if(reflectcall)
|
||||||
|
framesize = 0;
|
||||||
|
|
||||||
|
// For reflectcall the context already points to beginning of reflect·call.
|
||||||
if(!reflectcall)
|
if(!reflectcall)
|
||||||
runtime·rewindmorestack(&gp->sched);
|
runtime·rewindmorestack(&gp->sched);
|
||||||
|
|
||||||
|
|
@ -238,9 +241,24 @@ runtime·newstack(void)
|
||||||
runtime·throw("runtime: stack split argsize");
|
runtime·throw("runtime: stack split argsize");
|
||||||
}
|
}
|
||||||
|
|
||||||
reflectcall = framesize==1;
|
if(gp->stackguard0 == StackPreempt) {
|
||||||
if(reflectcall)
|
if(gp == m->g0)
|
||||||
framesize = 0;
|
runtime·throw("runtime: preempt g0");
|
||||||
|
if(oldstatus == Grunning && (m->p == nil || m->p->status != Prunning))
|
||||||
|
runtime·throw("runtime: g is running but p is not");
|
||||||
|
// Be conservative about where we preempt.
|
||||||
|
// We are interested in preempting user Go code, not runtime code.
|
||||||
|
if(oldstatus != Grunning || m->locks || m->mallocing || m->gcing) {
|
||||||
|
// Let the goroutine keep running for now.
|
||||||
|
// TODO(dvyukov): remember but delay the preemption.
|
||||||
|
gp->stackguard0 = gp->stackguard;
|
||||||
|
gp->status = oldstatus;
|
||||||
|
runtime·gogo(&gp->sched); // never return
|
||||||
|
}
|
||||||
|
// Act like goroutine called runtime.Gosched.
|
||||||
|
gp->status = oldstatus;
|
||||||
|
runtime·gosched0(gp); // never return
|
||||||
|
}
|
||||||
|
|
||||||
if(reflectcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
|
if(reflectcall && m->morebuf.sp - sizeof(Stktop) - argsize - 32 > gp->stackguard) {
|
||||||
// special case: called from reflect.call (framesize==1)
|
// special case: called from reflect.call (framesize==1)
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,10 @@ enum {
|
||||||
// The actual size can be smaller than this but cannot be larger.
|
// The actual size can be smaller than this but cannot be larger.
|
||||||
// Checked in proc.c's runtime.malg.
|
// Checked in proc.c's runtime.malg.
|
||||||
StackTop = 96,
|
StackTop = 96,
|
||||||
|
|
||||||
// Goroutine preemption request.
|
|
||||||
// Stored into g->stackguard0 to cause split stack check failure.
|
|
||||||
// Must be greater than any real sp.
|
|
||||||
StackPreempt = (uintptr)(intptr)0xfffffade,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Goroutine preemption request.
|
||||||
|
// Stored into g->stackguard0 to cause split stack check failure.
|
||||||
|
// Must be greater than any real sp.
|
||||||
|
// 0xfffffade in hex.
|
||||||
|
#define StackPreempt ((uintptr)-1314)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue