// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
-// can overwrite this data, which could cause the finalizer
+// can overwrite this data, which could cause the cleanup
// to close the wrong file descriptor.
type file struct {
fdmu poll.FDMutex
name string
dirinfo atomic.Pointer[dirInfo] // nil unless directory being read
appendMode bool // whether file is opened for appending
+ cleanup runtime.Cleanup // cleanup closes the file when no longer referenced
}
// Fd returns the integer Plan 9 file descriptor referencing the open file.
// If f is closed, the file descriptor becomes invalid.
-// If f is garbage collected, a finalizer may close the file descriptor,
-// making it invalid; see [runtime.SetFinalizer] for more information on when
-// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline]
+// If f is garbage collected, a cleanup may close the file descriptor,
+// making it invalid; see [runtime.AddCleanup] for more information on when
+// a cleanup might be run. On Unix systems this will cause the [File.SetDeadline]
// methods to stop working.
//
// As an alternative, see the f.SyscallConn method.
return nil
}
f := &File{&file{fd: fdi, name: name}}
- runtime.SetFinalizer(f.file, (*file).close)
+ f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file)
return f
}
err := file.decref()
- // no need for a finalizer anymore
- runtime.SetFinalizer(file, nil)
+ // There is no need for a cleanup at this point. File must be alive at the point
+ // where cleanup.stop is called.
+ file.cleanup.Stop()
return err
}
// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
-// can overwrite this data, which could cause the finalizer
+// can overwrite this data, which could cause the cleanup
// to close the wrong file descriptor.
type file struct {
pfd poll.FD
nonblock bool // whether we set nonblocking mode
stdoutOrErr bool // whether this is stdout or stderr
appendMode bool // whether file is opened for appending
+ cleanup runtime.Cleanup // cleanup closes the file when no longer referenced
}
// Fd returns the integer Unix file descriptor referencing the open file.
// If f is closed, the file descriptor becomes invalid.
-// If f is garbage collected, a finalizer may close the file descriptor,
-// making it invalid; see [runtime.SetFinalizer] for more information on when
-// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline]
+// If f is garbage collected, a cleanup may close the file descriptor,
+// making it invalid; see [runtime.AddCleanup] for more information on when
+// a cleanup might be run. On Unix systems this will cause the [File.SetDeadline]
// methods to stop working.
// Because file descriptors can be reused, the returned file descriptor may
-// only be closed through the [File.Close] method of f, or by its finalizer during
-// garbage collection. Otherwise, during garbage collection the finalizer
+// only be closed through the [File.Close] method of f, or by its cleanup during
+// garbage collection. Otherwise, during garbage collection the cleanup
// may close an unrelated file descriptor with the same (reused) number.
//
// As an alternative, see the f.SyscallConn method.
}
}
- runtime.SetFinalizer(f.file, (*file).close)
+ // Close the file when the File is not live.
+ f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file)
return f
}
err = &PathError{Op: "close", Path: file.name, Err: e}
}
- // no need for a finalizer anymore
- runtime.SetFinalizer(file, nil)
+ // There is no need for a cleanup at this point. File must be alive at the point
+ // where cleanup.stop is called.
+ file.cleanup.Stop()
return err
}
// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
-// can overwrite this data, which could cause the finalizer
+// can overwrite this data, which could cause the cleanup
// to close the wrong file descriptor.
type file struct {
pfd poll.FD
name string
dirinfo atomic.Pointer[dirInfo] // nil unless directory being read
appendMode bool // whether file is opened for appending
+ cleanup runtime.Cleanup // cleanup closes the file when no longer referenced
}
// Fd returns the Windows handle referencing the open file.
// If f is closed, the file descriptor becomes invalid.
-// If f is garbage collected, a finalizer may close the file descriptor,
-// making it invalid; see [runtime.SetFinalizer] for more information on when
-// a finalizer might be run. On Unix systems this will cause the [File.SetDeadline]
+// If f is garbage collected, a cleanup may close the file descriptor,
+// making it invalid; see [runtime.AddCleanup] for more information on when
+// a cleanup might be run. On Unix systems this will cause the [File.SetDeadline]
// methods to stop working.
func (file *File) Fd() uintptr {
if file == nil {
},
name: name,
}}
- runtime.SetFinalizer(f.file, (*file).close)
+ f.cleanup = runtime.AddCleanup(f, func(f *file) { f.close() }, f.file)
// Ignore initialization errors.
// Assume any problems will show up in later I/O.
err = &PathError{Op: "close", Path: file.name, Err: e}
}
- // no need for a finalizer anymore
- runtime.SetFinalizer(file, nil)
+ // There is no need for a cleanup at this point. File must be alive at the point
+ // where cleanup.stop is called.
+ file.cleanup.Stop()
return err
}
// refs is incremented while an operation is using fd.
// closed is set when Close is called.
// fd is closed when closed is true and refs is 0.
- mu sync.Mutex
- fd sysfdType
- refs int // number of active operations
- closed bool // set when closed
+ mu sync.Mutex
+ fd sysfdType
+ refs int // number of active operations
+ closed bool // set when closed
+ cleanup runtime.Cleanup // cleanup closes the file when no longer referenced
}
func (r *root) Close() error {
syscall.Close(r.fd)
}
r.closed = true
- runtime.SetFinalizer(r, nil) // no need for a finalizer any more
+ // There is no need for a cleanup at this point. Root must be alive at the point
+ // where cleanup.stop is called.
+ r.cleanup.Stop()
return nil
}
fd: fd,
name: name,
}}
- runtime.SetFinalizer(r.root, (*root).Close)
+ r.root.cleanup = runtime.AddCleanup(r, func(f *root) { f.Close() }, r.root)
return r, nil
}
fd: fd,
name: name,
}}
- runtime.SetFinalizer(r.root, (*root).Close)
+ r.root.cleanup = runtime.AddCleanup(r, func(f *root) { f.Close() }, r.root)
return r, nil
}