From c2047754c300b68c05d65faa8dc2925fe67b71b4 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Sat, 14 Jan 2017 00:05:42 +0000 Subject: libgo: update to Go 1.8 release candidate 1 Compiler changes: * Change map assignment to use mapassign and assign value directly. * Change string iteration to use decoderune, faster for ASCII strings. * Change makeslice to take int, and use makeslice64 for larger values. * Add new noverflow field to hmap struct used for maps. Unresolved problems, to be fixed later: * Commented out test in go/types/sizes_test.go that doesn't compile. * Commented out reflect.TestStructOf test for padding after zero-sized field. Reviewed-on: https://go-review.googlesource.com/35231 gotools/: Updates for Go 1.8rc1. * Makefile.am (go_cmd_go_files): Add bug.go. (s-zdefaultcc): Write defaultPkgConfig. * Makefile.in: Rebuild. From-SVN: r244456 --- libgo/go/io/io.go | 8 ++--- libgo/go/io/ioutil/ioutil.go | 9 +----- libgo/go/io/ioutil/tempfile.go | 5 ++++ libgo/go/io/ioutil/tempfile_test.go | 16 ++++++++++ libgo/go/io/multi.go | 11 ++++++- libgo/go/io/multi_test.go | 60 ++++++++++++++++++++++++++++++++++++- libgo/go/io/pipe.go | 19 ++++++++---- libgo/go/io/pipe_test.go | 12 ++++++++ 8 files changed, 120 insertions(+), 20 deletions(-) (limited to 'libgo/go/io') diff --git a/libgo/go/io/io.go b/libgo/go/io/io.go index 19d0ae5..9e4b865 100644 --- a/libgo/go/io/io.go +++ b/libgo/go/io/io.go @@ -402,11 +402,10 @@ func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) { break } } - if er == EOF { - break - } if er != nil { - err = er + if er != EOF { + err = er + } break } } @@ -421,6 +420,7 @@ func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} } // A LimitedReader reads from R but limits the amount of // data returned to just N bytes. Each call to Read // updates N to reflect the new amount remaining. +// Read returns EOF when N <= 0 or when the underlying R returns EOF. type LimitedReader struct { R Reader // underlying reader N int64 // max bytes remaining diff --git a/libgo/go/io/ioutil/ioutil.go b/libgo/go/io/ioutil/ioutil.go index 8ecbb2d..f0da616 100644 --- a/libgo/go/io/ioutil/ioutil.go +++ b/libgo/go/io/ioutil/ioutil.go @@ -88,13 +88,6 @@ func WriteFile(filename string, data []byte, perm os.FileMode) error { return err } -// byName implements sort.Interface. -type byName []os.FileInfo - -func (f byName) Len() int { return len(f) } -func (f byName) Less(i, j int) bool { return f[i].Name() < f[j].Name() } -func (f byName) Swap(i, j int) { f[i], f[j] = f[j], f[i] } - // ReadDir reads the directory named by dirname and returns // a list of directory entries sorted by filename. func ReadDir(dirname string) ([]os.FileInfo, error) { @@ -107,7 +100,7 @@ func ReadDir(dirname string) ([]os.FileInfo, error) { if err != nil { return nil, err } - sort.Sort(byName(list)) + sort.Slice(list, func(i, j int) bool { return list[i].Name() < list[j].Name() }) return list, nil } diff --git a/libgo/go/io/ioutil/tempfile.go b/libgo/go/io/ioutil/tempfile.go index 42718cc..e5e315c 100644 --- a/libgo/go/io/ioutil/tempfile.go +++ b/libgo/go/io/ioutil/tempfile.go @@ -90,6 +90,11 @@ func TempDir(dir, prefix string) (name string, err error) { } continue } + if os.IsNotExist(err) { + if _, err := os.Stat(dir); os.IsNotExist(err) { + return "", err + } + } if err == nil { name = try } diff --git a/libgo/go/io/ioutil/tempfile_test.go b/libgo/go/io/ioutil/tempfile_test.go index d2a132a..6a70aed 100644 --- a/libgo/go/io/ioutil/tempfile_test.go +++ b/libgo/go/io/ioutil/tempfile_test.go @@ -51,3 +51,19 @@ func TestTempDir(t *testing.T) { } } } + +// test that we return a nice error message if the dir argument to TempDir doesn't +// exist (or that it's empty and os.TempDir doesn't exist) +func TestTempDir_BadDir(t *testing.T) { + dir, err := TempDir("", "TestTempDir_BadDir") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + badDir := filepath.Join(dir, "not-exist") + _, err = TempDir(badDir, "foo") + if pe, ok := err.(*os.PathError); !ok || !os.IsNotExist(err) || pe.Path != badDir { + t.Errorf("TempDir error = %#v; want PathError for path %q satisifying os.IsNotExist", err, badDir) + } +} diff --git a/libgo/go/io/multi.go b/libgo/go/io/multi.go index 3a9d036..d784846 100644 --- a/libgo/go/io/multi.go +++ b/libgo/go/io/multi.go @@ -4,13 +4,19 @@ package io +type eofReader struct{} + +func (eofReader) Read([]byte) (int, error) { + return 0, EOF +} + type multiReader struct { readers []Reader } func (mr *multiReader) Read(p []byte) (n int, err error) { for len(mr.readers) > 0 { - // Optimization to flatten nested multiReaders (Issue 13558) + // Optimization to flatten nested multiReaders (Issue 13558). if len(mr.readers) == 1 { if r, ok := mr.readers[0].(*multiReader); ok { mr.readers = r.readers @@ -19,6 +25,9 @@ func (mr *multiReader) Read(p []byte) (n int, err error) { } n, err = mr.readers[0].Read(p) if err == EOF { + // Use eofReader instead of nil to avoid nil panic + // after performing flatten (Issue 18232). + mr.readers[0] = eofReader{} // permit earlier GC mr.readers = mr.readers[1:] } if n > 0 || err != EOF { diff --git a/libgo/go/io/multi_test.go b/libgo/go/io/multi_test.go index 447e7f5..70f2a48 100644 --- a/libgo/go/io/multi_test.go +++ b/libgo/go/io/multi_test.go @@ -14,6 +14,7 @@ import ( "runtime" "strings" "testing" + "time" ) func TestMultiReader(t *testing.T) { @@ -212,7 +213,7 @@ func (b byteAndEOFReader) Read(p []byte) (n int, err error) { return 1, EOF } -// In Go 1.7, this yielded bytes forever. +// This used to yield bytes forever; issue 16795. func TestMultiReaderSingleByteWithEOF(t *testing.T) { got, err := ioutil.ReadAll(LimitReader(MultiReader(byteAndEOFReader('a'), byteAndEOFReader('b')), 10)) if err != nil { @@ -235,3 +236,60 @@ func TestMultiReaderFinalEOF(t *testing.T) { t.Errorf("got %v, %v; want 1, EOF", n, err) } } + +func TestMultiReaderFreesExhaustedReaders(t *testing.T) { + if runtime.Compiler == "gccgo" { + t.Skip("skipping finalizer test on gccgo with conservative GC") + } + + var mr Reader + closed := make(chan struct{}) + { + buf1 := bytes.NewReader([]byte("foo")) + buf2 := bytes.NewReader([]byte("bar")) + mr = MultiReader(buf1, buf2) + runtime.SetFinalizer(buf1, func(*bytes.Reader) { + close(closed) + }) + } + + buf := make([]byte, 4) + if n, err := ReadFull(mr, buf); err != nil || string(buf) != "foob" { + t.Fatalf(`ReadFull = %d (%q), %v; want 3, "foo", nil`, n, buf[:n], err) + } + + runtime.GC() + select { + case <-closed: + case <-time.After(5 * time.Second): + t.Fatal("timeout waiting for collection of buf1") + } + + if n, err := ReadFull(mr, buf[:2]); err != nil || string(buf[:2]) != "ar" { + t.Fatalf(`ReadFull = %d (%q), %v; want 2, "ar", nil`, n, buf[:n], err) + } +} + +func TestInterleavedMultiReader(t *testing.T) { + r1 := strings.NewReader("123") + r2 := strings.NewReader("45678") + + mr1 := MultiReader(r1, r2) + mr2 := MultiReader(mr1) + + buf := make([]byte, 4) + + // Have mr2 use mr1's []Readers. + // Consume r1 (and clear it for GC to handle) and consume part of r2. + n, err := ReadFull(mr2, buf) + if got := string(buf[:n]); got != "1234" || err != nil { + t.Errorf(`ReadFull(mr2) = (%q, %v), want ("1234", nil)`, got, err) + } + + // Consume the rest of r2 via mr1. + // This should not panic even though mr2 cleared r1. + n, err = ReadFull(mr1, buf) + if got := string(buf[:n]); got != "5678" || err != nil { + t.Errorf(`ReadFull(mr1) = (%q, %v), want ("5678", nil)`, got, err) + } +} diff --git a/libgo/go/io/pipe.go b/libgo/go/io/pipe.go index 7e98cd2..b6e7755 100644 --- a/libgo/go/io/pipe.go +++ b/libgo/go/io/pipe.go @@ -85,6 +85,7 @@ func (p *pipe) write(b []byte) (n int, err error) { } if p.werr != nil { err = ErrClosedPipe + break } p.wwait.Wait() } @@ -148,7 +149,7 @@ type PipeWriter struct { } // Write implements the standard Write interface: -// it writes data to the pipe, blocking until readers +// it writes data to the pipe, blocking until one or more readers // have consumed all the data or the read end is closed. // If the read end is closed with an error, that err is // returned as err; otherwise err is ErrClosedPipe. @@ -175,11 +176,17 @@ func (w *PipeWriter) CloseWithError(err error) error { // Pipe creates a synchronous in-memory pipe. // It can be used to connect code expecting an io.Reader // with code expecting an io.Writer. -// Reads on one end are matched with writes on the other, -// copying data directly between the two; there is no internal buffering. -// It is safe to call Read and Write in parallel with each other or with -// Close. Close will complete once pending I/O is done. Parallel calls to -// Read, and parallel calls to Write, are also safe: +// +// Reads and Writes on the pipe are matched one to one +// except when multiple Reads are needed to consume a single Write. +// That is, each Write to the PipeWriter blocks until it has satisfied +// one or more Reads from the PipeReader that fully consume +// the written data. +// The data is copied directly from the Write to the corresponding +// Read (or Reads); there is no internal buffering. +// +// It is safe to call Read and Write in parallel with each other or with Close. +// Parallel calls to Read and parallel calls to Write are also safe: // the individual calls will be gated sequentially. func Pipe() (*PipeReader, *PipeWriter) { p := new(pipe) diff --git a/libgo/go/io/pipe_test.go b/libgo/go/io/pipe_test.go index b16e653..95930e8 100644 --- a/libgo/go/io/pipe_test.go +++ b/libgo/go/io/pipe_test.go @@ -247,6 +247,18 @@ func TestPipeWriteClose(t *testing.T) { } } +// Test close on Write side during Write. +func TestPipeWriteClose2(t *testing.T) { + c := make(chan int, 1) + _, w := Pipe() + go delayClose(t, w, c, pipeTest{}) + n, err := w.Write(make([]byte, 64)) + <-c + if n != 0 || err != ErrClosedPipe { + t.Errorf("write to closed pipe: %v, %v want %v, %v", n, err, 0, ErrClosedPipe) + } +} + func TestWriteEmpty(t *testing.T) { r, w := Pipe() go func() { -- cgit v1.1