1
0
mirror of https://github.com/golang/go synced 2024-11-22 01:34:41 -07:00

runtime: reduce lock contention via wakeup on scheduler unlock.

R=rsc
CC=golang-dev
https://golang.org/cl/4275043
This commit is contained in:
Ian Lance Taylor 2011-03-11 18:14:45 -08:00
parent c01238a571
commit 5e963a826c

View File

@ -78,6 +78,13 @@ struct Sched {
Sched runtime·sched;
int32 gomaxprocs;
// An m which is waiting for notewakeup(&m->havenextg). This may be
// only be accessed while the scheduler lock is held. This is used to
// minimize the number of times we call notewakeup while the scheduler
// lock is held, since the m will normally move quickly to lock the
// scheduler itself, producing lock contention.
static M* mwakeup;
// Scheduling helpers. Sched must be locked.
static void gput(G*); // put/get on ghead/gtail
static G* gget(void);
@ -133,6 +140,26 @@ runtime·schedinit(void)
m->nomemprof--;
}
// Lock the scheduler.
static void
schedlock(void)
{
runtime·lock(&runtime·sched);
}
// Unlock the scheduler.
static void
schedunlock(void)
{
M *m;
m = mwakeup;
mwakeup = nil;
runtime·unlock(&runtime·sched);
if(m != nil)
runtime·notewakeup(&m->havenextg);
}
// Called after main·init_function; main·main will be called on return.
void
runtime·initdone(void)
@ -144,9 +171,9 @@ runtime·initdone(void)
// If main·init_function started other goroutines,
// kick off new ms to handle them, like ready
// would have, had it not been pre-dawn.
runtime·lock(&runtime·sched);
schedlock();
matchmg();
runtime·unlock(&runtime·sched);
schedunlock();
}
void
@ -264,9 +291,9 @@ mget(G *g)
void
runtime·ready(G *g)
{
runtime·lock(&runtime·sched);
schedlock();
readylocked(g);
runtime·unlock(&runtime·sched);
schedunlock();
}
// Mark g ready to run. Sched is already locked.
@ -317,7 +344,9 @@ mnextg(M *m, G *g)
m->nextg = g;
if(m->waitnextg) {
m->waitnextg = 0;
runtime·notewakeup(&m->havenextg);
if(mwakeup != nil)
runtime·notewakeup(&mwakeup->havenextg);
mwakeup = m;
}
}
@ -338,7 +367,7 @@ nextgandunlock(void)
if(m->nextg != nil) {
gp = m->nextg;
m->nextg = nil;
runtime·unlock(&runtime·sched);
schedunlock();
return gp;
}
@ -356,7 +385,7 @@ nextgandunlock(void)
continue;
}
runtime·sched.mcpu++; // this m will run gp
runtime·unlock(&runtime·sched);
schedunlock();
return gp;
}
// Otherwise, wait on global m queue.
@ -371,7 +400,7 @@ nextgandunlock(void)
runtime·sched.waitstop = 0;
runtime·notewakeup(&runtime·sched.stopped);
}
runtime·unlock(&runtime·sched);
schedunlock();
runtime·notesleep(&m->havenextg);
if((gp = m->nextg) == nil)
@ -385,7 +414,7 @@ nextgandunlock(void)
void
runtime·stoptheworld(void)
{
runtime·lock(&runtime·sched);
schedlock();
runtime·gcwaiting = 1;
runtime·sched.mcpumax = 1;
while(runtime·sched.mcpu > 1) {
@ -395,11 +424,11 @@ runtime·stoptheworld(void)
// so this is okay.
runtime·noteclear(&runtime·sched.stopped);
runtime·sched.waitstop = 1;
runtime·unlock(&runtime·sched);
schedunlock();
runtime·notesleep(&runtime·sched.stopped);
runtime·lock(&runtime·sched);
schedlock();
}
runtime·unlock(&runtime·sched);
schedunlock();
}
// TODO(rsc): Remove. This is only temporary,
@ -407,11 +436,11 @@ runtime·stoptheworld(void)
void
runtime·starttheworld(void)
{
runtime·lock(&runtime·sched);
schedlock();
runtime·gcwaiting = 0;
runtime·sched.mcpumax = runtime·gomaxprocs;
matchmg();
runtime·unlock(&runtime·sched);
schedunlock();
}
// Called to start an M.
@ -500,7 +529,7 @@ matchmg(void)
static void
schedule(G *gp)
{
runtime·lock(&runtime·sched);
schedlock();
if(gp != nil) {
if(runtime·sched.predawn)
runtime·throw("init rescheduling");
@ -584,7 +613,7 @@ runtime·entersyscall(void)
runtime·gosave(&g->sched);
if(runtime·sched.predawn)
return;
runtime·lock(&runtime·sched);
schedlock();
g->status = Gsyscall;
runtime·sched.mcpu--;
runtime·sched.msyscall++;
@ -594,7 +623,7 @@ runtime·entersyscall(void)
runtime·sched.waitstop = 0;
runtime·notewakeup(&runtime·sched.stopped);
}
runtime·unlock(&runtime·sched);
schedunlock();
}
// The goroutine g exited its system call.
@ -607,13 +636,13 @@ runtime·exitsyscall(void)
if(runtime·sched.predawn)
return;
runtime·lock(&runtime·sched);
schedlock();
runtime·sched.msyscall--;
runtime·sched.mcpu++;
// Fast path - if there's room for this m, we're done.
if(runtime·sched.mcpu <= runtime·sched.mcpumax) {
g->status = Grunning;
runtime·unlock(&runtime·sched);
schedunlock();
return;
}
// Tell scheduler to put g back on the run queue:
@ -621,7 +650,7 @@ runtime·exitsyscall(void)
// but keeps the garbage collector from thinking
// that g is running right now, which it's not.
g->readyonstop = 1;
runtime·unlock(&runtime·sched);
schedunlock();
// Slow path - all the cpus are taken.
// The scheduler will ready g and put this m to sleep.
@ -815,7 +844,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
if(siz > 1024)
runtime·throw("runtime.newproc: too many args");
runtime·lock(&runtime·sched);
schedlock();
if((newg = gfget()) != nil){
newg->status = Gwaiting;
@ -848,7 +877,7 @@ runtime·newproc1(byte *fn, byte *argp, int32 narg, int32 nret, void *callerpc)
newg->goid = runtime·goidgen;
newprocreadylocked(newg);
runtime·unlock(&runtime·sched);
schedunlock();
return newg;
//printf(" goid=%d\n", newg->goid);
@ -1156,7 +1185,7 @@ runtime·gomaxprocsfunc(int32 n)
{
int32 ret;
runtime·lock(&runtime·sched);
schedlock();
ret = runtime·gomaxprocs;
if (n <= 0)
n = ret;
@ -1164,7 +1193,7 @@ runtime·gomaxprocsfunc(int32 n)
runtime·sched.mcpumax = n;
// handle fewer procs?
if(runtime·sched.mcpu > runtime·sched.mcpumax) {
runtime·unlock(&runtime·sched);
schedunlock();
// just give up the cpu.
// we'll only get rescheduled once the
// number has come down.
@ -1173,7 +1202,7 @@ runtime·gomaxprocsfunc(int32 n)
}
// handle more procs
matchmg();
runtime·unlock(&runtime·sched);
schedunlock();
return ret;
}