diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c index 455a39e22b..ba16f48bbc 100644 --- a/src/pkg/runtime/proc.c +++ b/src/pkg/runtime/proc.c @@ -166,6 +166,18 @@ runtime·tracebackothers(G *me) } } +// Mark this g as m's idle goroutine. +// This functionality might be used in environments where programs +// are limited to a single thread, to simulate a select-driven +// network server. It is not exposed via the standard runtime API. +void +runtime·idlegoroutine(void) +{ + if(g->idlem != nil) + runtime·throw("g is already an idle goroutine"); + g->idlem = m; +} + // Put on `g' queue. Sched must be locked. static void gput(G *g) @@ -177,6 +189,18 @@ gput(G *g) mnextg(m, g); return; } + + // If g is the idle goroutine for an m, hand it off. + if(g->idlem != nil) { + if(g->idlem->idleg != nil) { + runtime·printf("m%d idle out of sync: g%d g%d\n", + g->idlem->id, + g->idlem->idleg->goid, g->goid); + runtime·throw("runtime: double idle"); + } + g->idlem->idleg = g; + return; + } g->schedlink = nil; if(runtime·sched.ghead == nil) @@ -199,6 +223,9 @@ gget(void) if(runtime·sched.ghead == nil) runtime·sched.gtail = nil; runtime·sched.gwait--; + } else if(m->idleg != nil) { + g = m->idleg; + m->idleg = nil; } return g; } @@ -532,6 +559,7 @@ scheduler(void) gp->lockedm = nil; m->lockedg = nil; } + gp->idlem = nil; unwindstack(gp, nil); gfput(gp); if(--runtime·sched.gcount == 0) diff --git a/src/pkg/runtime/runtime.h b/src/pkg/runtime/runtime.h index 4456e9b8d4..5db86a1771 100644 --- a/src/pkg/runtime/runtime.h +++ b/src/pkg/runtime/runtime.h @@ -197,6 +197,7 @@ struct G bool ispanic; M* m; // for debuggers, but offset not hard-coded M* lockedm; + M* idlem; int32 sig; uintptr sigcode0; uintptr sigcode1; @@ -233,6 +234,7 @@ struct M uint32 machport; // Return address for Mach IPC (OS X) MCache *mcache; G* lockedg; + G* idleg; uint32 freglo[16]; // D[i] lsb and F[i] uint32 freghi[16]; // D[i] msb and F[i+16] uint32 fflag; // floating point compare flags