Skip to content

compress/zlib: Writer appears to ignore underlying writer errors at times. #16749

Closed
@superfell

Description

@superfell

Please answer these questions before submitting your issue. Thanks!

  1. What version of Go are you using (go version)?
    go version go1.7 darwin/amd64
  2. What operating system and processor architecture are you using (go env)?
    GOARCH="amd64"
    GOBIN=""
    GOEXE=""
    GOHOSTARCH="amd64"
    GOHOSTOS="darwin"
    GOOS="darwin"
    GOPATH="/Users/simon.fell/go"
    GORACE=""
    GOROOT="/usr/local/go"
    GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
    CC="clang"
    GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -gno-record-gcc-switches -fno-common"
    CXX="clang++"
    CGO_ENABLED="1"
  3. What did you do?
    It seems like there's a condition where the zlib Writer doesn't notice that the Writer its writing to returned an error, the test below fails for me on go 1.7 on OSX, passes on go 1.6 linux and go 1.6.3 on osx. The number of writes to zlib seems to be significant in reproducing the error.

The test has a writer that returns an error for the 4th write to the writer, however neither the call the zlib.Writer.Write or to zlib.Writer.Close surface the error.

When running the test, it'll fail with
--- FAIL: Test_WriteErrorCheck (0.00s)
compress_test.go:54: Write count=1, len=2
compress_test.go:54: Write count=2, len=240
compress_test.go:54: Write count=3, len=240
compress_test.go:54: Write count=4, len=240
compress_test.go:54: Write count=5, len=240
compress_test.go:54: Write count=6, len=240
compress_test.go:54: Write count=7, len=240
compress_test.go:54: Write count=8, len=240
compress_test.go:54: Write count=9, len=240
compress_test.go:54: Write count=10, len=240
compress_test.go:54: Write count=11, len=240
compress_test.go:54: Write count=12, len=240
compress_test.go:54: Write count=13, len=240
compress_test.go:54: Write count=14, len=240
compress_test.go:54: Write count=15, len=240
compress_test.go:54: Write count=16, len=240
compress_test.go:54: Write count=17, len=149
compress_test.go:54: Write count=18, len=4
compress_test.go:54: Write count=19, len=4
compress_test.go:38: Underlying writer returned error, that wasn't surfaced to zlib caller [total num writes=2000]
compress_test.go:54: Write count=1, len=2
compress_test.go:54: Write count=2, len=240
compress_test.go:54: Write count=3, len=240
compress_test.go:54: Write count=4, len=240
compress_test.go:54: Write count=5, len=240
compress_test.go:54: Write count=6, len=240
compress_test.go:54: Write count=7, len=240
compress_test.go:54: Write count=8, len=134
compress_test.go:54: Write count=9, len=4
compress_test.go:54: Write count=10, len=4
compress_test.go:38: Underlying writer returned error, that wasn't surfaced to zlib caller [total num writes=2000]
FAIL
FAIL _/Users/simon.fell/code/zlibtest 0.008s

I thought this may of been related to the compression algo for zlib.BestSpeed, but also seem same issue with DefaultCompression as well.

  1. What did you expect to see?
    Expect to see the test pass [as it does on go 1.6]
  2. What did you see instead?
    test fails, no error is surfaces from the zlib Writer.

package main

import (
"compress/zlib"
crand "crypto/rand"
"errors"
"testing"
)

var errWriteFailed = errors.New("I/O Error")

func Test_WriteErrorCheck(t *testing.T) {
src := make([]byte, 2000)
crand.Read(src)
writes := make([][]byte, 2000)
for i := range writes {
writes[i] = src[i/2 : i/2+10]
}
checkWriteErrorSurfaced(t, zlib.BestSpeed, writes)
checkWriteErrorSurfaced(t, zlib.DefaultCompression, writes)
}

func checkWriteErrorSurfaced(t *testing.T, compLevel int, writes [][]byte) bool {
w := &errorWriter{t: t}
c, err := zlib.NewWriterLevel(w, compLevel)
if err != nil {
t.Fatalf("Unable to construct zlib.Writer: %v", err)
}
var errw error
for _, w := range writes {
_, errw = c.Write(w)
if errw != nil {
break
}
}
errc := c.Close()
if errc == nil && errw == nil {
t.Errorf("Underlying writer returned error, that wasn't surfaced to zlib caller [total num writes=%d]", len(writes))
return false
}
t.Logf("%v", errw)
t.Logf("%v", errc)
return true
}

// errorWriter is an io.Writer that returns an error for the 4th write
type errorWriter struct {
t *testing.T
writeCount int
}

func (w *errorWriter) Write(d []byte) (int, error) {
w.writeCount++
w.t.Logf("Write count=%d, len=%d", w.writeCount, len(d))
if w.writeCount == 4 {
return 0, errWriteFailed
}
return len(d), nil
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions