github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/src/net/dial_unix_test.go (about)

     1  // Copyright 2016 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // +build darwin dragonfly freebsd linux netbsd openbsd solaris
     6  
     7  package net
     8  
     9  import (
    10  	"context"
    11  	"syscall"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  // Issue 16523
    17  func TestDialContextCancelRace(t *testing.T) {
    18  	oldConnectFunc := connectFunc
    19  	oldGetsockoptIntFunc := getsockoptIntFunc
    20  	oldTestHookCanceledDial := testHookCanceledDial
    21  	defer func() {
    22  		connectFunc = oldConnectFunc
    23  		getsockoptIntFunc = oldGetsockoptIntFunc
    24  		testHookCanceledDial = oldTestHookCanceledDial
    25  	}()
    26  
    27  	ln, err := newLocalListener("tcp")
    28  	if err != nil {
    29  		t.Fatal(err)
    30  	}
    31  	listenerDone := make(chan struct{})
    32  	go func() {
    33  		defer close(listenerDone)
    34  		c, err := ln.Accept()
    35  		if err == nil {
    36  			c.Close()
    37  		}
    38  	}()
    39  	defer func() { <-listenerDone }()
    40  	defer ln.Close()
    41  
    42  	sawCancel := make(chan bool, 1)
    43  	testHookCanceledDial = func() {
    44  		sawCancel <- true
    45  	}
    46  
    47  	ctx, cancelCtx := context.WithCancel(context.Background())
    48  
    49  	connectFunc = func(fd int, addr syscall.Sockaddr) error {
    50  		err := oldConnectFunc(fd, addr)
    51  		t.Logf("connect(%d, addr) = %v", fd, err)
    52  		if err == nil {
    53  			// On some operating systems, localhost
    54  			// connects _sometimes_ succeed immediately.
    55  			// Prevent that, so we exercise the code path
    56  			// we're interested in testing. This seems
    57  			// harmless. It makes FreeBSD 10.10 work when
    58  			// run with many iterations. It failed about
    59  			// half the time previously.
    60  			return syscall.EINPROGRESS
    61  		}
    62  		return err
    63  	}
    64  
    65  	getsockoptIntFunc = func(fd, level, opt int) (val int, err error) {
    66  		val, err = oldGetsockoptIntFunc(fd, level, opt)
    67  		t.Logf("getsockoptIntFunc(%d, %d, %d) = (%v, %v)", fd, level, opt, val, err)
    68  		if level == syscall.SOL_SOCKET && opt == syscall.SO_ERROR && err == nil && val == 0 {
    69  			t.Logf("canceling context")
    70  
    71  			// Cancel the context at just the moment which
    72  			// caused the race in issue 16523.
    73  			cancelCtx()
    74  
    75  			// And wait for the "interrupter" goroutine to
    76  			// cancel the dial by messing with its write
    77  			// timeout before returning.
    78  			select {
    79  			case <-sawCancel:
    80  				t.Logf("saw cancel")
    81  			case <-time.After(5 * time.Second):
    82  				t.Errorf("didn't see cancel after 5 seconds")
    83  			}
    84  		}
    85  		return
    86  	}
    87  
    88  	var d Dialer
    89  	c, err := d.DialContext(ctx, "tcp", ln.Addr().String())
    90  	if err == nil {
    91  		c.Close()
    92  		t.Fatal("unexpected successful dial; want context canceled error")
    93  	}
    94  
    95  	select {
    96  	case <-ctx.Done():
    97  	case <-time.After(5 * time.Second):
    98  		t.Fatal("expected context to be canceled")
    99  	}
   100  
   101  	oe, ok := err.(*OpError)
   102  	if !ok || oe.Op != "dial" {
   103  		t.Fatalf("Dial error = %#v; want dial *OpError", err)
   104  	}
   105  	if oe.Err != ctx.Err() {
   106  		t.Errorf("DialContext = (%v, %v); want OpError with error %v", c, err, ctx.Err())
   107  	}
   108  }