Robert Klemme
10/7/2003 8:18:00 AM
"Ara.T.Howard" <ahoward@fsl.noaa.gov> schrieb im Newsbeitrag
news:Pine.LNX.4.53.0310061633240.22940@eli.fsl.noaa.gov...
>
> in the code below, i am able to obtain TWO exclusive locks on files
using the
> following logic:
>
> iff flock(LOCK_EX | LOCK_NB) fails, then open again and retry
>
> this should not be possible should it??
>
> note that the following code SHOULD deadlock - but does not.
>
> CODE:
>
> ----CUT----
> #!/usr/local/ruby-1.8.0/bin/ruby
> threads = []
> flags = (File::LOCK_EX | File::LOCK_NB)
>
> system 'touch a b' rescue nil
>
> threads << Thread.new do
> %w(a b).each do |path|
> fd = open(path)
> #until((ret = fd.flock flags)) # this works (deadlock)
> until((ret = open(path).flock flags)) # this doesn't
> Thread.pass
> end
> printf "0 LOCK_EX %s <%s>\n", path, ret
> sleep 0.5
> end
> end
>
> threads << Thread.new do
> %w(b a).each do |path|
> fd = open(path)
> #until((ret = fd.flock flags)) # this works (deadlock)
> until((ret = open(path).flock flags)) # this doesn't
> Thread.pass
> end
> printf "1 LOCK_EX %s <%s>\n", path, ret
> sleep 0.5
> end
> end
>
> Thread.abort_on_exception = true
> threads.map{|thread| thread.join}
> ----CUT----
>
>
> OUTPUT:
>
> [ahoward@localhost flock]$ ./flock.rb
> 0 LOCK_EX a <0>
> 1 LOCK_EX b <0>
> 0 LOCK_EX b <0>
> 1 LOCK_EX a <0>
> so, two threads are able to obtain exclusive locks on a file? perhaps i
am
> making an obvious mistake??
Since you don't print an unlock message, you don't see when a lock is
released. I guess, when the IO instance that "fd" is bound to, is
finalized, the lock is released, too. And that happens at the end of one
block run since "fd" is local to the block. So, IMHO your test does not
proove multiple locks at the same time.
Reopening the file over and over again (labeled "this doesn't") is bad
practice IMHO since you get a lock on an IO instance that you immediately
forget again ("fd" is not reassigned).
> shouldn't each call to open(path).flock be
> referring to the same open file table entry and, therefore, not affect
the
> thread's ability to obtain an LOCK_EX?
Reopening the same file IMHO creates a new entry, because you have
duplicate state with regard to seek position etc.
Try the implementation below that really deadlocks.
Regards
robert
# basic settings
Thread.abort_on_exception = true
FLAGS = (File::LOCK_EX | File::LOCK_NB)
# ruby "touch"
%w(a b).each {|path| File.open(path, "w").close }
# methods
def wait_for_lock(fd)
until fd.flock(FLAGS)
puts "Thread #{Thread.current["label"]} waiting"
Thread.pass
end
end
def create_thread(label, file1, file2)
Thread.new(label, file1, file2) do |l,a,b|
Thread.current["label"]=l
File.open(a) do |fd_a|
wait_for_lock fd_a
puts "Thread #{l} Locked #{a}"
sleep 0.5 # if removed deadlock disappears because of the timing
File.open(b) do |fd_b|
wait_for_lock fd_b
puts "Thread #{l} Locked #{b}"
sleep 0.5
puts "Thread #{l} About to unlock #{b}"
end
puts "Thread #{l} About to unlock #{a}"
end
end
end
#
# MAIN
#
threads = []
threads << create_thread( "0", "a", "b" )
threads << create_thread( "1", "b", "a" )
threads.each{|t| t.join}