diff options
| author | qmuntal <quimmuntal@gmail.com> | 2026-02-02 12:06:22 +0100 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2026-02-03 07:55:04 -0800 |
| commit | eedccc63c087a16e297e91ab1004e0d7b88fe924 (patch) | |
| tree | 67cb55a4e6bda81f0f3014a78abb1d0ef681e1c4 | |
| parent | a56d064bd3e8ed3320175708c725e459ba31d5cd (diff) | |
| download | go-eedccc63c087a16e297e91ab1004e0d7b88fe924.tar.xz | |
[release-branch.go1.26] internal/poll: readWriteUnlock should destroy fd when no remaining references
Updates #77404
Fixes #77405
Change-Id: I0402becb94855baf942d6ba3815cc2a3c1526d6e
Reviewed-on: https://go-review.googlesource.com/c/go/+/740921
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Reviewed-by: Michael Pratt <mpratt@google.com>
(cherry picked from commit 31c9bcb1037a332fd547808693cd1899090b5854)
Reviewed-on: https://go-review.googlesource.com/c/go/+/741360
Auto-Submit: Michael Pratt <mpratt@google.com>
| -rw-r--r-- | src/internal/poll/fd_mutex.go | 4 | ||||
| -rw-r--r-- | src/os/os_test.go | 28 |
2 files changed, 31 insertions, 1 deletions
diff --git a/src/internal/poll/fd_mutex.go b/src/internal/poll/fd_mutex.go index c1c9297687..f71c7739a1 100644 --- a/src/internal/poll/fd_mutex.go +++ b/src/internal/poll/fd_mutex.go @@ -265,7 +265,9 @@ func (fd *FD) readWriteLock() error { // is no remaining reference. func (fd *FD) readWriteUnlock() { fd.fdmu.rwunlock(true) - fd.fdmu.rwunlock(false) + if fd.fdmu.rwunlock(false) { + fd.destroy() + } } // closing returns true if fd is closing. diff --git a/src/os/os_test.go b/src/os/os_test.go index 29f2e6d3b2..47f4163220 100644 --- a/src/os/os_test.go +++ b/src/os/os_test.go @@ -3468,6 +3468,34 @@ func TestWriteStringAlloc(t *testing.T) { } } +// Test that it's OK to have parallel I/O and Close on a file. +func TestFileIOCloseRace(t *testing.T) { + t.Parallel() + file, err := Create(filepath.Join(t.TempDir(), "test.txt")) + if err != nil { + t.Fatal(err) + } + var wg sync.WaitGroup + wg.Go(func() { + var tmp [100]byte + if _, err := file.Write(tmp[:]); err != nil && !errors.Is(err, ErrClosed) { + t.Error(err) + } + }) + wg.Go(func() { + var tmp [100]byte + if _, err := file.Read(tmp[:]); err != nil && err != io.EOF && !errors.Is(err, ErrClosed) { + t.Error(err) + } + }) + wg.Go(func() { + if err := file.Close(); err != nil { + t.Error(err) + } + }) + wg.Wait() +} + // Test that it's OK to have parallel I/O and Close on a pipe. func TestPipeIOCloseRace(t *testing.T) { // Skip on wasm, which doesn't have pipes. |
