aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorqmuntal <quimmuntal@gmail.com>2026-02-02 12:06:22 +0100
committerGopher Robot <gobot@golang.org>2026-02-03 07:55:04 -0800
commiteedccc63c087a16e297e91ab1004e0d7b88fe924 (patch)
tree67cb55a4e6bda81f0f3014a78abb1d0ef681e1c4
parenta56d064bd3e8ed3320175708c725e459ba31d5cd (diff)
downloadgo-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.go4
-rw-r--r--src/os/os_test.go28
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.