gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit 1da60bb4601ddd313012d7e9bc0e5d1deba2cbc3
parent 8ef51f83cdce8c580333e02797c6e0693be5205f
Author: francoispqt <francois@parquet.ninja>
Date:   Tue,  1 May 2018 10:13:28 +0800

fix race condition in stream tests

Diffstat:
MMakefile | 2+-
Mencode_number.go | 36++++++++++++++++++++++++++++++++----
Mencode_number_test.go | 2+-
Mencode_stream.go | 2+-
Mencode_stream_test.go | 16++++++++--------
5 files changed, 43 insertions(+), 15 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,6 +1,6 @@ .PHONY: test test: - go test -run=^Test -v + go test -race -run=^Test -v .PHONY: cover cover: diff --git a/encode_number.go b/encode_number.go @@ -18,17 +18,45 @@ func (enc *Encoder) encodeInt(n int64) ([]byte, error) { } // EncodeFloat encodes a float64 to JSON -func (enc *Encoder) EncodeFloat(n float64) ([]byte, error) { +func (enc *Encoder) EncodeFloat(n float64) error { if enc.isPooled == 1 { panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) } - return enc.encodeFloat(n) + _, err := enc.encodeFloat(n) + if err != nil { + return err + } + _, err = enc.write() + if err != nil { + return err + } + return nil } // encodeFloat encodes a float64 to JSON func (enc *Encoder) encodeFloat(n float64) ([]byte, error) { - s := strconv.FormatFloat(n, 'f', -1, 64) - enc.writeString(s) + enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 64) + return enc.buf, nil +} + +// EncodeFloat encodes a float32 to JSON +func (enc *Encoder) EncodeFloat32(n float32) error { + if enc.isPooled == 1 { + panic(InvalidUsagePooledEncoderError("Invalid usage of pooled encoder")) + } + _, err := enc.encodeFloat32(n) + if err != nil { + return err + } + _, err = enc.write() + if err != nil { + return err + } + return nil +} + +func (enc *Encoder) encodeFloat32(n float32) ([]byte, error) { + enc.buf = strconv.AppendFloat(enc.buf, float64(n), 'f', -1, 32) return enc.buf, nil } diff --git a/encode_number_test.go b/encode_number_test.go @@ -126,6 +126,6 @@ func TestEncoderFloatPooledError(t *testing.T) { assert.IsType(t, InvalidUsagePooledEncoderError(""), err, "err should be of type InvalidUsagePooledEncoderError") assert.Equal(t, "Invalid usage of pooled encoder", err.(InvalidUsagePooledEncoderError).Error(), "err should be of type InvalidUsagePooledDecoderError") }() - _, _ = enc.EncodeFloat(v) + _ = enc.EncodeFloat(v) assert.True(t, false, "should not be called as it should have panicked") } diff --git a/encode_stream.go b/encode_stream.go @@ -71,9 +71,9 @@ func (s *StreamEncoder) NConsumer(n int) *StreamEncoder { // If a decoder is used after calling Release // a panic will be raised with an InvalidUsagePooledDecoderError error. func (s *StreamEncoder) Release() { + s.Encoder.isPooled = 1 select { case streamEncPool <- s: - s.Encoder.isPooled = 1 default: } } diff --git a/encode_stream_test.go b/encode_stream_test.go @@ -57,7 +57,7 @@ func TestEncodeStreamSingleConsumer(t *testing.T) { `{"testStr":"","testInt":0,"testInt64":0,"testInt32":0,"testInt16":0,"testInt8":0,"testUint64":0,"testUint32":0,"testUint16":0,"testUint8":0,"testFloat64":0,"testFloat32":0,"testBool":false} ` // create our writer - w := &TestWriter{target: 100, mux: &sync.Mutex{}} + w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) @@ -73,7 +73,7 @@ func TestEncodeStreamSingleConsumer(t *testing.T) { } func TestEncodeStreamSingleConsumerInt(t *testing.T) { // create our writer - w := &TestWriter{target: 100, mux: &sync.Mutex{}} + w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanInt(make(chan int)) @@ -86,7 +86,7 @@ func TestEncodeStreamSingleConsumerInt(t *testing.T) { } func TestEncodeStreamSingleConsumerFloat(t *testing.T) { // create our writer - w := &TestWriter{target: 100, mux: &sync.Mutex{}} + w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanFloat(make(chan float64)) @@ -99,7 +99,7 @@ func TestEncodeStreamSingleConsumerFloat(t *testing.T) { } func TestEncodeStreamSingleConsumerMarshalError(t *testing.T) { // create our writer - w := &TestWriter{target: 100, mux: &sync.Mutex{}} + w := &TestWriter{target: 100, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).LineDelimited() w.enc = enc s := StreamChanError(make(chan *testObject)) @@ -114,7 +114,7 @@ func TestEncodeStreamSingleConsumerCommaDelimited(t *testing.T) { expectedStr := `{"testStr":"","testInt":0,"testInt64":0,"testInt32":0,"testInt16":0,"testInt8":0,"testUint64":0,"testUint32":0,"testUint16":0,"testUint8":0,"testFloat64":0,"testFloat32":0,"testBool":false},` // create our writer - w := &TestWriter{target: 5000, mux: &sync.Mutex{}} + w := &TestWriter{target: 5000, mux: &sync.RWMutex{}} enc := Stream.BorrowEncoder(w).NConsumer(50).CommaDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) @@ -134,7 +134,7 @@ func TestEncodeStreamMultipleConsumer(t *testing.T) { `{"testStr":"","testInt":0,"testInt64":0,"testInt32":0,"testInt16":0,"testInt8":0,"testUint64":0,"testUint32":0,"testUint16":0,"testUint8":0,"testFloat64":0,"testFloat32":0,"testBool":false} ` // create our writer - w := &TestWriter{target: 5000, mux: &sync.Mutex{}} + w := &TestWriter{target: 5000, mux: &sync.RWMutex{}} enc := Stream.NewEncoder(w).NConsumer(50).LineDelimited() w.enc = enc s := StreamChanObject(make(chan *testObject)) @@ -155,17 +155,17 @@ type TestWriter struct { target int enc *StreamEncoder result [][]byte - mux *sync.Mutex + mux *sync.RWMutex } func (w *TestWriter) Write(b []byte) (int, error) { if len(b) > 0 { w.mux.Lock() w.result = append(w.result, b) - w.mux.Unlock() if len(w.result) == w.target { w.enc.Cancel(nil) } + w.mux.Unlock() } return len(b), nil }