How do ruby exceptions cause mutices to unlock?
Asked Answered
B

1

6

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?

Brae answered 30/9, 2011 at 17:53 Comment(2)
This is an interesting observation. However...what's your question? "How does this happen" in Ruby internal implementation? Why does this happen? Is it intended or a bug?Probation
I'm curious as to quite how it's implemented, but the main issue is the cause, which I believe you have explained with much eloquence --- thanks.Brae
P
4

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

Probation answered 30/9, 2011 at 18:15 Comment(2)
Intriguing. Inserting a sleep between rescue and end illustrates that nicely. The notion of a thread releasing its locks is quite within my expectations, I guess I never expected ruby to be so clever.Brae
You can see it in source code: thread.c:338Githens

© 2022 - 2024 — McMap. All rights reserved.