Mutex confusion

I’m a little confused by Mutex’s behaviour. It doesn’t seem right that a
single thread should be able to block itself:

[email protected]:/tmp$ cat thread-block.rb
require ‘thread’

m = Mutex.new

m.synchronize do
m.synchronize do
puts “Woo!”
end
end

[email protected]:/tmp$ ruby thread-block.rb
thread-block.rb:6:in synchronize': stopping only thread (ThreadError) note: use sleep to stop forever from thread-block.rb:6 from thread-block.rb:5:insynchronize’
from thread-block.rb:5

Why does it behave like this? Is it traditional for mutexes (mutices?)
to be designed this way?


Alex

Alex Y. wrote:

puts "Woo!"

Why does it behave like this? Is it traditional for mutexes (mutices?)
to be designed this way?


Alex

As far as “traditional” behavior, it can go either way. Sometimes
counting semaphores are used, which may be acquired multiple times and
must be released a corresponding number of times. POSIX threads, for
example, provide both options. In this case, however, it is just a
binary semaphore: either it is locked or not. The Mutex#synchronize call
checks if the lock is available. If not (even it is the current thread
that locked it), it blocks, as you noticed. That is its defined behavior
in Ruby.

-Justin

Justin C. wrote:

Alex Y. wrote:

puts "Woo!"

Why does it behave like this? Is it traditional for mutexes (mutices?)
to be designed this way?


Alex

As far as “traditional” behavior, it can go either way. Sometimes
counting semaphores are used, which may be acquired multiple times and
must be released a corresponding number of times. POSIX threads, for
example, provide both options. In this case, however, it is just a
binary semaphore: either it is locked or not. The Mutex#synchronize call
checks if the lock is available. If not (even it is the current thread
that locked it), it blocks, as you noticed. That is its defined behavior
in Ruby.

What would be broken if, hypothetically, the mutex behaviour were
changed not to block if the current thread already holds the lock? Would
any possible implementation introduce a race condition?

For a bit of background, this came up while writing a (fairly complex)
Capistrano recipe. Capistrano uses #set and #fetch methods to allow the
user to give lazily evaluated settings, like this:

set(:foo){File.join(fetch(:deploy_to),"foo")}
set(:deploy_to){"/tmp"}
puts fetch(:foo) # <-- this is where the previous two blocks get 

called

#set and #fetch both protect the inner variable in the same mutex (per
setting key, that is). This bit me when I was trying to set up a lazily
evaluated hash, like this:

def add_to_settings(key,&val)
  set(:hsh, {}) unless exists?(:hsh)
  set(:hsh) do
    fetch(:hsh).merge(key => val.call)
  end
end

The idea is that I’d build up a stack of procs which would be called
when, some time later, I called fetch(:hsh), to return a fully merged
hash. Unfortunately, because the inner fetch tries to synchronize on the
same mutex that the outer set already holds, this deadlocks. I’ve had to
sidestep set and fetch and forego any thread safety because of this, and
so far it hasn’t been a problem.

Given that I’ve got a workaround, it’s more interesting than annoying,
but I’m intrigued by the design decision.


Alex

Alex Y. wrote:

What would be broken if, hypothetically, the mutex behaviour were
changed not to block if the current thread already holds the lock? Would
any possible implementation introduce a race condition?

I’m just learning multi-threading with Ruby, don’t like what I see,
anyways Ruby
doesn’t support re-entrance with Mutex, it should but doesn’t. There
would be no
adverse effect regrading re-entrance. A thread that owns the mutex
should never
be blocked, because its thread safe. Other threads would behave just
like they
do now, they would block on the mutex until it was released by the
owning
thread. There would be no racing condition, since the code is thread
safe.


Alex


Kind Regards,
Rajinder Y.

http://DevMentor.org
Do Good ~ Share Freely

On 21.10.2009 08:02, Alex Y. wrote:

counting semaphores are used, which may be acquired multiple times and
must be released a corresponding number of times. POSIX threads, for
example, provide both options. In this case, however, it is just a
binary semaphore: either it is locked or not. The Mutex#synchronize call
checks if the lock is available. If not (even it is the current thread
that locked it), it blocks, as you noticed. That is its defined behavior
in Ruby.

What would be broken if, hypothetically, the mutex behaviour were
changed not to block if the current thread already holds the lock? Would
any possible implementation introduce a race condition?

You want Monitor or MonitorMixin then. Mutex is just not implemented
reentrant (for efficiency reasons I believe), that’s all.

$ ruby19 -r monitor -e ‘[Monitor,Mutex].each {|m|x=m.new;x.synchronize
{x.synchronize {puts m}}}’
Monitor
internal:prelude:6:in lock': deadlock; recursive locking (ThreadError) from <internal:prelude>:6:insynchronize’
from -e:1:in block (2 levels) in <main>' from <internal:prelude>:8:insynchronize’
from -e:1:in block in <main>' from -e:1:ineach’
from -e:1:in `’

[email protected] ~
$

Given that I’ve got a workaround, it’s more interesting than annoying,
but I’m intrigued by the design decision.

See above.

Kind regards

robert

Robert K. wrote:

On 21.10.2009 08:02, Alex Y. wrote:

What would be broken if, hypothetically, the mutex behaviour were
changed not to block if the current thread already holds the lock?
Would any possible implementation introduce a race condition?

You want Monitor or MonitorMixin then. Mutex is just not implemented
reentrant (for efficiency reasons I believe), that’s all.

I need to find a better source for Ruby multi-threading, did not know
about
monitors. Great I picked up something new today!

Kind regards

robert


Kind Regards,
Rajinder Y.

http://DevMentor.org
Do Good ~ Share Freely

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs