From 41b427a2648ed2e049952450c698be917e0bb125 Mon Sep 17 00:00:00 2001 From: Peter Zhu Date: Thu, 15 Aug 2024 12:17:12 -0400 Subject: [ruby/tempfile] Ensure finalizer order in Tempfile The Closer and Remover finalizers are defined on different objects in Tempfile. The Closer is defined on the Tempfile object while the Remover is defined on the finalizer_obj. This means that there is no guarantee of the finalizer order. On Windows, we must close the file before removing it because we cannot remove an open file. But since the order is not guaranteed, the GC may run the Remover finalizer first, which will fail with an Errno::EACCES (Permission denied @ apply2files). This commit changes it so that both the Closer and Remover finalizers are defined on the finalizer_obj, which guarantees the order that it is ran. https://github.com/ruby/tempfile/commit/eb2d8b1175 --- lib/tempfile.rb | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/tempfile.rb b/lib/tempfile.rb index 51c105d591..52a8d6cc9d 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -228,22 +228,25 @@ class Tempfile < DelegateClass(File) tmpfile = File.open(tmpname, @mode, **opts) @opts = opts.freeze end - ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(tmpfile.path)) - ObjectSpace.define_finalizer(self, Closer.new(tmpfile)) super(tmpfile) + + define_finalizers + end + + private def define_finalizers + ObjectSpace.define_finalizer(@finalizer_obj, Closer.new(__getobj__)) + ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(__getobj__.path)) end def initialize_dup(other) # :nodoc: initialize_copy_iv(other) super(other) - ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end def initialize_clone(other) # :nodoc: initialize_copy_iv(other) super(other) - ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) end private def initialize_copy_iv(other) # :nodoc: @@ -256,10 +259,13 @@ class Tempfile < DelegateClass(File) # Opens or reopens the file with mode "r+". def open _close - ObjectSpace.undefine_finalizer(self) + mode = @mode & ~(File::CREAT|File::EXCL) __setobj__(File.open(__getobj__.path, mode, **@opts)) - ObjectSpace.define_finalizer(self, Closer.new(__getobj__)) + + ObjectSpace.undefine_finalizer(@finalizer_obj) + define_finalizers + __getobj__ end @@ -327,7 +333,10 @@ class Tempfile < DelegateClass(File) # may not be able to unlink on Windows; just ignore return end + ObjectSpace.undefine_finalizer(@finalizer_obj) + ObjectSpace.define_finalizer(@finalizer_obj, Closer.new(__getobj__)) + @unlinked = true end alias delete unlink -- cgit v1.2.3