TBR: v.io/jiri: fix a race condition in runtile/sequence.go

Share a lock across all writers passed to Capture regardless
of if they are the same or not. This handles cases of the form

var out bytes.Buffer
stdout := io.MultiWriter(&out, jirix.Stdout())
stderr := io.MultiWriter(&out, jirix.Stdout())
s.Capture(stdout, stderr).Run....

where the same underlying output stream is aliased via
a wrapper.

Change-Id: Idbe010b14de992a30c35793e6c562ce26f3406a8
diff --git a/runutil/.api b/runutil/.api
index 318b8e8..d636801 100644
--- a/runutil/.api
+++ b/runutil/.api
@@ -1,4 +1,8 @@
+pkg runutil, func GetOriginalError(error) error
+pkg runutil, func IsExist(error) bool
 pkg runutil, func IsFNLHost() bool
+pkg runutil, func IsNotExist(error) bool
+pkg runutil, func IsPermission(error) bool
 pkg runutil, func LookPath(string, map[string]string) (string, error)
 pkg runutil, func NewRun(map[string]string, io.Reader, io.Writer, io.Writer, bool, bool, bool) *Run
 pkg runutil, func NewSequence(map[string]string, io.Reader, io.Writer, io.Writer, bool, bool, bool) *Sequence
@@ -44,6 +48,7 @@
 pkg runutil, method (*Sequence) Env(map[string]string) *Sequence
 pkg runutil, method (*Sequence) Error() error
 pkg runutil, method (*Sequence) FileExists(string) (bool, error)
+pkg runutil, method (*Sequence) Fprintf(io.Writer, string, ...interface{}) *Sequence
 pkg runutil, method (*Sequence) IsDir(string) (bool, error)
 pkg runutil, method (*Sequence) Last(string, ...string) error
 pkg runutil, method (*Sequence) Lstat(string) (os.FileInfo, error)
@@ -65,6 +70,7 @@
 pkg runutil, method (*Sequence) Symlink(string, string) *Sequence
 pkg runutil, method (*Sequence) TempDir(string, string) (string, error)
 pkg runutil, method (*Sequence) Timeout(time.Duration) *Sequence
+pkg runutil, method (*Sequence) Verbose(bool) *Sequence
 pkg runutil, method (*Sequence) WriteFile(string, []byte, os.FileMode) *Sequence
 pkg runutil, method (*Start) Command(string, ...string) (*exec.Cmd, error)
 pkg runutil, method (*Start) CommandWithOpts(Opts, string, ...string) (*exec.Cmd, error)
diff --git a/runutil/sequence.go b/runutil/sequence.go
index 324e8dc..48350ab 100644
--- a/runutil/sequence.go
+++ b/runutil/sequence.go
@@ -24,6 +24,8 @@
 // The first method to encounter an error short circuits any following
 // methods and the result of that first error is returned by the
 // Done method or any of the other 'terminating methods' (see below).
+// Sequence is not thread safe. It also good practice to use a new
+// instance of a Sequence in defer's.
 //
 // Unless directed to specific stdout and stderr io.Writers using Capture(),
 // the stdout and stderr output from the command is discarded, unless an error
@@ -262,17 +264,29 @@
 	}
 }
 
-type lockedWriter struct {
-	sync.Mutex
-	f io.Writer
+type sharedLockWriter struct {
+	mu *sync.Mutex
+	f  io.Writer
 }
 
-func (lw *lockedWriter) Write(d []byte) (int, error) {
-	lw.Lock()
-	defer lw.Unlock()
+func (lw *sharedLockWriter) Write(d []byte) (int, error) {
+	lw.mu.Lock()
+	defer lw.mu.Unlock()
 	return lw.f.Write(d)
 }
 
+func serializedWriters(a, b io.Writer) (io.Writer, io.Writer) {
+	mu := new(sync.Mutex)
+	var ra, rb io.Writer
+	if a != nil {
+		ra = &sharedLockWriter{mu, a}
+	}
+	if b != nil {
+		rb = &sharedLockWriter{mu, b}
+	}
+	return ra, rb
+}
+
 func (s *Sequence) initAndDefer() func() {
 	if s.stdout == nil && s.stderr == nil {
 		fout, err := ioutil.TempFile("", "seq")
@@ -312,13 +326,8 @@
 		opts.Stdin = s.stdin
 	}
 	var stdinCh, stderrCh chan error
-	stdout := s.stdout
-	stderr := s.stderr
+	stdout, stderr := serializedWriters(s.stdout, s.stderr)
 	if stdout != nil {
-		if stdout == stderr {
-			stdout = &lockedWriter{f: stdout}
-			stderr = &lockedWriter{f: stderr}
-		}
 		stdinCh = make(chan error)
 		go copy(stdout, rStdout, stdinCh)
 	} else {