summaryrefslogtreecommitdiff
path: root/lib/tempfile.rb
diff options
context:
space:
mode:
authorPeter Zhu <[email protected]>2024-08-20 13:28:11 -0400
committergit <[email protected]>2024-08-20 18:07:42 +0000
commita68331e7036d7ab433778bf65eb854aabd5009c4 (patch)
treee64a0d781c4a332631a49f9fa6066e088b1c92a3 /lib/tempfile.rb
parent41b427a2648ed2e049952450c698be917e0bb125 (diff)
[ruby/tempfile] Add FinalizerManager to manage finalizers
As @jeremyevans pointed out for commit eb2d8b1: > Each Tempfile instance has a separate File instance and file descriptor: > > t = Tempfile.new > t.to_i # => 6 > t.dup.to_i => 7 FinalizerManager will keep track of the open File objects for the particular file and will only unlink the file when all of the File objects have been closed. https://github.com/ruby/tempfile/commit/753ab16642
Diffstat (limited to 'lib/tempfile.rb')
-rw-r--r--lib/tempfile.rb58
1 files changed, 24 insertions, 34 deletions
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index 52a8d6cc9d..98706f7156 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -221,7 +221,6 @@ class Tempfile < DelegateClass(File)
@unlinked = false
@mode = mode|File::RDWR|File::CREAT|File::EXCL
- @finalizer_obj = Object.new
tmpfile = nil
::Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts|
opts[:perm] = 0600
@@ -231,29 +230,27 @@ class Tempfile < DelegateClass(File)
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))
+ @finalizer_manager = FinalizerManager.new(__getobj__.path)
+ @finalizer_manager.register(self, __getobj__)
end
def initialize_dup(other) # :nodoc:
initialize_copy_iv(other)
super(other)
+ @finalizer_manager.register(self, __getobj__)
end
def initialize_clone(other) # :nodoc:
initialize_copy_iv(other)
super(other)
+ @finalizer_manager.register(self, __getobj__)
end
private def initialize_copy_iv(other) # :nodoc:
@unlinked = other.unlinked
@mode = other.mode
@opts = other.opts
- @finalizer_obj = other.finalizer_obj
+ @finalizer_manager = other.finalizer_manager
end
# Opens or reopens the file with mode "r+".
@@ -263,8 +260,7 @@ class Tempfile < DelegateClass(File)
mode = @mode & ~(File::CREAT|File::EXCL)
__setobj__(File.open(__getobj__.path, mode, **@opts))
- ObjectSpace.undefine_finalizer(@finalizer_obj)
- define_finalizers
+ @finalizer_manager.register(self, __getobj__)
__getobj__
end
@@ -334,9 +330,6 @@ class Tempfile < DelegateClass(File)
return
end
- ObjectSpace.undefine_finalizer(@finalizer_obj)
- ObjectSpace.define_finalizer(@finalizer_obj, Closer.new(__getobj__))
-
@unlinked = true
end
alias delete unlink
@@ -370,35 +363,32 @@ class Tempfile < DelegateClass(File)
protected
- attr_reader :unlinked, :mode, :opts, :finalizer_obj
-
- class Closer # :nodoc:
- def initialize(tmpfile)
- @tmpfile = tmpfile
- end
-
- def call(*args)
- @tmpfile.close
- end
- end
+ attr_reader :unlinked, :mode, :opts, :finalizer_manager
- class Remover # :nodoc:
+ class FinalizerManager # :nodoc:
def initialize(path)
- @pid = Process.pid
+ @open_files = {}
@path = path
+ @pid = Process.pid
end
- def call(*args)
- return if @pid != Process.pid
+ def register(obj, file)
+ ObjectSpace.undefine_finalizer(obj)
+ ObjectSpace.define_finalizer(obj, self)
+ @open_files[obj.object_id] = file
+ end
- $stderr.puts "removing #{@path}..." if $DEBUG
+ def call(object_id)
+ @open_files.delete(object_id).close
- begin
- File.unlink(@path)
- rescue Errno::ENOENT
+ if @open_files.empty? && Process.pid == @pid
+ $stderr.puts "removing #{@path}..." if $DEBUG
+ begin
+ File.unlink(@path)
+ rescue Errno::ENOENT
+ end
+ $stderr.puts "done" if $DEBUG
end
-
- $stderr.puts "done" if $DEBUG
end
end