runtime: impose thread count limit

Actually working to stay within the limit could cause subtle deadlocks.
Crashing avoids the subtlety.

Fixes #4056.

R=golang-dev, r, dvyukov
CC=golang-dev
https://golang.org/cl/13037043
diff --git a/src/pkg/runtime/crash_test.go b/src/pkg/runtime/crash_test.go
index 7ea1b6b..e07810b 100644
--- a/src/pkg/runtime/crash_test.go
+++ b/src/pkg/runtime/crash_test.go
@@ -125,6 +125,14 @@
 	}
 }
 
+func TestThreadExhaustion(t *testing.T) {
+	output := executeTest(t, threadExhaustionSource, nil)
+	want := "runtime: program exceeds 10-thread limit\nfatal error: thread exhaustion"
+	if !strings.HasPrefix(output, want) {
+		t.Fatalf("output does not start with %q:\n%s", want, output)
+	}
+}
+
 const crashSource = `
 package main
 
@@ -243,3 +251,25 @@
 	return x[0] + f(buf[:])
 }
 `
+
+const threadExhaustionSource = `
+package main
+
+import (
+	"runtime"
+	"runtime/debug"
+)
+
+func main() {
+	debug.SetMaxThreads(10)
+	c := make(chan int)
+	for i := 0; i < 100; i++ {
+		go func() {
+			runtime.LockOSThread()
+			c <- 0
+			select{}
+		}()
+		<-c
+	}
+}
+`
diff --git a/src/pkg/runtime/debug/garbage.go b/src/pkg/runtime/debug/garbage.go
index 3658fea..8337d5d 100644
--- a/src/pkg/runtime/debug/garbage.go
+++ b/src/pkg/runtime/debug/garbage.go
@@ -25,6 +25,7 @@
 func setGCPercent(int) int
 func freeOSMemory()
 func setMaxStack(int) int
+func setMaxThreads(int) int
 
 // ReadGCStats reads statistics about garbage collection into stats.
 // The number of entries in the pause history is system-dependent;
@@ -114,3 +115,21 @@
 func SetMaxStack(bytes int) int {
 	return setMaxStack(bytes)
 }
+
+// SetMaxThreads sets the maximum number of operating system
+// threads that the Go program can use. If it attempts to use more than
+// this many, the program crashes.
+// SetMaxThreads returns the previous setting.
+// The initial setting is 10,000 threads.
+//
+// The limit controls the number of operating system threads, not the number
+// of goroutines. A Go program creates a new thread only when a goroutine
+// is ready to run but all the existing threads are blocked in system calls, cgo calls,
+// or are locked to other goroutines due to use of runtime.LockOSThread.
+//
+// SetMaxThreads is useful mainly for limiting the damage done by
+// programs that create an unbounded number of threads. The idea is
+// to take down the program before it takes down the operating system.
+func SetMaxThreads(threads int) int {
+	return setMaxThreads(threads)
+}
diff --git a/src/pkg/runtime/proc.c b/src/pkg/runtime/proc.c
index 6950f4b..dab62ad 100644
--- a/src/pkg/runtime/proc.c
+++ b/src/pkg/runtime/proc.c
@@ -32,6 +32,7 @@
 	int32	nmidle;	 // number of idle m's waiting for work
 	int32	nmidlelocked; // number of locked m's waiting for work
 	int32	mcount;	 // number of m's that have been created
+	int32	maxmcount;	// maximum number of m's allowed (or die)
 
 	P*	pidle;  // idle P's
 	uint32	npidle;
@@ -126,6 +127,8 @@
 	int32 n, procs;
 	byte *p;
 
+	runtime·sched.maxmcount = 10000;
+
 	m->nomemprof++;
 	runtime·mprofinit();
 	runtime·mallocinit();
@@ -284,6 +287,16 @@
 }
 
 static void
+checkmcount(void)
+{
+	// sched lock is held
+	if(runtime·sched.mcount > runtime·sched.maxmcount) {
+		runtime·printf("runtime: program exceeds %d-thread limit\n", runtime·sched.maxmcount);
+		runtime·throw("thread exhaustion");
+	}
+}
+
+static void
 mcommoninit(M *mp)
 {
 	// If there is no mcache runtime·callers() will crash,
@@ -295,7 +308,7 @@
 
 	runtime·lock(&runtime·sched);
 	mp->id = runtime·sched.mcount++;
-
+	checkmcount();
 	runtime·mpreinit(mp);
 
 	// Add to runtime·allm so garbage collector doesn't free m
@@ -2821,3 +2834,14 @@
 		f->entry == (uintptr)runtime·lessstack ||
 		f->entry == (uintptr)_rt0_go;
 }
+
+void
+runtimeāˆ•debug·setMaxThreads(intgo in, intgo out)
+{
+	runtime·lock(&runtime·sched);
+	out = runtime·sched.maxmcount;
+	runtime·sched.maxmcount = in;
+	checkmcount();
+	runtime·unlock(&runtime·sched);
+	FLUSH(&out);
+}