* 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
This commit is contained in:
knu 2002-11-17 18:01:17 +00:00
parent e6251100cc
commit 34413718ac
2 changed files with 67 additions and 29 deletions

View File

@ -1,3 +1,18 @@
Mon Nov 18 02:13:36 2002 Akinori MUSHA <knu@iDaemons.org>
* 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 <nobu.nokada@softhome.net> Sun Nov 17 22:57:31 2002 Nobuyoshi Nakada <nobu.nokada@softhome.net>
* parse.y (dsym): garbage returned. (ruby-bugs-ja:PR#358) * parse.y (dsym): garbage returned. (ruby-bugs-ja:PR#358)

View File

@ -4,28 +4,37 @@
# The class for temporary files. # The class for temporary files.
# o creates a temporary file, which name is "basename.pid.n" with mode "w+". # o creates a temporary file, which name is "basename.pid.n" with mode "w+".
# o Tempfile objects can be used like IO object. # o Tempfile objects can be used like IO object.
# o with tempfile.close(true) created temporary files are removed. # o the temporary directory is determined by ENV['TMPDIR'], ENV['TMP'],
# o created files are also removed on script termination. # 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 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' require 'delegate'
class Tempfile < SimpleDelegator class Tempfile < SimpleDelegator
Max_try = 10 Max_try = 10
@@cleanlist = []
def Tempfile.callback(path, data) def Tempfile.callback(data)
pid = $$ pid = $$
lambda{ lambda{
if pid == $$ if pid == $$
path, tmpfile, cleanlist = *data
print "removing ", path, "..." if $DEBUG print "removing ", path, "..." if $DEBUG
data[0].close if data[0]
if File.exist?(path) tmpfile.close if tmpfile
File.unlink(path)
end # keep this order for thread safeness
if File.exist?(path + '.lock') File.unlink(path) if File.exist?(path)
Dir.rmdir(path + '.lock') cleanlist.delete(path) if cleanlist
end
print "done\n" if $DEBUG print "done\n" if $DEBUG
end end
} }
@ -35,30 +44,44 @@ class Tempfile < SimpleDelegator
if $SAFE > 0 and tmpdir.tainted? if $SAFE > 0 and tmpdir.tainted?
tmpdir = '/tmp' tmpdir = '/tmp'
end end
n = 0
while true lock = nil
n = failure = 0
begin
Thread.critical = true
begin begin
tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n) tmpname = sprintf('%s/%s%d.%d', tmpdir, basename, $$, n)
lock = tmpname + '.lock' lock = tmpname + '.lock'
unless File.exist?(tmpname) or File.exist?(lock) n += 1
Dir.mkdir(lock) end while @@cleanlist.include?(tmpname) or
break File.exist?(lock) or File.exist?(tmpname)
end
rescue Dir.mkdir(lock)
raise "cannot generate tempfile `%s'" % tmpname if n >= Max_try rescue
#sleep(1) failure += 1
end retry if failure < Max_try
n += 1 raise "cannot generate tempfile `%s'" % tmpname
ensure
Thread.critical = false
end end
@protect = [] @data = [tmpname]
@clean_files = Tempfile.callback(tmpname, @protect) @clean_proc = Tempfile.callback(@data)
ObjectSpace.define_finalizer(self, @clean_files) ObjectSpace.define_finalizer(self, @clean_proc)
@tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600) @tmpfile = File.open(tmpname, File::RDWR|File::CREAT|File::EXCL, 0600)
@protect[0] = @tmpfile
@tmpname = tmpname @tmpname = tmpname
@@cleanlist << @tmpname
@data[1] = @tmpfile
@data[2] = @@cleanlist
super(@tmpfile) super(@tmpfile)
# Now we have all the File/IO methods defined, you must not
# carelessly put bare puts(), etc. after this.
Dir.rmdir(lock) Dir.rmdir(lock)
end end
@ -69,15 +92,15 @@ class Tempfile < SimpleDelegator
def open def open
@tmpfile.close if @tmpfile @tmpfile.close if @tmpfile
@tmpfile = File.open(@tmpname, 'r+') @tmpfile = File.open(@tmpname, 'r+')
@protect[0] = @tmpfile @data[1] = @tmpfile
__setobj__(@tmpfile) __setobj__(@tmpfile)
end end
def close(real=false) def close(real=false)
@tmpfile.close if @tmpfile @tmpfile.close if @tmpfile
@protect[0] = @tmpfile = nil @data[1] = @tmpfile = nil
if real if real
@clean_files.call @clean_proc.call
ObjectSpace.undefine_finalizer(self) ObjectSpace.undefine_finalizer(self)
end end
end end