mirror of
https://github.com/golang/go
synced 2024-11-22 01:34:41 -07:00
runtime: refactor goroutine blocking
The change is a preparation for the new scheduler. It introduces runtime.park() function, that will atomically unlock the mutex and park the goroutine. It will allow to remove the racy readyonstop flag that is difficult to implement w/o the global scheduler mutex. R=rsc, remyoudompheng, dave CC=golang-dev https://golang.org/cl/6501077
This commit is contained in:
parent
e4389c008a
commit
f20fd87384
@ -155,9 +155,7 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
|
|||||||
*pres = false;
|
*pres = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g->status = Gwaiting;
|
runtime·park(nil, nil, "chan send (nil chan)");
|
||||||
g->waitreason = "chan send (nil chan)";
|
|
||||||
runtime·gosched();
|
|
||||||
return; // not reached
|
return; // not reached
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -202,11 +200,8 @@ runtime·chansend(ChanType *t, Hchan *c, byte *ep, bool *pres)
|
|||||||
mysg.g = g;
|
mysg.g = g;
|
||||||
mysg.selgen = NOSELGEN;
|
mysg.selgen = NOSELGEN;
|
||||||
g->param = nil;
|
g->param = nil;
|
||||||
g->status = Gwaiting;
|
|
||||||
g->waitreason = "chan send";
|
|
||||||
enqueue(&c->sendq, &mysg);
|
enqueue(&c->sendq, &mysg);
|
||||||
runtime·unlock(c);
|
runtime·park(runtime·unlock, c, "chan send");
|
||||||
runtime·gosched();
|
|
||||||
|
|
||||||
if(g->param == nil) {
|
if(g->param == nil) {
|
||||||
runtime·lock(c);
|
runtime·lock(c);
|
||||||
@ -230,11 +225,8 @@ asynch:
|
|||||||
mysg.g = g;
|
mysg.g = g;
|
||||||
mysg.elem = nil;
|
mysg.elem = nil;
|
||||||
mysg.selgen = NOSELGEN;
|
mysg.selgen = NOSELGEN;
|
||||||
g->status = Gwaiting;
|
|
||||||
g->waitreason = "chan send";
|
|
||||||
enqueue(&c->sendq, &mysg);
|
enqueue(&c->sendq, &mysg);
|
||||||
runtime·unlock(c);
|
runtime·park(runtime·unlock, c, "chan send");
|
||||||
runtime·gosched();
|
|
||||||
|
|
||||||
runtime·lock(c);
|
runtime·lock(c);
|
||||||
goto asynch;
|
goto asynch;
|
||||||
@ -280,9 +272,7 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
|
|||||||
*selected = false;
|
*selected = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g->status = Gwaiting;
|
runtime·park(nil, nil, "chan receive (nil chan)");
|
||||||
g->waitreason = "chan receive (nil chan)";
|
|
||||||
runtime·gosched();
|
|
||||||
return; // not reached
|
return; // not reached
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -320,11 +310,8 @@ runtime·chanrecv(ChanType *t, Hchan* c, byte *ep, bool *selected, bool *receive
|
|||||||
mysg.g = g;
|
mysg.g = g;
|
||||||
mysg.selgen = NOSELGEN;
|
mysg.selgen = NOSELGEN;
|
||||||
g->param = nil;
|
g->param = nil;
|
||||||
g->status = Gwaiting;
|
|
||||||
g->waitreason = "chan receive";
|
|
||||||
enqueue(&c->recvq, &mysg);
|
enqueue(&c->recvq, &mysg);
|
||||||
runtime·unlock(c);
|
runtime·park(runtime·unlock, c, "chan receive");
|
||||||
runtime·gosched();
|
|
||||||
|
|
||||||
if(g->param == nil) {
|
if(g->param == nil) {
|
||||||
runtime·lock(c);
|
runtime·lock(c);
|
||||||
@ -352,11 +339,8 @@ asynch:
|
|||||||
mysg.g = g;
|
mysg.g = g;
|
||||||
mysg.elem = nil;
|
mysg.elem = nil;
|
||||||
mysg.selgen = NOSELGEN;
|
mysg.selgen = NOSELGEN;
|
||||||
g->status = Gwaiting;
|
|
||||||
g->waitreason = "chan receive";
|
|
||||||
enqueue(&c->recvq, &mysg);
|
enqueue(&c->recvq, &mysg);
|
||||||
runtime·unlock(c);
|
runtime·park(runtime·unlock, c, "chan receive");
|
||||||
runtime·gosched();
|
|
||||||
|
|
||||||
runtime·lock(c);
|
runtime·lock(c);
|
||||||
goto asynch;
|
goto asynch;
|
||||||
@ -774,9 +758,7 @@ selunlock(Select *sel)
|
|||||||
void
|
void
|
||||||
runtime·block(void)
|
runtime·block(void)
|
||||||
{
|
{
|
||||||
g->status = Gwaiting; // forever
|
runtime·park(nil, nil, "select (no cases)"); // forever
|
||||||
g->waitreason = "select (no cases)";
|
|
||||||
runtime·gosched();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void* selectgo(Select**);
|
static void* selectgo(Select**);
|
||||||
@ -907,10 +889,7 @@ loop:
|
|||||||
}
|
}
|
||||||
|
|
||||||
g->param = nil;
|
g->param = nil;
|
||||||
g->status = Gwaiting;
|
runtime·park((void(*)(Lock*))selunlock, (Lock*)sel, "select");
|
||||||
g->waitreason = "select";
|
|
||||||
selunlock(sel);
|
|
||||||
runtime·gosched();
|
|
||||||
|
|
||||||
sellock(sel);
|
sellock(sel);
|
||||||
sg = g->param;
|
sg = g->param;
|
||||||
|
@ -1027,9 +1027,7 @@ runfinq(void)
|
|||||||
finq = nil;
|
finq = nil;
|
||||||
if(fb == nil) {
|
if(fb == nil) {
|
||||||
fingwait = 1;
|
fingwait = 1;
|
||||||
g->status = Gwaiting;
|
runtime·park(nil, nil, "finalizer wait");
|
||||||
g->waitreason = "finalizer wait";
|
|
||||||
runtime·gosched();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for(; fb; fb=next) {
|
for(; fb; fb=next) {
|
||||||
|
@ -951,6 +951,18 @@ runtime·gosched(void)
|
|||||||
runtime·mcall(schedule);
|
runtime·mcall(schedule);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Puts the current goroutine into a waiting state and unlocks the lock.
|
||||||
|
// The goroutine can be made runnable again by calling runtime·ready(gp).
|
||||||
|
void
|
||||||
|
runtime·park(void (*unlockf)(Lock*), Lock *lock, int8 *reason)
|
||||||
|
{
|
||||||
|
g->status = Gwaiting;
|
||||||
|
g->waitreason = reason;
|
||||||
|
if(unlockf)
|
||||||
|
unlockf(lock);
|
||||||
|
runtime·gosched();
|
||||||
|
}
|
||||||
|
|
||||||
// The goroutine g is about to enter a system call.
|
// The goroutine g is about to enter a system call.
|
||||||
// Record that it's not using the cpu anymore.
|
// Record that it's not using the cpu anymore.
|
||||||
// This is called only from the go syscall library and cgocall,
|
// This is called only from the go syscall library and cgocall,
|
||||||
|
@ -614,7 +614,8 @@ uint32 runtime·fastrand1(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·tsleep(int64);
|
void runtime·park(void(*)(Lock*), Lock*, int8*);
|
||||||
|
void runtime·tsleep(int64, int8*);
|
||||||
M* runtime·newm(void);
|
M* runtime·newm(void);
|
||||||
void runtime·goexit(void);
|
void runtime·goexit(void);
|
||||||
void runtime·asmcgocall(void (*fn)(void*), void*);
|
void runtime·asmcgocall(void (*fn)(void*), void*);
|
||||||
|
@ -125,10 +125,7 @@ runtime·semacquire(uint32 volatile *addr)
|
|||||||
// Any semrelease after the cansemacquire knows we're waiting
|
// Any semrelease after the cansemacquire knows we're waiting
|
||||||
// (we set nwait above), so go to sleep.
|
// (we set nwait above), so go to sleep.
|
||||||
semqueue(root, addr, &s);
|
semqueue(root, addr, &s);
|
||||||
g->status = Gwaiting;
|
runtime·park(runtime·unlock, root, "semacquire");
|
||||||
g->waitreason = "semacquire";
|
|
||||||
runtime·unlock(root);
|
|
||||||
runtime·gosched();
|
|
||||||
if(cansemacquire(addr))
|
if(cansemacquire(addr))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -23,14 +23,14 @@ static bool deltimer(Timer*);
|
|||||||
|
|
||||||
// Sleep puts the current goroutine to sleep for at least ns nanoseconds.
|
// Sleep puts the current goroutine to sleep for at least ns nanoseconds.
|
||||||
func Sleep(ns int64) {
|
func Sleep(ns int64) {
|
||||||
g->status = Gwaiting;
|
runtime·tsleep(ns, "sleep");
|
||||||
g->waitreason = "sleep";
|
|
||||||
runtime·tsleep(ns);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// startTimer adds t to the timer heap.
|
// startTimer adds t to the timer heap.
|
||||||
func startTimer(t *Timer) {
|
func startTimer(t *Timer) {
|
||||||
|
runtime·lock(&timers);
|
||||||
addtimer(t);
|
addtimer(t);
|
||||||
|
runtime·unlock(&timers);
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopTimer removes t from the timer heap if it is there.
|
// stopTimer removes t from the timer heap if it is there.
|
||||||
@ -55,24 +55,21 @@ ready(int64 now, Eface e)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Put the current goroutine to sleep for ns nanoseconds.
|
// Put the current goroutine to sleep for ns nanoseconds.
|
||||||
// The caller must have set g->status and g->waitreason.
|
|
||||||
void
|
void
|
||||||
runtime·tsleep(int64 ns)
|
runtime·tsleep(int64 ns, int8 *reason)
|
||||||
{
|
{
|
||||||
Timer t;
|
Timer t;
|
||||||
|
|
||||||
if(ns <= 0) {
|
if(ns <= 0)
|
||||||
g->status = Grunning;
|
|
||||||
g->waitreason = nil;
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
t.when = runtime·nanotime() + ns;
|
t.when = runtime·nanotime() + ns;
|
||||||
t.period = 0;
|
t.period = 0;
|
||||||
t.f = ready;
|
t.f = ready;
|
||||||
t.arg.data = g;
|
t.arg.data = g;
|
||||||
|
runtime·lock(&timers);
|
||||||
addtimer(&t);
|
addtimer(&t);
|
||||||
runtime·gosched();
|
runtime·park(runtime·unlock, &timers, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add a timer to the heap and start or kick the timer proc
|
// Add a timer to the heap and start or kick the timer proc
|
||||||
@ -83,7 +80,6 @@ addtimer(Timer *t)
|
|||||||
int32 n;
|
int32 n;
|
||||||
Timer **nt;
|
Timer **nt;
|
||||||
|
|
||||||
runtime·lock(&timers);
|
|
||||||
if(timers.len >= timers.cap) {
|
if(timers.len >= timers.cap) {
|
||||||
// Grow slice.
|
// Grow slice.
|
||||||
n = 16;
|
n = 16;
|
||||||
@ -111,7 +107,6 @@ addtimer(Timer *t)
|
|||||||
}
|
}
|
||||||
if(timers.timerproc == nil)
|
if(timers.timerproc == nil)
|
||||||
timers.timerproc = runtime·newproc1((byte*)timerproc, nil, 0, 0, addtimer);
|
timers.timerproc = runtime·newproc1((byte*)timerproc, nil, 0, 0, addtimer);
|
||||||
runtime·unlock(&timers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete timer t from the heap.
|
// Delete timer t from the heap.
|
||||||
@ -191,10 +186,7 @@ timerproc(void)
|
|||||||
if(delta < 0) {
|
if(delta < 0) {
|
||||||
// No timers left - put goroutine to sleep.
|
// No timers left - put goroutine to sleep.
|
||||||
timers.rescheduling = true;
|
timers.rescheduling = true;
|
||||||
g->status = Gwaiting;
|
runtime·park(runtime·unlock, &timers, "timer goroutine (idle)");
|
||||||
g->waitreason = "timer goroutine (idle)";
|
|
||||||
runtime·unlock(&timers);
|
|
||||||
runtime·gosched();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// At least one timer pending. Sleep until then.
|
// At least one timer pending. Sleep until then.
|
||||||
|
Loading…
Reference in New Issue
Block a user