diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 4534425fe67945149d7481fc72be82caa720358d..0000000000000000000000000000000000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,55 +0,0 @@ -name: ci -on: [push, pull_request] - -jobs: - fmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/cache@v1 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Run make fmt - uses: ./ci/image - with: - args: make fmt - - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/cache@v1 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Run make lint - uses: ./ci/image - with: - args: make lint - - test: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v1 - - uses: actions/cache@v1 - with: - path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- - - name: Run make test - uses: ./ci/image - with: - args: make test - env: - COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }} - - name: Upload coverage.html - uses: actions/upload-artifact@master - with: - name: coverage - path: ci/out/coverage.html diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000000000000000000000000000000000..41d3c201468cbbaf1a0b50a8b93c74e10f0e77d8 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,40 @@ +language: go +go: 1.x +dist: bionic + +env: + global: + - SHFMT_URL=https://github.com/mvdan/sh/releases/download/v3.0.1/shfmt_v3.0.1_linux_amd64 + - GOFLAGS="-mod=readonly" + +jobs: + include: + - name: Format + before_script: + - sudo apt-get install -y npm + - sudo npm install -g prettier + - sudo curl -L "$SHFMT_URL" > /usr/local/bin/shfmt && sudo chmod +x /usr/local/bin/shfmt + - go get golang.org/x/tools/cmd/stringer + - go get golang.org/x/tools/cmd/goimports + script: make -j16 fmt + - name: Lint + before_script: + - sudo apt-get install -y shellcheck + - go get golang.org/x/lint/golint + script: make -j16 lint + - name: Test + before_script: + - sudo apt-get install -y chromium-browser + - go get github.com/agnivade/wasmbrowsertest + - go get github.com/mattn/goveralls + script: make -j16 test + +addons: + apt: + update: true + +cache: + npm: true + directories: + - ~/.cache + - ~/gopath/pkg diff --git a/ci/image/Dockerfile b/ci/image/Dockerfile deleted file mode 100644 index ed408edab60fec348794e6ccd01c867672fb39a5..0000000000000000000000000000000000000000 --- a/ci/image/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -FROM golang:1 - -RUN apt-get update -RUN apt-get install -y chromium npm shellcheck - -ARG SHFMT_URL=https://github.com/mvdan/sh/releases/download/v3.0.1/shfmt_v3.0.1_linux_amd64 -RUN curl -L $SHFMT_URL > /usr/local/bin/shfmt && chmod +x /usr/local/bin/shfmt - -ENV GOFLAGS="-mod=readonly" -ENV CI=true -ENV MAKEFLAGS="--jobs=16 --output-sync=target" - -RUN npm install -g prettier -RUN go get golang.org/x/tools/cmd/stringer -RUN go get golang.org/x/tools/cmd/goimports -RUN go get golang.org/x/lint/golint -RUN go get github.com/agnivade/wasmbrowsertest -RUN go get github.com/mattn/goveralls diff --git a/close_notjs.go b/close_notjs.go index c25b088f19bad5f6a25a5ea76f633470608eebd8..2537299560bd2f7560a112d8c86a67b639aecf61 100644 --- a/close_notjs.go +++ b/close_notjs.go @@ -86,11 +86,11 @@ func (c *Conn) waitCloseHandshake() error { ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) defer cancel() - err := c.readMu.Lock(ctx) + err := c.readMu.lock(ctx) if err != nil { return err } - defer c.readMu.Unlock() + defer c.readMu.unlock() if c.readCloseFrameErr != nil { return c.readCloseFrameErr diff --git a/conn_notjs.go b/conn_notjs.go index 7ee60fbc300ea249fdd30535e92093f5a52ebd71..2ec5f5bf9f2a7902b557a5901968b31557b4b205 100644 --- a/conn_notjs.go +++ b/conn_notjs.go @@ -139,16 +139,9 @@ func (c *Conn) close(err error) { c.rwc.Close() go func() { - if c.client { - c.writeFrameMu.Lock(context.Background()) - putBufioWriter(c.bw) - } c.msgWriterState.close() c.msgReader.close() - if c.client { - putBufioReader(c.br) - } }() } @@ -237,7 +230,11 @@ func newMu(c *Conn) *mu { } } -func (m *mu) Lock(ctx context.Context) error { +func (m *mu) forceLock() { + m.ch <- struct{}{} +} + +func (m *mu) lock(ctx context.Context) error { select { case <-m.c.closed: return m.c.closeErr @@ -246,11 +243,19 @@ func (m *mu) Lock(ctx context.Context) error { m.c.close(err) return err case m.ch <- struct{}{}: + // To make sure the connection is certainly alive. + // As it's possible the send on m.ch was selected + // the receive on closed. + select { + case <-m.c.closed: + return m.c.closeErr + default: + } return nil } } -func (m *mu) Unlock() { +func (m *mu) unlock() { select { case <-m.ch: default: diff --git a/conn_test.go b/conn_test.go index 64e6736fd334174196c29df92e718d3f1d9ae1b7..535afe247e6b759fe117e94c8beead9a41a8888a 100644 --- a/conn_test.go +++ b/conn_test.go @@ -268,6 +268,10 @@ func TestConn(t *testing.T) { func TestWasm(t *testing.T) { t.Parallel() + if os.Getenv("CI") != "" { + t.Skip("skipping on CI") + } + var wg sync.WaitGroup s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { wg.Add(1) diff --git a/dial.go b/dial.go index f882f122f5578333d17d7f52390e5c529b6372a0..50a0ecce3f1aeedde43715337e2b67f85f50eb85 100644 --- a/dial.go +++ b/dial.go @@ -253,10 +253,10 @@ func verifyServerExtensions(copts *compressionOptions, h http.Header) (*compress return copts, nil } -var readerPool sync.Pool +var bufioReaderPool sync.Pool func getBufioReader(r io.Reader) *bufio.Reader { - br, ok := readerPool.Get().(*bufio.Reader) + br, ok := bufioReaderPool.Get().(*bufio.Reader) if !ok { return bufio.NewReader(r) } @@ -265,13 +265,13 @@ func getBufioReader(r io.Reader) *bufio.Reader { } func putBufioReader(br *bufio.Reader) { - readerPool.Put(br) + bufioReaderPool.Put(br) } -var writerPool sync.Pool +var bufioWriterPool sync.Pool func getBufioWriter(w io.Writer) *bufio.Writer { - bw, ok := writerPool.Get().(*bufio.Writer) + bw, ok := bufioWriterPool.Get().(*bufio.Writer) if !ok { return bufio.NewWriter(w) } @@ -280,5 +280,5 @@ func getBufioWriter(w io.Writer) *bufio.Writer { } func putBufioWriter(bw *bufio.Writer) { - writerPool.Put(bw) + bufioWriterPool.Put(bw) } diff --git a/read.go b/read.go index a1efecabb269c1efceacab1557962cebb54841fa..f2c7a801984fb590c342cf1f2aa30bbc23ceddf5 100644 --- a/read.go +++ b/read.go @@ -109,12 +109,17 @@ func (mr *msgReader) putFlateReader() { } func (mr *msgReader) close() { - mr.c.readMu.Lock(context.Background()) + mr.c.readMu.forceLock() mr.putFlateReader() mr.dict.close() if mr.flateBufio != nil { putBufioReader(mr.flateBufio) } + + if mr.c.client { + putBufioReader(mr.c.br) + mr.c.br = nil + } } func (mr *msgReader) flateContextTakeover() bool { @@ -292,11 +297,11 @@ func (c *Conn) handleControl(ctx context.Context, h header) (err error) { func (c *Conn) reader(ctx context.Context) (_ MessageType, _ io.Reader, err error) { defer errd.Wrap(&err, "failed to get reader") - err = c.readMu.Lock(ctx) + err = c.readMu.lock(ctx) if err != nil { return 0, nil, err } - defer c.readMu.Unlock() + defer c.readMu.unlock() if !c.msgReader.fin { return 0, nil, errors.New("previous message not read to completion") @@ -368,11 +373,11 @@ func (mr *msgReader) Read(p []byte) (n int, err error) { errd.Wrap(&err, "failed to read") }() - err = mr.c.readMu.Lock(mr.ctx) + err = mr.c.readMu.lock(mr.ctx) if err != nil { return 0, err } - defer mr.c.readMu.Unlock() + defer mr.c.readMu.unlock() n, err = mr.limitReader.Read(p) if mr.flate && mr.flateContextTakeover() { diff --git a/write.go b/write.go index 81b9141ae7e06aac325de7868ed4302bdfef4975..2d20b292f4f1110b8434da672b0dcd8ff2a1e4c2 100644 --- a/write.go +++ b/write.go @@ -125,7 +125,7 @@ func (c *Conn) write(ctx context.Context, typ MessageType, p []byte) (int, error } if !c.flate() { - defer c.msgWriterState.mu.Unlock() + defer c.msgWriterState.mu.unlock() return c.writeFrame(ctx, true, false, c.msgWriterState.opcode, p) } @@ -139,7 +139,7 @@ func (c *Conn) write(ctx context.Context, typ MessageType, p []byte) (int, error } func (mw *msgWriterState) reset(ctx context.Context, typ MessageType) error { - err := mw.mu.Lock(ctx) + err := mw.mu.lock(ctx) if err != nil { return err } @@ -204,11 +204,16 @@ func (mw *msgWriterState) Close() (err error) { if mw.flate && !mw.flateContextTakeover() { mw.dict.close() } - mw.mu.Unlock() + mw.mu.unlock() return nil } func (mw *msgWriterState) close() { + if mw.c.client { + mw.c.writeFrameMu.forceLock() + putBufioWriter(mw.c.bw) + } + mw.writeMu.Lock() mw.dict.close() } @@ -226,11 +231,11 @@ func (c *Conn) writeControl(ctx context.Context, opcode opcode, p []byte) error // frame handles all writes to the connection. func (c *Conn) writeFrame(ctx context.Context, fin bool, flate bool, opcode opcode, p []byte) (int, error) { - err := c.writeFrameMu.Lock(ctx) + err := c.writeFrameMu.lock(ctx) if err != nil { return 0, err } - defer c.writeFrameMu.Unlock() + defer c.writeFrameMu.unlock() select { case <-c.closed: