mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
net: use context.AfterFunc in connect
This saves a goroutine when ctx can be but is not canceled during the connect call. The redundant fd.Close() call is removed, because the caller closes the fd on error. Change-Id: I124d7e480294a48ef74d5650d8ef0489bdfc64d6 Reviewed-on: https://go-review.googlesource.com/c/go/+/699256 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Sean Liao <sean@liao.dev> Reviewed-by: Damien Neil <dneil@google.com> Auto-Submit: Sean Liao <sean@liao.dev> Reviewed-by: Mark Freeman <markfreeman@google.com>
This commit is contained in:
parent
ac803b5949
commit
080882a928
1 changed files with 22 additions and 36 deletions
|
|
@ -74,46 +74,32 @@ func (fd *netFD) connect(ctx context.Context, la, ra syscall.Sockaddr) (rsa sysc
|
||||||
if err := fd.pfd.Init(fd.net, true); err != nil {
|
if err := fd.pfd.Init(fd.net, true); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
|
||||||
fd.pfd.SetWriteDeadline(deadline)
|
|
||||||
defer fd.pfd.SetWriteDeadline(noDeadline)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the "interrupter" goroutine, if this context might be canceled.
|
|
||||||
//
|
|
||||||
// The interrupter goroutine waits for the context to be done and
|
|
||||||
// interrupts the dial (by altering the fd's write deadline, which
|
|
||||||
// wakes up waitWrite).
|
|
||||||
ctxDone := ctx.Done()
|
ctxDone := ctx.Done()
|
||||||
if ctxDone != nil {
|
if ctxDone != nil {
|
||||||
// Wait for the interrupter goroutine to exit before returning
|
if deadline, hasDeadline := ctx.Deadline(); hasDeadline {
|
||||||
// from connect.
|
fd.pfd.SetWriteDeadline(deadline)
|
||||||
done := make(chan struct{})
|
defer fd.pfd.SetWriteDeadline(noDeadline)
|
||||||
interruptRes := make(chan error)
|
}
|
||||||
|
|
||||||
|
stop := context.AfterFunc(ctx, func() {
|
||||||
|
// Force the runtime's poller to immediately give up
|
||||||
|
// waiting for writability, unblocking waitWrite
|
||||||
|
// below.
|
||||||
|
_ = fd.pfd.SetWriteDeadline(aLongTimeAgo)
|
||||||
|
testHookCanceledDial()
|
||||||
|
})
|
||||||
defer func() {
|
defer func() {
|
||||||
close(done)
|
if !stop() && ret == nil {
|
||||||
if ctxErr := <-interruptRes; ctxErr != nil && ret == nil {
|
// The context.AfterFunc has called or is about to call
|
||||||
// The interrupter goroutine called SetWriteDeadline,
|
// SetWriteDeadline, but the connect code below had
|
||||||
// but the connect code below had returned from
|
// returned from waitWrite already and did a successful
|
||||||
// waitWrite already and did a successful connect (ret
|
// connect (ret == nil). Because we've now poisoned the
|
||||||
// == nil). Because we've now poisoned the connection
|
// connection by making it unwritable, don't return a
|
||||||
// by making it unwritable, don't return a successful
|
// successful dial. This was issue 16523.
|
||||||
// dial. This was issue 16523.
|
ret = mapErr(ctx.Err())
|
||||||
ret = mapErr(ctxErr)
|
// The caller closes fd on error, so there's no need to
|
||||||
fd.Close() // prevent a leak
|
// wait for the SetWriteDeadline call to return.
|
||||||
}
|
|
||||||
}()
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case <-ctxDone:
|
|
||||||
// Force the runtime's poller to immediately give up
|
|
||||||
// waiting for writability, unblocking waitWrite
|
|
||||||
// below.
|
|
||||||
fd.pfd.SetWriteDeadline(aLongTimeAgo)
|
|
||||||
testHookCanceledDial()
|
|
||||||
interruptRes <- ctx.Err()
|
|
||||||
case <-done:
|
|
||||||
interruptRes <- nil
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue