playground: patch runtime with https://golang.org/cl/105235

Add a test case (currently flaky) that checks for deterministic output
ordering.

Updates golang/go#24615.
Updates golang/go#24659.

Change-Id: I7edc5d77a890edcd684ddf2aeee4c7a7dea68af1
Reviewed-on: https://go-review.googlesource.com/106216
Reviewed-by: Andrew Bonventre <andybons@golang.org>
diff --git a/Dockerfile b/Dockerfile
index 13441c8..8c4a520 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -13,6 +13,7 @@
 
 # Fake time
 COPY enable-fake-time.patch /usr/local/playground/
+COPY strict-time.patch /usr/local/playground/
 # Fake file system
 COPY fake_fs.lst /usr/local/playground/
 
@@ -29,6 +30,7 @@
 RUN cp -R /usr/local/go $GOROOT_BOOTSTRAP
 # Apply the fake time and fake filesystem patches.
 RUN patch /usr/local/go/src/runtime/rt0_nacl_amd64p32.s /usr/local/playground/enable-fake-time.patch
+RUN patch -p1 -d /usr/local/go </usr/local/playground/strict-time.patch
 RUN cd /usr/local/go && go run misc/nacl/mkzip.go -p syscall /usr/local/playground/fake_fs.lst src/syscall/fstest_nacl.go
 # Re-build the Go toolchain.
 RUN cd /usr/local/go/src && GOOS=nacl GOARCH=amd64p32 ./make.bash --no-clean
diff --git a/sandbox.go b/sandbox.go
index 51ffea6..04b7f7f 100644
--- a/sandbox.go
+++ b/sandbox.go
@@ -615,4 +615,20 @@
 	fmt.Println("Main")
 }
 `, want: "Main"},
+
+	{prog: `
+package main
+
+import (
+	"fmt"
+	"os"
+)
+
+func main() {
+	fmt.Fprintln(os.Stdout, "A")
+	fmt.Fprintln(os.Stderr, "B")
+	fmt.Fprintln(os.Stdout, "A")
+	fmt.Fprintln(os.Stdout, "A")
+}
+`, want: "A\nB\nA\nA\n"},
 }
diff --git a/strict-time.patch b/strict-time.patch
new file mode 100644
index 0000000..86e663d
--- /dev/null
+++ b/strict-time.patch
@@ -0,0 +1,83 @@
+From 9fd794d3a2e2b02688fe3a42c4c2c6d19d8dd668 Mon Sep 17 00:00:00 2001
+From: "Bryan C. Mills" <bcmills@google.com>
+Date: Fri, 6 Apr 2018 16:19:30 -0400
+Subject: [PATCH] runtime: make Playground timestamps change when the stream fd
+ changes
+
+The process reading the output of the binary may read stderr and
+stdout separately, and may interleave reads from the two streams
+arbitrarily. Because we explicitly serialize writes on the writer
+side, we can reuse a timestamp within a single stream without losing
+information; however, if we use the same timestamp for write on both
+streams, the reader can't tell how to interleave them.
+
+This change ensures that every time we change between the two fds, we
+also bump the timestamp. That way, writes within a stream continue to
+show the same timestamp, but a sorted merge of the contents of the two
+streams always interleaves them in the correct order.
+
+This still requires a corresponding change to the Playground parser to
+actually reconstruct the correct interleaving. It currently merges the
+two streams without reordering them; it should instead buffer them
+separately and perform a sorted merge. (See
+https://golang.org/cl/105496.)
+
+Updates golang/go#24615.
+Updates golang/go#24659.
+
+Change-Id: Id789dfcc02eb4247906c9ddad38dac50cf829979
+---
+ src/runtime/os_nacl.go          |  9 +++++++++
+ src/runtime/sys_nacl_amd64p32.s | 16 ++++++++++++++++
+ 2 files changed, 25 insertions(+)
+
+diff --git a/src/runtime/os_nacl.go b/src/runtime/os_nacl.go
+index d03cb8faf2..7b8a7d548e 100644
+--- a/src/runtime/os_nacl.go
++++ b/src/runtime/os_nacl.go
+@@ -289,6 +289,15 @@ type gsignalStack struct{}
+ 
+ var writelock uint32 // test-and-set spin lock for write
+ 
++// lastfaketime stores the last faketime value written to fd 1 or 2.
++var lastfaketime int64
++
++// lastfaketimefd stores the fd to which lastfaketime was written.
++//
++// Subsequent writes to the same fd may use the same timestamp,
++// but the timestamp must increase if the fd changes.
++var lastfaketimefd int32
++
+ /*
+ An attempt at IRT. Doesn't work. See end of sys_nacl_amd64.s.
+ 
+diff --git a/src/runtime/sys_nacl_amd64p32.s b/src/runtime/sys_nacl_amd64p32.s
+index ff4c2e7bb5..4c4d509576 100644
+--- a/src/runtime/sys_nacl_amd64p32.s
++++ b/src/runtime/sys_nacl_amd64p32.s
+@@ -89,6 +89,22 @@ playback:
+ 	CMPL BX, $0
+ 	JNE playback
+ 
++	MOVQ runtime·lastfaketime(SB), CX
++	MOVL runtime·lastfaketimefd(SB), BX
++	CMPL DI, BX
++	JE samefd
++
++	// If the current fd doesn't match the fd of the previous write,
++	// ensure that the timestamp is strictly greater. That way, we can
++	// recover the original order even if we read the fds separately.
++	INCQ CX
++	MOVL DI, runtime·lastfaketimefd(SB)
++
++samefd:
++	CMPQ AX, CX
++	CMOVQLT CX, AX
++	MOVQ AX, runtime·lastfaketime(SB)
++
+ 	// Playback header: 0 0 P B <8-byte time> <4-byte data length>
+ 	MOVL $(('B'<<24) | ('P'<<16)), 0(SP)
+ 	BSWAPQ AX
+-- 
+2.17.0.484.g0c8726318c-goog
+