]> Cypherpunks repositories - gostls13.git/commitdiff
internal/poll: readWriteUnlock should destroy fd when no remaining references
authorqmuntal <quimmuntal@gmail.com>
Mon, 2 Feb 2026 11:06:22 +0000 (12:06 +0100)
committerQuim Muntal <quimmuntal@gmail.com>
Tue, 3 Feb 2026 09:00:16 +0000 (01:00 -0800)
Fixes #77404

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>
src/internal/poll/fd_mutex.go
src/os/os_test.go

index c1c92976870658efae5829e375729ee7e16b1596..f71c7739a1aa6e1a1eb1962264a9e00644b0a90a 100644 (file)
@@ -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.
index 29f2e6d3b2258c8123d6fc25ced496b8cad022bf..47f4163220e4bbe00d2ef1565de7f83c107910d8 100644 (file)
@@ -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.