Merge "veyron/runtimes/google/ipc: Introduce a NoRetry opt to make ipc tests run much faster."
diff --git a/lib/exec/child.go b/lib/exec/child.go
index 09b53fe..688a74b 100644
--- a/lib/exec/child.go
+++ b/lib/exec/child.go
@@ -26,6 +26,15 @@
 	// veyron framework to notify the parent that the child process has
 	// successfully started.
 	statusPipe *os.File
+
+	// statusMu protexts sentStatus and statusErr and prevents us from trying to
+	// send multiple status updates to the parent.
+	statusMu sync.Mutex
+	// sentStatus records the status that was already sent to the parent.
+	sentStatus string
+	// statusErr records the error we received, if any, when sentStatus
+	// was sent.
+	statusErr error
 }
 
 var (
@@ -57,35 +66,43 @@
 	return childHandle, childHandleErr
 }
 
-func (c *ChildHandle) writeStatus(status string) error {
-	toWrite := make([]byte, 0, len(status))
-	var buf [utf8.UTFMax]byte
-	// This replaces any invalid utf-8 bytes in the status string with the
-	// Unicode replacement character.  This ensures that we only send valid
-	// utf-8 (followed by the eofChar).
-	for _, r := range status {
-		n := utf8.EncodeRune(buf[:], r)
-		toWrite = append(toWrite, buf[:n]...)
+func (c *ChildHandle) writeStatus(status, detail string) error {
+	c.statusMu.Lock()
+	defer c.statusMu.Unlock()
+
+	if c.sentStatus == "" {
+		c.sentStatus = status
+		status = status + detail
+		toWrite := make([]byte, 0, len(status))
+		var buf [utf8.UTFMax]byte
+		// This replaces any invalid utf-8 bytes in the status string with the
+		// Unicode replacement character.  This ensures that we only send valid
+		// utf-8 (followed by the eofChar).
+		for _, r := range status {
+			n := utf8.EncodeRune(buf[:], r)
+			toWrite = append(toWrite, buf[:n]...)
+		}
+		toWrite = append(toWrite, eofChar)
+		_, c.statusErr = c.statusPipe.Write(toWrite)
+		c.statusPipe.Close()
+	} else if c.sentStatus != status {
+		return errors.New("A different status: " + c.sentStatus + " has already been sent.")
 	}
-	toWrite = append(toWrite, eofChar)
-	_, err := c.statusPipe.Write(toWrite)
-	c.statusPipe.Close()
-	return err
+	return c.statusErr
 }
 
-// TODO(caprita): There's nothing preventing SetReady and SetFailed from being
-// called multiple times (e.g. from different instances of the runtime
-// intializing themselves).  This results in errors for all but the first
-// invocation. Should we instead run these with sync.Once?
-
 // SetReady writes a 'ready' status to its parent.
+// Only one of SetReady or SetFailed can be called, attempting to send
+// both will fail.  In addition the status is only sent once to the parent
+// subsequent calls will return immediately with the same error that was
+// returned on the first call (possibly nil).
 func (c *ChildHandle) SetReady() error {
-	return c.writeStatus(readyStatus + strconv.Itoa(os.Getpid()))
+	return c.writeStatus(readyStatus, strconv.Itoa(os.Getpid()))
 }
 
 // SetFailed writes a 'failed' status to its parent.
 func (c *ChildHandle) SetFailed(oerr error) error {
-	return c.writeStatus(failedStatus + oerr.Error())
+	return c.writeStatus(failedStatus, oerr.Error())
 }
 
 // NewExtraFile creates a new file handle for the i-th file descriptor after
diff --git a/lib/exec/exec_test.go b/lib/exec/exec_test.go
index f2ca9db..44f92a5 100644
--- a/lib/exec/exec_test.go
+++ b/lib/exec/exec_test.go
@@ -536,17 +536,28 @@
 			log.Fatal(err)
 		}
 		verifyNoExecVariable()
-		if err := ch.SetFailed(fmt.Errorf("%s", strings.Join(args, " "))); err != nil {
-
+		err = ch.SetFailed(fmt.Errorf("%s", strings.Join(args, " ")))
+		if err != nil {
 			fmt.Fprintf(os.Stderr, "%s\n", err)
 		}
+		// It's fine to call SetFailed multiple times.
+		if err2 := ch.SetFailed(fmt.Errorf("dummy")); err != err2 {
+			fmt.Fprintf(os.Stderr, "Received new error got: %v, want %v\n", err2, err)
+		}
 	case "testReady":
 		ch, err := vexec.GetChildHandle()
 		if err != nil {
 			log.Fatal(err)
 		}
 		verifyNoExecVariable()
-		ch.SetReady()
+		err = ch.SetReady()
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "%s\n", err)
+		}
+		// It's fine to call SetReady multiple times.
+		if err2 := ch.SetReady(); err != err2 {
+			fmt.Fprintf(os.Stderr, "Received new error got: %v, want %v\n", err2, err)
+		}
 		fmt.Fprintf(os.Stderr, ".")
 	case "testReadySlow":
 		ch, err := vexec.GetChildHandle()
diff --git a/runtimes/google/rt/mgmt.go b/runtimes/google/rt/mgmt.go
index ccaad62..56240d6 100644
--- a/runtimes/google/rt/mgmt.go
+++ b/runtimes/google/rt/mgmt.go
@@ -59,14 +59,7 @@
 		return err
 	}
 
-	// Note(mattr): that we ignore the error here.
-	// As far as I can tell this is because the modules framework actually calls
-	// SetReady(), so we would then call it here a second time and get
-	// an error.
-	// TODO(mattr): We should change the modules framework so we can properly check
-	// errors here.
-	handle.SetReady()
-	return nil
+	return handle.SetReady()
 }
 
 func getListenSpec(handle *exec.ChildHandle) (*ipc.ListenSpec, error) {
diff --git a/services/mgmt/application/applicationd/applicationd_v23_test.go b/services/mgmt/application/applicationd/applicationd_v23_test.go
index 2f521bd..e6c6a46 100644
--- a/services/mgmt/application/applicationd/applicationd_v23_test.go
+++ b/services/mgmt/application/applicationd/applicationd_v23_test.go
@@ -84,7 +84,7 @@
 	if err != nil {
 		i.Fatal(err)
 	}
-	sigJSON, err := json.MarshalIndent(sig, "  ", "  ")
+	sigJSON, err := json.MarshalIndent(sig, "    ", "  ")
 	if err != nil {
 		i.Fatal(err)
 	}
@@ -99,8 +99,10 @@
 	wantEnvelope := `{
   "Title": "title",
   "Args": null,
-  "Binary": "foo",
-  "Signature": ` + string(sigJSON) + `,
+  "Binary": {
+    "File": "foo",
+    "Signature": ` + string(sigJSON) + `
+  },
   "Publisher": ` + string(pubJSON) + `,
   "Env": null,
   "Packages": null