class Rex::ReadWriteLock

This class implements a read/write lock synchronization primitive. It is meant to allow for more efficient access to resources that are more often read from than written to and many times can have concurrent reader threads. By allowing the reader threads to lock the resource concurrently rather than serially, a large performance boost can be seen. Acquiring a write lock results in exclusive access to the resource and thereby prevents any read operations during the time that a write lock is acquired. Only one write lock may be acquired at a time.

Public Class Methods

new() click to toggle source

Initializes a reader/writer lock instance.

# File lib/rex/sync/read_write_lock.rb, line 23
def initialize
  @read_sync_mutex  = Mutex.new
  @write_sync_mutex = Mutex.new
  @exclusive_mutex  = Mutex.new
  @readers          = 0
  @writer           = false
end

Public Instance Methods

lock_read() click to toggle source

Acquires the read lock for the calling thread.

# File lib/rex/sync/read_write_lock.rb, line 34
def lock_read
  read_sync_mutex.lock

  begin
    # If there are a non-zero number of readers and a
    # writer is waiting to acquire the exclusive lock,
    # free up the sync mutex temporarily and lock/unlock
    # the exclusive lock.  This is to give the writer
    # thread a chance to acquire the lock and prevents
    # it from being constantly starved.
    if ((@readers > 0) and
        (@writer))
      read_sync_mutex.unlock
      exclusive_mutex.lock
      exclusive_mutex.unlock
      read_sync_mutex.lock
    end

    # Increment the active reader count
    @readers += 1

    # If we now have just one reader, acquire the exclusive
    # lock.  Track the thread owner so that we release the
    # lock from within the same thread context later on.
    if (@readers == 1)
      exclusive_mutex.lock

      @owner = Thread.current
    end
  ensure
    read_sync_mutex.unlock
  end
end
lock_write() click to toggle source

Acquire the exclusive write lock.

# File lib/rex/sync/read_write_lock.rb, line 113
def lock_write
  write_sync_mutex.lock

  begin
    @writer = true

    exclusive_mutex.lock

    @owner  = Thread.current
  ensure
    write_sync_mutex.unlock
  end
end
synchronize_read() { || ... } click to toggle source

Synchronize a block for read access.

# File lib/rex/sync/read_write_lock.rb, line 146
def synchronize_read
  lock_read
  begin
    yield
  ensure
    unlock_read
  end
end
synchronize_write() { || ... } click to toggle source

Synchronize a block for write access.

# File lib/rex/sync/read_write_lock.rb, line 158
def synchronize_write
  lock_write
  begin
    yield
  ensure
    unlock_write
  end
end
unlock_read() click to toggle source

Releases the read lock for the calling thread.

# File lib/rex/sync/read_write_lock.rb, line 71
def unlock_read
  read_sync_mutex.lock

  begin
    unlocked = false

    # Keep looping until we've lost this thread's reader
    # lock
    while (!unlocked)
      # If there are no more readers left after this one
      if (@readers - 1 == 0)
        # If the calling thread is the owner of the exclusive
        # reader lock, then let's release it
        if (Thread.current == @owner)
          @owner = nil

          exclusive_mutex.unlock
        end
      # If there is more than one reader left and this thread is
      # the owner of the exclusive lock, then keep looping so that
      # we can eventually unlock the exclusive mutex in this thread's
      # context
      elsif (Thread.current == @owner)
        read_sync_mutex.unlock

        next
      end

      # Unlocked!
      unlocked = true

      # Decrement the active reader count
      @readers -= 1
    end
  ensure
    read_sync_mutex.unlock
  end
end
unlock_write() click to toggle source

Release the exclusive write lock.

# File lib/rex/sync/read_write_lock.rb, line 130
def unlock_write
  # If the caller is not the owner of the write lock, then someone is
  # doing something broken, let's let them know.
  if (Thread.current != @owner)
    raise RuntimeError, "Non-owner calling thread attempted to release write lock", caller
  end

  # Otherwise, release the exclusive write lock
  @writer = false

  exclusive_mutex.unlock
end