github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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 }