Recently, I have been working with Ruby's threads, and have uncovered a slightly unexpected behaviour. In a critical section, calling raise causes the mutex to release. I could expect this of the synchronize method, with its block, but it also seems to happen when lock and unlock are called separately.
For example, the code below outputs:
$ ruby testmutex.rb
x sync
y sync
...where I'd expect y to be blocked until the heat death of the universe.
m = Mutex.new
x = Thread.new() do
begin
m.lock
puts "x sync"
sleep 5
raise "x err"
sleep 5
m.unlock
rescue
end
end
y = Thread.new() do
sleep 0.5
m.lock
puts "y sync"
m.unlock
end
x.join
y.join
Why is the y thread allowed to run even though the m.unlock in the x thread is never executed?
Note that if you remove the raise and the unlock from x the behavior is the same. So you have a situation where the x thread locks the mutex, and then the thread ends, and the mutex is unlocked.
m = Mutex.new
Thread.new{ m.lock; p m.locked? }.join
#=> true
p m.locked?
#=> false
Thus we see that the situation is unrelated to raise. Because you have a begin/rescue block around your raise, you just exit the x thread 5 seconds earlier than you would have otherwise.
Presumably the interpreter keeps tracks of any mutexes locked by a thread and automatically and intentionally unlocks them when the thread dies. (I cannot back this up with source-code inspection, however. This is just a guess, based on behavior.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With