Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do ruby exceptions cause mutices to unlock?

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?

like image 774
Stephen Wattam Avatar asked Sep 30 '11 17:09

Stephen Wattam


1 Answers

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.)

like image 59
Phrogz Avatar answered Oct 11 '22 13:10

Phrogz