On Jul 25, 2004, at 2:06 PM, Brian Schroeder wrote:

> Hello,
>
> playing around with threads I observed, that it is impossible to lock a
> mutex twice from within the same thread. Coming from a delphi 
> background

Look at monitor.rb (comes in standard Ruby distribution). It has almost 
same API as Mutex, however it was designed to be re-enterable by the 
current owner.

> I'm used to have the possibility to enter a critical section as often 
> as I
> like, as long as I'm in the same thread. That allows for some simpler
> programming, as I can for example allow the user to read from a 
> protected
> accessor while he has aquired the lock for some bigger operation.
>
> To be more specific:
>
> The following is not working:
> === Test 1 ===
> require 'thread'
>
> m = Mutex.new
>
> m.synchronize do
>   puts 'locked level 1'
>   m.synchronize do
>     puts 'level 2'
>   end
> end
> === EOF ===
>
> I'd like to use it for this usecase:
>
> === usecase ===
> class A
>   def initialize
>     @lock = Mutex.new
>   end
>
>   def x
>     result = ''
>     @lock.synchronize do
>       # Do something with result
>     end
>     result
>   end
>
>   def synchronize
>     @lock.synchronize do yield end
>   end
> end
>
> a = A.new
> a.synchronize do
>   # Do something with a
>   puts a.x
>   # Do some more with a
> end
> === EOF ===
>
> I looked into the thread library and came up with some changes that 
> should
> implement the behaviour I want. But my knowledge of ruby threads is 
> quite
> limited, so I'd like your feedback on how good this is. Also I'm
> interested if you think that this is a good idea, and if it should be
> proposed for inclusion in the standard library?
>
> === MultiMutex ===
> class MultiMutex < Mutex
>   def initialize
>     @lockingthread = nil
>     @lockdepth = 0
>     super
>   end
>
>   def lock
>     Thread.critical = true
>     begin
>       if @lockingthread = Thread.current then
>         @lockdepth += 1
>       else
>         while (Thread.critical = true; @lockdepth > 0)
>           @waiting.push Thread.current
>           Thread.stop
>         end
>         @lockdepth = 1
>       end
>     ensure
>       Thread.critical = false
>     end
>     self
>   end
>
>   def unlock
>     return @lockdepth unless @locked
>     Thread.critical = true
>     begin
>       if @lockdepth > 1 then
>         @lockdepth -= 1
>         return @lockdepth
>       else
>         @lockingthread = nil
>         @lockdepth = 0
>         begin
>           t = @waiting.shift
>           t.wakeup if t
>         rescue ThreadError
>           retry
>         end
>       end
>     ensure
>       Thread.critical = false
>     end
>     begin
>       t.run if t
>     rescue ThreadError
>     end
>     @lockdepth
>   end
> end
> === EOF ===
>
> -- 
> Brian Schröäer
> http://www.brian-schroeder.de/
>
>

Sincerely,
Gennady Bystritsky