Skip to content

Commit 382d492

Browse files
internal/poll: don't return from Close until descriptor is closed
This permits the program to reliably know that when the Close method returns, the descriptor has definitely been closed. This matters at least for listeners. Fixes #21856 Updates #7970 Change-Id: I1fd0cfd2333649e6e67c6ae956e19fdff3a35a83 Reviewed-on: https://go-review.googlesource.com/66150 Run-TryBot: Ian Lance Taylor <iant@golang.org> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Joe Tsai <joetsai@google.com>
1 parent 8e11cb3 commit 382d492

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

src/internal/poll/fd_unix.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ type FD struct {
2626
// Writev cache.
2727
iovecs *[]syscall.Iovec
2828

29+
// Semaphore signaled when file is closed.
30+
csema uint32
31+
2932
// Whether this is a streaming descriptor, as opposed to a
3033
// packet-based descriptor like a UDP socket. Immutable.
3134
IsStream bool
@@ -62,6 +65,7 @@ func (fd *FD) destroy() error {
6265
fd.pd.close()
6366
err := CloseFunc(fd.Sysfd)
6467
fd.Sysfd = -1
68+
runtime_Semrelease(&fd.csema)
6569
return err
6670
}
6771

@@ -79,7 +83,11 @@ func (fd *FD) Close() error {
7983
fd.pd.evict()
8084
// The call to decref will call destroy if there are no other
8185
// references.
82-
return fd.decref()
86+
err := fd.decref()
87+
// Wait until the descriptor is closed. If this was the only
88+
// reference, it is already closed.
89+
runtime_Semacquire(&fd.csema)
90+
return err
8391
}
8492

8593
// Shutdown wraps the shutdown network call.

src/internal/poll/fd_windows.go

+9-1
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,9 @@ type FD struct {
278278
readbyte []byte // buffer to hold decoding of readuint16 from utf16 to utf8
279279
readbyteOffset int // readbyte[readOffset:] is yet to be consumed with file.Read
280280

281+
// Semaphore signaled when file is closed.
282+
csema uint32
283+
281284
skipSyncNotif bool
282285

283286
// Whether this is a streaming descriptor, as opposed to a
@@ -399,6 +402,7 @@ func (fd *FD) destroy() error {
399402
err = CloseFunc(fd.Sysfd)
400403
}
401404
fd.Sysfd = syscall.InvalidHandle
405+
runtime_Semrelease(&fd.csema)
402406
return err
403407
}
404408

@@ -410,7 +414,11 @@ func (fd *FD) Close() error {
410414
}
411415
// unblock pending reader and writer
412416
fd.pd.evict()
413-
return fd.decref()
417+
err := fd.decref()
418+
// Wait until the descriptor is closed. If this was the only
419+
// reference, it is already closed.
420+
runtime_Semacquire(&fd.csema)
421+
return err
414422
}
415423

416424
// Shutdown wraps the shutdown network call.

src/net/listen_test.go

+32
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"runtime"
1414
"syscall"
1515
"testing"
16+
"time"
1617
)
1718

1819
func (ln *TCPListener) port() string {
@@ -696,3 +697,34 @@ func multicastRIBContains(ip IP) (bool, error) {
696697
}
697698
return false, nil
698699
}
700+
701+
// Issue 21856.
702+
func TestClosingListener(t *testing.T) {
703+
listener, err := Listen("tcp", ":0")
704+
if err != nil {
705+
t.Fatal(err)
706+
}
707+
addr := listener.Addr()
708+
709+
go func() {
710+
for {
711+
c, err := listener.Accept()
712+
if err != nil {
713+
return
714+
}
715+
c.Close()
716+
}
717+
}()
718+
719+
// Let the goroutine start. We don't sleep long: if the
720+
// goroutine doesn't start, the test will pass without really
721+
// testing anything, which is OK.
722+
time.Sleep(time.Millisecond)
723+
724+
listener.Close()
725+
726+
_, err = Listen("tcp", addr.String())
727+
if err != nil {
728+
t.Error(err)
729+
}
730+
}

0 commit comments

Comments
 (0)