diff options
Diffstat (limited to 'lib/tempfile.rb')
-rw-r--r-- | lib/tempfile.rb | 81 |
1 files changed, 52 insertions, 29 deletions
diff --git a/lib/tempfile.rb b/lib/tempfile.rb index fd03b2ccc8..e08cd7c6a4 100644 --- a/lib/tempfile.rb +++ b/lib/tempfile.rb @@ -4,28 +4,37 @@ # The class for temporary files. # o creates a temporary file, which name is "basename.pid.n" with mode "w+". # o Tempfile objects can be used like IO object. -# o with tempfile.close(true) created temporary files are removed. -# o created files are also removed on script termination. +# o the temporary directory is determined by ENV['TMPDIR'], ENV['TMP'], +# ENV['TEMP'] and /tmp, in the order named. +# o when $SAFE > 0, you should specify a directory via the second argument +# of Tempfile::new(), or it will end up finding an ENV value tainted and +# pick /tmp. In case you don't have it, an exception will be raised. +# o tempfile.close(true) gets the temporary file removed immediately. +# o otherwise, the removal is delayed until the object is finalized. # o with Tempfile#open, you can reopen the temporary file. -# o file mode of the temporary files are 0600. +# o file mode of the temporary files is 0600. +# o this library is (considered to be) thread safe. require 'delegate' class Tempfile < SimpleDelegator Max_try = 10 + @@cleanlist = [] - def Tempfile.callback(path, data) + def Tempfile.callback(data) pid = $$ lambda{ if pid == $$ + path, tmpfile, cleanlist = *data + print "removing ", path, "..." if $DEBUG - data[0].close if data[0] - if File.exist?(path) - File.unlink(path) - end - if File.exist?(path + '.lock') - Dir.rmdir(path + '.lock') - end + + tmpfile.close if tmpfile + + # keep this order for thread safeness + File.unlink(path) if File.exist?(path) + cleanlist.delete(path) if cleanlist + print "done\n" if $DEBUG end } @@ -35,30 +44,44 @@ class Tempfile < SimpleDelegator if $SAFE > 0 and tmpdir.tainted? tmpdir = '/tmp' end - n = 0 - while true + + lock = nil + n = failure = 0 + + begin + Thread.critical = true + begin tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n) lock = tmpname + '.lock' - unless File.exist?(tmpname) or File.exist?(lock) - Dir.mkdir(lock) - break - end - rescue - raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try - #sleep(1) - end - n += 1 + n += 1 + end while @@cleanlist.include?(tmpname) or + File.exist?(lock) or File.exist?(tmpname) + + Dir.mkdir(lock) + rescue + failure += 1 + retry if failure < Max_try + raise "cannot generate tempfile `%s'" % tmpname + ensure + Thread.critical = false end - @protect = [] - @clean_files = Tempfile.callback(tmpname, @protect) - ObjectSpace.define_finalizer(self, @clean_files) + @data = [tmpname] + @clean_proc = Tempfile.callback(@data) + ObjectSpace.define_finalizer(self, @clean_proc) @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600) - @protect[0] = @tmpfile @tmpname = tmpname + @@cleanlist << @tmpname + @data[1] = @tmpfile + @data[2] = @@cleanlist + super(@tmpfile) + + # Now we have all the File/IO methods defined, you must not + # carelessly put bare puts(), etc. after this. + Dir.rmdir(lock) end @@ -69,15 +92,15 @@ class Tempfile < SimpleDelegator def open @tmpfile.close if @tmpfile @tmpfile = File.open(@tmpname, 'r+') - @protect[0] = @tmpfile + @data[1] = @tmpfile __setobj__(@tmpfile) end def close(real=false) @tmpfile.close if @tmpfile - @protect[0] = @tmpfile = nil + @data[1] = @tmpfile = nil if real - @clean_files.call + @clean_proc.call ObjectSpace.undefine_finalizer(self) end end |