summaryrefslogtreecommitdiff
path: root/libgo/go/os/pipe_test.go
diff options
context:
space:
mode:
authorIan Lance Taylor <ian@gcc.gnu.org>2018-01-17 14:20:29 +0000
committerIan Lance Taylor <ian@gcc.gnu.org>2018-01-17 14:20:29 +0000
commitc6d6367f848cfd8381aba41e035c5e7e873667c5 (patch)
treea218e98243463fc27f5053b4444e2544c63cd57a /libgo/go/os/pipe_test.go
parent9bff0086915f544fa648ea81131f035cb9ce79a4 (diff)
downloadgcc-c6d6367f848cfd8381aba41e035c5e7e873667c5.tar.gz
libgo: update to Go1.10beta2 release
Reviewed-on: https://go-review.googlesource.com/87897 From-SVN: r256794
Diffstat (limited to 'libgo/go/os/pipe_test.go')
-rw-r--r--libgo/go/os/pipe_test.go85
1 files changed, 85 insertions, 0 deletions
diff --git a/libgo/go/os/pipe_test.go b/libgo/go/os/pipe_test.go
index 9d79d84575d..aad6c27f1b1 100644
--- a/libgo/go/os/pipe_test.go
+++ b/libgo/go/os/pipe_test.go
@@ -10,6 +10,7 @@ package os_test
import (
"fmt"
"internal/testenv"
+ "io"
"io/ioutil"
"os"
osexec "os/exec"
@@ -17,6 +18,7 @@ import (
"runtime"
"strconv"
"strings"
+ "sync"
"syscall"
"testing"
"time"
@@ -220,3 +222,86 @@ func TestReadNonblockingFd(t *testing.T) {
t.Errorf("child process failed: %v", err)
}
}
+
+func TestCloseWithBlockingReadByNewFile(t *testing.T) {
+ var p [2]int
+ err := syscall.Pipe(p[:])
+ if err != nil {
+ t.Fatal(err)
+ }
+ // os.NewFile returns a blocking mode file.
+ testCloseWithBlockingRead(t, os.NewFile(uintptr(p[0]), "reader"), os.NewFile(uintptr(p[1]), "writer"))
+}
+
+func TestCloseWithBlockingReadByFd(t *testing.T) {
+ r, w, err := os.Pipe()
+ if err != nil {
+ t.Fatal(err)
+ }
+ // Calling Fd will put the file into blocking mode.
+ _ = r.Fd()
+ testCloseWithBlockingRead(t, r, w)
+}
+
+// Test that we don't let a blocking read prevent a close.
+func testCloseWithBlockingRead(t *testing.T, r, w *os.File) {
+ defer r.Close()
+ defer w.Close()
+
+ c1, c2 := make(chan bool), make(chan bool)
+ var wg sync.WaitGroup
+
+ wg.Add(1)
+ go func(c chan bool) {
+ defer wg.Done()
+ // Give the other goroutine a chance to enter the Read
+ // or Write call. This is sloppy but the test will
+ // pass even if we close before the read/write.
+ time.Sleep(20 * time.Millisecond)
+
+ if err := r.Close(); err != nil {
+ t.Error(err)
+ }
+ close(c)
+ }(c1)
+
+ wg.Add(1)
+ go func(c chan bool) {
+ defer wg.Done()
+ var b [1]byte
+ _, err := r.Read(b[:])
+ close(c)
+ if err == nil {
+ t.Error("I/O on closed pipe unexpectedly succeeded")
+ }
+ if err != io.EOF {
+ t.Errorf("got %v, expected io.EOF", err)
+ }
+ }(c2)
+
+ for c1 != nil || c2 != nil {
+ select {
+ case <-c1:
+ c1 = nil
+ // r.Close has completed, but the blocking Read
+ // is hanging. Close the writer to unblock it.
+ w.Close()
+ case <-c2:
+ c2 = nil
+ case <-time.After(1 * time.Second):
+ switch {
+ case c1 != nil && c2 != nil:
+ t.Error("timed out waiting for Read and Close")
+ w.Close()
+ case c1 != nil:
+ t.Error("timed out waiting for Close")
+ case c2 != nil:
+ t.Error("timed out waiting for Read")
+ default:
+ t.Error("impossible case")
+ }
+ }
+ }
+
+ wg.Wait()
+}