Skip to content

Commit eb2d8b1

Browse files
committed
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.
1 parent ae896a5 commit eb2d8b1

File tree

1 file changed

+15
-6
lines changed

1 file changed

+15
-6
lines changed

lib/tempfile.rb

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,22 +228,25 @@ def initialize(basename="", tmpdir=nil, mode: 0, **options)
228228
tmpfile = File.open(tmpname, @mode, **opts)
229229
@opts = opts.freeze
230230
end
231-
ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(tmpfile.path))
232-
ObjectSpace.define_finalizer(self, Closer.new(tmpfile))
233231

234232
super(tmpfile)
233+
234+
define_finalizers
235+
end
236+
237+
private def define_finalizers
238+
ObjectSpace.define_finalizer(@finalizer_obj, Closer.new(__getobj__))
239+
ObjectSpace.define_finalizer(@finalizer_obj, Remover.new(__getobj__.path))
235240
end
236241

237242
def initialize_dup(other) # :nodoc:
238243
initialize_copy_iv(other)
239244
super(other)
240-
ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
241245
end
242246

243247
def initialize_clone(other) # :nodoc:
244248
initialize_copy_iv(other)
245249
super(other)
246-
ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
247250
end
248251

249252
private def initialize_copy_iv(other) # :nodoc:
@@ -256,10 +259,13 @@ def initialize_clone(other) # :nodoc:
256259
# Opens or reopens the file with mode "r+".
257260
def open
258261
_close
259-
ObjectSpace.undefine_finalizer(self)
262+
260263
mode = @mode & ~(File::CREAT|File::EXCL)
261264
__setobj__(File.open(__getobj__.path, mode, **@opts))
262-
ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
265+
266+
ObjectSpace.undefine_finalizer(@finalizer_obj)
267+
define_finalizers
268+
263269
__getobj__
264270
end
265271

@@ -327,7 +333,10 @@ def unlink
327333
# may not be able to unlink on Windows; just ignore
328334
return
329335
end
336+
330337
ObjectSpace.undefine_finalizer(@finalizer_obj)
338+
ObjectSpace.define_finalizer(@finalizer_obj, Closer.new(__getobj__))
339+
331340
@unlinked = true
332341
end
333342
alias delete unlink

0 commit comments

Comments
 (0)