From 34413718ac83afe13b7f3f6d2f55f01dc10d2898 Mon Sep 17 00:00:00 2001 From: knu Date: Sun, 17 Nov 2002 18:01:17 +0000 Subject: * lib/tempfile.rb: Make this libary thread safe. * lib/tempfile.rb: Do not pick a name which was once used and is still scheduled for removal. * lib/tempfile.rb: A lock file need not and must not be scheduled for removal. * lib/tempfile.rb: Compare Max_try with the number of mkdir failures instead of the suffix counter. * lib/tempfile.rb: Overall cleanup and add some important notices. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@3051 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 15 +++++++++++ lib/tempfile.rb | 81 ++++++++++++++++++++++++++++++++++++--------------------- 2 files changed, 67 insertions(+), 29 deletions(-) diff --git a/ChangeLog b/ChangeLog index b71fdce628..8a661e7c9c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,18 @@ +Mon Nov 18 02:13:36 2002 Akinori MUSHA + + * lib/tempfile.rb: Make this libary thread safe. + + * lib/tempfile.rb: Do not pick a name which was once used and is + still scheduled for removal. + + * lib/tempfile.rb: A lock file need not and must not be scheduled + for removal. + + * lib/tempfile.rb: Compare Max_try with the number of mkdir + failures instead of the suffix counter. + + * lib/tempfile.rb: Overall cleanup and add some important notices. + Sun Nov 17 22:57:31 2002 Nobuyoshi Nakada * parse.y (dsym): garbage returned. (ruby-bugs-ja:PR#358) 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 -- cgit v1.2.3