Source file src/net/dial_unix_test.go

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

View as plain text