snapshot of https://github.com/grpc/grpc-go.git (master, 2026-09-15; blobs > 131072B omitted)
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
Some checks failed
codecov / upload (push) Failing after 13m59s
CodeQL / Analyze (push) Failing after 1s
Testing / vet-proto (push) Failing after 1s
Testing / tests (arm, latest) (push) Waiting to run
Testing / extras (latest) (push) Failing after 1s
Testing / static checks (latest-1) (push) Failing after 1s
Testing / tests (-race, latest) (push) Failing after 1s
Testing / tests (i386, latest) (push) Failing after 1s
Testing / tests (latest) (push) Failing after 1s
Testing / tests (latest-1) (push) Failing after 1s
Lock Threads / lock (push) Failing after 1s
govulncheck / govulncheck (.) (push) Failing after 1s
govulncheck / govulncheck (./cmd/protoc-gen-go-grpc) (push) Failing after 1s
govulncheck / govulncheck (./gcp/observability) (push) Failing after 1s
govulncheck / govulncheck (./security/advancedtls) (push) Failing after 1s
govulncheck / govulncheck (./stats/opencensus) (push) Failing after 1s
Stale bot / stale (push) Failing after 1s
This commit is contained in:
317
test/bufconn/bufconn.go
Normal file
317
test/bufconn/bufconn.go
Normal file
@@ -0,0 +1,317 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
// Package bufconn provides a net.Conn implemented by a buffer and related
|
||||
// dialing and listening functionality.
|
||||
package bufconn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Listener implements a net.Listener that creates local, buffered net.Conns
|
||||
// via its Accept and Dial method.
|
||||
type Listener struct {
|
||||
mu sync.Mutex
|
||||
sz int
|
||||
ch chan net.Conn
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
// Implementation of net.Error providing timeout
|
||||
type netErrorTimeout struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (e netErrorTimeout) Timeout() bool { return true }
|
||||
func (e netErrorTimeout) Temporary() bool { return false }
|
||||
|
||||
var errClosed = fmt.Errorf("closed")
|
||||
var errTimeout net.Error = netErrorTimeout{error: fmt.Errorf("i/o timeout")}
|
||||
|
||||
// Listen returns a Listener that can only be contacted by its own Dialers and
|
||||
// creates buffered connections between the two.
|
||||
func Listen(sz int) *Listener {
|
||||
return &Listener{sz: sz, ch: make(chan net.Conn), done: make(chan struct{})}
|
||||
}
|
||||
|
||||
// Accept blocks until Dial is called, then returns a net.Conn for the server
|
||||
// half of the connection.
|
||||
func (l *Listener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case <-l.done:
|
||||
return nil, errClosed
|
||||
case c := <-l.ch:
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Close stops the listener.
|
||||
func (l *Listener) Close() error {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
select {
|
||||
case <-l.done:
|
||||
// Already closed.
|
||||
default:
|
||||
close(l.done)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Addr reports the address of the listener.
|
||||
func (l *Listener) Addr() net.Addr { return addr{} }
|
||||
|
||||
// Dial creates an in-memory full-duplex network connection, unblocks Accept by
|
||||
// providing it the server half of the connection, and returns the client half
|
||||
// of the connection.
|
||||
func (l *Listener) Dial() (net.Conn, error) {
|
||||
return l.DialContext(context.Background())
|
||||
}
|
||||
|
||||
// DialContext creates an in-memory full-duplex network connection, unblocks Accept by
|
||||
// providing it the server half of the connection, and returns the client half
|
||||
// of the connection. If ctx is Done, returns ctx.Err()
|
||||
func (l *Listener) DialContext(ctx context.Context) (net.Conn, error) {
|
||||
p1, p2 := newPipe(l.sz), newPipe(l.sz)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
case <-l.done:
|
||||
return nil, errClosed
|
||||
case l.ch <- &conn{p1, p2}:
|
||||
return &conn{p2, p1}, nil
|
||||
}
|
||||
}
|
||||
|
||||
type pipe struct {
|
||||
mu sync.Mutex
|
||||
|
||||
// buf contains the data in the pipe. It is a ring buffer of fixed capacity,
|
||||
// with r and w pointing to the offset to read and write, respectively.
|
||||
//
|
||||
// Data is read between [r, w) and written to [w, r), wrapping around the end
|
||||
// of the slice if necessary.
|
||||
//
|
||||
// The buffer is empty if r == len(buf), otherwise if r == w, it is full.
|
||||
//
|
||||
// w and r are always in the range [0, cap(buf)) and [0, len(buf)].
|
||||
buf []byte
|
||||
w, r int
|
||||
|
||||
wwait sync.Cond
|
||||
rwait sync.Cond
|
||||
|
||||
// Indicate that a write/read timeout has occurred
|
||||
wtimedout bool
|
||||
rtimedout bool
|
||||
|
||||
wtimer *time.Timer
|
||||
rtimer *time.Timer
|
||||
|
||||
closed bool
|
||||
writeClosed bool
|
||||
}
|
||||
|
||||
func newPipe(sz int) *pipe {
|
||||
p := &pipe{buf: make([]byte, 0, sz)}
|
||||
p.wwait.L = &p.mu
|
||||
p.rwait.L = &p.mu
|
||||
|
||||
p.wtimer = time.AfterFunc(0, func() {})
|
||||
p.rtimer = time.AfterFunc(0, func() {})
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *pipe) empty() bool {
|
||||
return p.r == len(p.buf)
|
||||
}
|
||||
|
||||
func (p *pipe) full() bool {
|
||||
return p.r < len(p.buf) && p.r == p.w
|
||||
}
|
||||
|
||||
func (p *pipe) Read(b []byte) (n int, err error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
// Block until p has data.
|
||||
for {
|
||||
if p.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
if !p.empty() {
|
||||
break
|
||||
}
|
||||
if p.writeClosed {
|
||||
return 0, io.EOF
|
||||
}
|
||||
if p.rtimedout {
|
||||
return 0, errTimeout
|
||||
}
|
||||
|
||||
p.rwait.Wait()
|
||||
}
|
||||
wasFull := p.full()
|
||||
|
||||
n = copy(b, p.buf[p.r:len(p.buf)])
|
||||
p.r += n
|
||||
if p.r == cap(p.buf) {
|
||||
p.r = 0
|
||||
p.buf = p.buf[:p.w]
|
||||
}
|
||||
|
||||
// Signal a blocked writer, if any
|
||||
if wasFull {
|
||||
p.wwait.Signal()
|
||||
}
|
||||
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (p *pipe) Write(b []byte) (n int, err error) {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
if p.closed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
for len(b) > 0 {
|
||||
// Block until p is not full.
|
||||
for {
|
||||
if p.closed || p.writeClosed {
|
||||
return 0, io.ErrClosedPipe
|
||||
}
|
||||
if !p.full() {
|
||||
break
|
||||
}
|
||||
if p.wtimedout {
|
||||
return 0, errTimeout
|
||||
}
|
||||
|
||||
p.wwait.Wait()
|
||||
}
|
||||
wasEmpty := p.empty()
|
||||
|
||||
end := cap(p.buf)
|
||||
if p.w < p.r {
|
||||
end = p.r
|
||||
}
|
||||
x := copy(p.buf[p.w:end], b)
|
||||
b = b[x:]
|
||||
n += x
|
||||
p.w += x
|
||||
if p.w > len(p.buf) {
|
||||
p.buf = p.buf[:p.w]
|
||||
}
|
||||
if p.w == cap(p.buf) {
|
||||
p.w = 0
|
||||
}
|
||||
|
||||
// Signal a blocked reader, if any.
|
||||
if wasEmpty {
|
||||
p.rwait.Signal()
|
||||
}
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (p *pipe) Close() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.closed = true
|
||||
// Signal all blocked readers and writers to return an error.
|
||||
p.rwait.Broadcast()
|
||||
p.wwait.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *pipe) closeWrite() error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.writeClosed = true
|
||||
// Signal all blocked readers and writers to return an error.
|
||||
p.rwait.Broadcast()
|
||||
p.wwait.Broadcast()
|
||||
return nil
|
||||
}
|
||||
|
||||
type conn struct {
|
||||
io.Reader
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (c *conn) Close() error {
|
||||
err1 := c.Reader.(*pipe).Close()
|
||||
err2 := c.Writer.(*pipe).closeWrite()
|
||||
if err1 != nil {
|
||||
return err1
|
||||
}
|
||||
return err2
|
||||
}
|
||||
|
||||
func (c *conn) SetDeadline(t time.Time) error {
|
||||
c.SetReadDeadline(t)
|
||||
c.SetWriteDeadline(t)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) SetReadDeadline(t time.Time) error {
|
||||
p := c.Reader.(*pipe)
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.rtimer.Stop()
|
||||
p.rtimedout = false
|
||||
if !t.IsZero() {
|
||||
p.rtimer = time.AfterFunc(time.Until(t), func() {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.rtimedout = true
|
||||
p.rwait.Broadcast()
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *conn) SetWriteDeadline(t time.Time) error {
|
||||
p := c.Writer.(*pipe)
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.wtimer.Stop()
|
||||
p.wtimedout = false
|
||||
if !t.IsZero() {
|
||||
p.wtimer = time.AfterFunc(time.Until(t), func() {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
p.wtimedout = true
|
||||
p.wwait.Broadcast()
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*conn) LocalAddr() net.Addr { return addr{} }
|
||||
func (*conn) RemoteAddr() net.Addr { return addr{} }
|
||||
|
||||
type addr struct{}
|
||||
|
||||
func (addr) Network() string { return "bufconn" }
|
||||
func (addr) String() string { return "bufconn" }
|
||||
328
test/bufconn/bufconn_test.go
Normal file
328
test/bufconn/bufconn_test.go
Normal file
@@ -0,0 +1,328 @@
|
||||
/*
|
||||
*
|
||||
* Copyright 2017 gRPC authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package bufconn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc/internal/grpctest"
|
||||
)
|
||||
|
||||
type s struct {
|
||||
grpctest.Tester
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
grpctest.RunSubTests(t, s{})
|
||||
}
|
||||
|
||||
func testRW(r io.Reader, w io.Writer) error {
|
||||
for i := 0; i < 20; i++ {
|
||||
d := make([]byte, i)
|
||||
for j := 0; j < i; j++ {
|
||||
d[j] = byte(i - j)
|
||||
}
|
||||
var rn int
|
||||
var rerr error
|
||||
b := make([]byte, i)
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
for rn < len(b) && rerr == nil {
|
||||
var x int
|
||||
x, rerr = r.Read(b[rn:])
|
||||
rn += x
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
wn, werr := w.Write(d)
|
||||
if wn != i || werr != nil {
|
||||
return fmt.Errorf("%v: w.Write(%v) = %v, %v; want %v, nil", i, d, wn, werr, i)
|
||||
}
|
||||
select {
|
||||
case <-done:
|
||||
case <-time.After(500 * time.Millisecond):
|
||||
return fmt.Errorf("%v: r.Read never returned", i)
|
||||
}
|
||||
if rn != i || rerr != nil {
|
||||
return fmt.Errorf("%v: r.Read = %v, %v; want %v, nil", i, rn, rerr, i)
|
||||
}
|
||||
if !reflect.DeepEqual(b, d) {
|
||||
return fmt.Errorf("%v: r.Read read %v; want %v", i, b, d)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s) TestPipe(t *testing.T) {
|
||||
p := newPipe(10)
|
||||
if err := testRW(p, p); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestPipeClose(t *testing.T) {
|
||||
p := newPipe(10)
|
||||
p.Close()
|
||||
if _, err := p.Write(nil); err != io.ErrClosedPipe {
|
||||
t.Fatalf("p.Write = _, %v; want _, %v", err, io.ErrClosedPipe)
|
||||
}
|
||||
if _, err := p.Read(nil); err != io.ErrClosedPipe {
|
||||
t.Fatalf("p.Read = _, %v; want _, %v", err, io.ErrClosedPipe)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestConn(t *testing.T) {
|
||||
p1, p2 := newPipe(10), newPipe(10)
|
||||
c1, c2 := &conn{p1, p2}, &conn{p2, p1}
|
||||
|
||||
if err := testRW(c1, c2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := testRW(c2, c1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestConnCloseWithData(t *testing.T) {
|
||||
lis := Listen(7)
|
||||
errChan := make(chan error, 1)
|
||||
var lisConn net.Conn
|
||||
go func() {
|
||||
var err error
|
||||
if lisConn, err = lis.Accept(); err != nil {
|
||||
errChan <- err
|
||||
}
|
||||
close(errChan)
|
||||
}()
|
||||
dialConn, err := lis.Dial()
|
||||
if err != nil {
|
||||
t.Fatalf("Dial error: %v", err)
|
||||
}
|
||||
if err := <-errChan; err != nil {
|
||||
t.Fatalf("Listen error: %v", err)
|
||||
}
|
||||
|
||||
// Write some data on both sides of the connection.
|
||||
n, err := dialConn.Write([]byte("hello"))
|
||||
if n != 5 || err != nil {
|
||||
t.Fatalf("dialConn.Write([]byte{\"hello\"}) = %v, %v; want 5, <nil>", n, err)
|
||||
}
|
||||
n, err = lisConn.Write([]byte("hello"))
|
||||
if n != 5 || err != nil {
|
||||
t.Fatalf("lisConn.Write([]byte{\"hello\"}) = %v, %v; want 5, <nil>", n, err)
|
||||
}
|
||||
|
||||
// Close dial-side; writes from either side should fail.
|
||||
dialConn.Close()
|
||||
if _, err := lisConn.Write([]byte("hello")); err != io.ErrClosedPipe {
|
||||
t.Fatalf("lisConn.Write() = _, <nil>; want _, <non-nil>")
|
||||
}
|
||||
if _, err := dialConn.Write([]byte("hello")); err != io.ErrClosedPipe {
|
||||
t.Fatalf("dialConn.Write() = _, <nil>; want _, <non-nil>")
|
||||
}
|
||||
|
||||
// Read from both sides; reads on lisConn should work, but dialConn should
|
||||
// fail.
|
||||
buf := make([]byte, 6)
|
||||
if _, err := dialConn.Read(buf); err != io.ErrClosedPipe {
|
||||
t.Fatalf("dialConn.Read(buf) = %v, %v; want _, io.ErrClosedPipe", n, err)
|
||||
}
|
||||
n, err = lisConn.Read(buf)
|
||||
if n != 5 || err != nil {
|
||||
t.Fatalf("lisConn.Read(buf) = %v, %v; want 5, <nil>", n, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestListener(t *testing.T) {
|
||||
l := Listen(7)
|
||||
var s net.Conn
|
||||
var serr error
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
s, serr = l.Accept()
|
||||
close(done)
|
||||
}()
|
||||
c, cerr := l.Dial()
|
||||
<-done
|
||||
if cerr != nil || serr != nil {
|
||||
t.Fatalf("cerr = %v, serr = %v; want nil, nil", cerr, serr)
|
||||
}
|
||||
if err := testRW(c, s); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := testRW(s, c); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestCloseWhileDialing(t *testing.T) {
|
||||
l := Listen(7)
|
||||
var c net.Conn
|
||||
var err error
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
c, err = l.Dial()
|
||||
close(done)
|
||||
}()
|
||||
l.Close()
|
||||
<-done
|
||||
if c != nil || err != errClosed {
|
||||
t.Fatalf("c, err = %v, %v; want nil, %v", c, err, errClosed)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestCloseWhileAccepting(t *testing.T) {
|
||||
l := Listen(7)
|
||||
var c net.Conn
|
||||
var err error
|
||||
done := make(chan struct{})
|
||||
go func() {
|
||||
c, err = l.Accept()
|
||||
close(done)
|
||||
}()
|
||||
l.Close()
|
||||
<-done
|
||||
if c != nil || err != errClosed {
|
||||
t.Fatalf("c, err = %v, %v; want nil, %v", c, err, errClosed)
|
||||
}
|
||||
}
|
||||
|
||||
func (s) TestDeadline(t *testing.T) {
|
||||
sig := make(chan error, 2)
|
||||
blockingWrite := func(conn net.Conn) {
|
||||
_, err := conn.Write([]byte("0123456789"))
|
||||
sig <- err
|
||||
}
|
||||
|
||||
blockingRead := func(conn net.Conn) {
|
||||
_, err := conn.Read(make([]byte, 10))
|
||||
sig <- err
|
||||
}
|
||||
|
||||
p1, p2 := newPipe(5), newPipe(5)
|
||||
c1, c2 := &conn{p1, p1}, &conn{p2, p2}
|
||||
defer c1.Close()
|
||||
defer c2.Close()
|
||||
|
||||
// Test with deadline
|
||||
c1.SetWriteDeadline(time.Now())
|
||||
|
||||
go blockingWrite(c1)
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatalf("Write timeout timed out, c = %v", c1)
|
||||
case err := <-sig:
|
||||
if netErr, ok := err.(net.Error); ok {
|
||||
if !netErr.Timeout() {
|
||||
t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, netErr)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, err)
|
||||
}
|
||||
}
|
||||
|
||||
c2.SetReadDeadline(time.Now())
|
||||
|
||||
go blockingRead(c2)
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatalf("Read timeout timed out, c = %v", c2)
|
||||
case err := <-sig:
|
||||
if netErr, ok := err.(net.Error); ok {
|
||||
if !netErr.Timeout() {
|
||||
t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, netErr)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test timing out pending reads/writes
|
||||
c1.SetWriteDeadline(time.Time{})
|
||||
c2.SetReadDeadline(time.Time{})
|
||||
|
||||
go blockingWrite(c1)
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
case err := <-sig:
|
||||
t.Fatalf("Write returned before timeout, err = %v", err)
|
||||
}
|
||||
|
||||
c1.SetWriteDeadline(time.Now())
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatalf("Write timeout timed out, c = %v", c1)
|
||||
case err := <-sig:
|
||||
if netErr, ok := err.(net.Error); ok {
|
||||
if !netErr.Timeout() {
|
||||
t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, netErr)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Write returned unexpected error, c = %v, err = %v", c1, err)
|
||||
}
|
||||
}
|
||||
|
||||
go blockingRead(c2)
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
case err := <-sig:
|
||||
t.Fatalf("Read returned before timeout, err = %v", err)
|
||||
}
|
||||
|
||||
c2.SetReadDeadline(time.Now())
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatalf("Read timeout timed out, c = %v", c2)
|
||||
case err := <-sig:
|
||||
if netErr, ok := err.(net.Error); ok {
|
||||
if !netErr.Timeout() {
|
||||
t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, netErr)
|
||||
}
|
||||
} else {
|
||||
t.Fatalf("Read returned unexpected error, c = %v, err = %v", c2, err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test non-blocking read/write
|
||||
c1, c2 = &conn{p1, p2}, &conn{p2, p1}
|
||||
|
||||
c1.SetWriteDeadline(time.Now().Add(10 * time.Second))
|
||||
c2.SetReadDeadline(time.Now().Add(10 * time.Second))
|
||||
|
||||
// Not blocking here
|
||||
go blockingWrite(c1)
|
||||
go blockingRead(c2)
|
||||
|
||||
// Read response from both routines
|
||||
for i := 0; i < 2; i++ {
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
t.Fatalf("Read/Write timed out, c1 = %v, c2 = %v", c1, c2)
|
||||
case err := <-sig:
|
||||
if err != nil {
|
||||
t.Fatalf("Read/Write failed to complete, c1 = %v, c2 = %v, err = %v", c1, c2, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user