Black Lives Matter. Support the Equal Justice Initiative.

Source file src/net/dial_unix_test.go

Documentation: net

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

View as plain text