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