github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/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 //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 }