summaryrefslogtreecommitdiff
path: root/lib/tempfile.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/tempfile.rb')
-rw-r--r--lib/tempfile.rb86
1 files changed, 52 insertions, 34 deletions
diff --git a/lib/tempfile.rb b/lib/tempfile.rb
index 51c105d591..cd512bb1c5 100644
--- a/lib/tempfile.rb
+++ b/lib/tempfile.rb
@@ -29,7 +29,7 @@ require 'tmpdir'
# require 'tempfile'
#
# # Tempfile.create with a block
-# # The filename are choosen automatically.
+# # The filename are chosen automatically.
# # (You can specify the prefix and suffix of the filename by an optional argument.)
# Tempfile.create {|f|
# f.puts "foo"
@@ -157,7 +157,7 @@ require 'tmpdir'
class Tempfile < DelegateClass(File)
# The version
- VERSION = "0.2.1"
+ VERSION = "0.3.1"
# Creates a file in the underlying file system;
# returns a new \Tempfile object based on that file.
@@ -221,45 +221,47 @@ 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
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)
+
+ @finalizer_manager = FinalizerManager.new(__getobj__.path)
+ @finalizer_manager.register(self, __getobj__)
end
def initialize_dup(other) # :nodoc:
initialize_copy_iv(other)
super(other)
- ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
+ @finalizer_manager.register(self, __getobj__)
end
def initialize_clone(other) # :nodoc:
initialize_copy_iv(other)
super(other)
- ObjectSpace.define_finalizer(self, Closer.new(__getobj__))
+ @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+".
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__))
+
+ @finalizer_manager.register(self, __getobj__)
+
__getobj__
end
@@ -327,7 +329,9 @@ class Tempfile < DelegateClass(File)
# may not be able to unlink on Windows; just ignore
return
end
- ObjectSpace.undefine_finalizer(@finalizer_obj)
+
+ @finalizer_manager.unlinked = true
+
@unlinked = true
end
alias delete unlink
@@ -361,35 +365,35 @@ class Tempfile < DelegateClass(File)
protected
- attr_reader :unlinked, :mode, :opts, :finalizer_obj
+ attr_reader :unlinked, :mode, :opts, :finalizer_manager
- class Closer # :nodoc:
- def initialize(tmpfile)
- @tmpfile = tmpfile
- end
-
- def call(*args)
- @tmpfile.close
- end
- end
+ class FinalizerManager # :nodoc:
+ attr_accessor :unlinked
- class Remover # :nodoc:
def initialize(path)
- @pid = Process.pid
+ @open_files = {}
@path = path
+ @pid = Process.pid
+ @unlinked = false
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? && !@unlinked && 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
@@ -546,8 +550,8 @@ end
#
# Implementation note:
#
-# The keyword argument +anonymous=true+ is implemented using FILE_SHARE_DELETE on Windows.
-# O_TMPFILE is used on Linux.
+# The keyword argument <tt>anonymous=true</tt> is implemented using +FILE_SHARE_DELETE+ on Windows.
+# +O_TMPFILE+ is used on Linux.
#
# Related: Tempfile.new.
#
@@ -560,6 +564,8 @@ def Tempfile.create(basename="", tmpdir=nil, mode: 0, anonymous: false, **option
end
class << Tempfile
+# :stopdoc:
+
private def create_with_filename(basename="", tmpdir=nil, mode: 0, **options)
tmpfile = nil
Dir::Tmpname.create(basename, tmpdir, **options) do |tmpname, n, opts|
@@ -589,6 +595,16 @@ private def create_with_filename(basename="", tmpdir=nil, mode: 0, **options)
end
end
+if RUBY_VERSION < "3.2"
+ module PathAttr # :nodoc:
+ attr_reader :path
+
+ def self.set_path(file, path)
+ file.extend(self).instance_variable_set(:@path, path)
+ end
+ end
+end
+
private def create_anonymous(basename="", tmpdir=nil, mode: 0, **options, &block)
tmpfile = nil
tmpdir = Dir.tmpdir() if tmpdir.nil?
@@ -604,12 +620,14 @@ private def create_anonymous(basename="", tmpdir=nil, mode: 0, **options, &block
mode |= File::SHARE_DELETE | File::BINARY # Windows needs them to unlink the opened file.
tmpfile = create_with_filename(basename, tmpdir, mode: mode, **options)
File.unlink(tmpfile.path)
+ tmppath = tmpfile.path
end
path = File.join(tmpdir, '')
- if tmpfile.path != path
+ unless tmppath == path
# clear path.
tmpfile.autoclose = false
tmpfile = File.new(tmpfile.fileno, mode: File::RDWR, path: path)
+ PathAttr.set_path(tmpfile, path) if defined?(PathAttr)
end
if block
begin