...

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