Mutex question / request

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
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öder
http://www.brian-schroeder.de/

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öder
http://www.brian-schroeder.de/

Sincerely,
Gennady Bystritsky

···

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