runtime: 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.

R=r, r2
CC=golang-dev
https://golang.org/cl/4254041
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 455a39e..ba16f48 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -166,6 +166,18 @@
 	}
 }
 
+// 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 @@
 		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 @@
 		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 @@
 				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 4456e9b..5db86a1 100644
--- a/src/pkg/runtime/runtime.h
+++ b/src/pkg/runtime/runtime.h
@@ -197,6 +197,7 @@
 	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 @@
 	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