go.talks/present: use race detector when Run is shift-clicked

R=dvyukov, r, rogpeppe
CC=golang-dev
https://golang.org/cl/8695049
diff --git a/pkg/socket/socket.go b/pkg/socket/socket.go
index 13c6e8e..49707f7 100644
--- a/pkg/socket/socket.go
+++ b/pkg/socket/socket.go
@@ -32,9 +32,9 @@
 // Handler implements a WebSocket handler for a client connection.
 var Handler = websocket.Handler(socketHandler)
 
-// Environ, if non-nil, is used to provide an environment to go command and
-// user binary invocations.
-var Environ func() []string
+// Environ provides an environment when a binary, such as the go tool, is
+// invoked.
+var Environ func() []string = os.Environ
 
 const msgLimit = 1000 // max number of messages to send per session
 
@@ -42,9 +42,15 @@
 // It is used for both sending output messages and receiving commands, as
 // distinguished by the Kind field.
 type Message struct {
-	Id   string // client-provided unique id for the process
-	Kind string // in: "run", "kill" out: "stdout", "stderr", "end"
-	Body string
+	Id      string // client-provided unique id for the process
+	Kind    string // in: "run", "kill" out: "stdout", "stderr", "end"
+	Body    string
+	Options *Options `json:",omitempty"`
+}
+
+// Options specify additional message options.
+type Options struct {
+	Race bool // use -race flag when building code (for "run" only)
 }
 
 // socketHandler handles the websocket connection for a given present session.
@@ -87,7 +93,7 @@
 			case "run":
 				proc[m.Id].Kill()
 				lOut := limiter(in, out)
-				proc[m.Id] = startProcess(m.Id, m.Body, lOut)
+				proc[m.Id] = startProcess(m.Id, m.Body, lOut, m.Options)
 			case "kill":
 				proc[m.Id].Kill()
 			}
@@ -115,13 +121,13 @@
 
 // startProcess builds and runs the given program, sending its output
 // and end event as Messages on the provided channel.
-func startProcess(id, body string, out chan<- *Message) *process {
+func startProcess(id, body string, out chan<- *Message, opt *Options) *process {
 	p := &process{
 		id:   id,
 		out:  out,
 		done: make(chan struct{}),
 	}
-	if err := p.start(body); err != nil {
+	if err := p.start(body, opt); err != nil {
 		p.end(err)
 		return nil
 	}
@@ -140,7 +146,7 @@
 
 // start builds and starts the given program, sending its output to p.out,
 // and stores the running *exec.Cmd in the run field.
-func (p *process) start(body string) error {
+func (p *process) start(body string, opt *Options) error {
 	// We "go build" and then exec the binary so that the
 	// resultant *exec.Cmd is a handle to the user's program
 	// (rather than the go tool process).
@@ -162,7 +168,16 @@
 	// build x.go, creating x
 	defer os.Remove(bin)
 	dir, file := filepath.Split(src)
-	cmd := p.cmd(dir, "go", "build", "-o", bin, file)
+	args := []string{"go", "build"}
+	if opt != nil && opt.Race {
+		p.out <- &Message{
+			Id: p.id, Kind: "stderr",
+			Body: "Running with race detector.\n",
+		}
+		args = append(args, "-race")
+	}
+	args = append(args, "-o", bin, file)
+	cmd := p.cmd(dir, args...)
 	cmd.Stdout = cmd.Stderr // send compiler output to stderr
 	if err := cmd.Run(); err != nil {
 		return err
@@ -170,6 +185,9 @@
 
 	// run x
 	cmd = p.cmd("", bin)
+	if opt != nil && opt.Race {
+		cmd.Env = append(cmd.Env, "GOMAXPROCS=2")
+	}
 	if err := cmd.Start(); err != nil {
 		return err
 	}
@@ -199,9 +217,7 @@
 func (p *process) cmd(dir string, args ...string) *exec.Cmd {
 	cmd := exec.Command(args[0], args[1:]...)
 	cmd.Dir = dir
-	if Environ != nil {
-		cmd.Env = Environ()
-	}
+	cmd.Env = Environ()
 	cmd.Stdout = &messageWriter{p.id, "stdout", p.out}
 	cmd.Stderr = &messageWriter{p.id, "stderr", p.out}
 	return cmd
diff --git a/present/js/play.js b/present/js/play.js
index 91743e3..42662f3 100644
--- a/present/js/play.js
+++ b/present/js/play.js
@@ -41,12 +41,13 @@
       }
     }
 
-    function onRun() {
+    function onRun(e) {
       onKill();
       outpre.innerHTML = "";
       output.style.display = "block";
       run.style.display = "none";
-      stopFunc = runFunc(text(code), outpre);
+      var options = {Race: e.shiftKey};
+      stopFunc = runFunc(text(code), outpre, options);
     }
 
     function onClose() {
diff --git a/present/js/socket.js b/present/js/socket.js
index b97b30c..3fc7836 100644
--- a/present/js/socket.js
+++ b/present/js/socket.js
@@ -55,10 +55,12 @@
         o.scrollTop = o.scrollHeight - o.offsetHeight;
   }
 
-  function run(body, output) {
+  function run(body, output, options) {
     var id = output.id;
     outputs[id] = output;
-    sendMessage({Id: id, Kind: "run", Body: body});
+    options = options || {};
+    options.Race = !!options.Race; // force boolean
+    sendMessage({Id: id, Kind: "run", Body: body, Options: options});
     return function() {
       sendMessage({Id: id, Kind: "kill"});
     };