class RWLock

This class provides a simple readers-writer lock (RWLock). A RWLock allows multiple “readers” to access a resource simultaneously but “writers” must have exclusive access. In other words, during a “write” only a single thread may have access to the resource.

This RWLock prevents writer starvation. While there are no hard guarantees, if a writer requests access it will generally get access as soon as all current readers are finished.

When the current writer is finished, waiting readers have first opportunity to grab the lock. This should result in no starvation of either readers nor writers, although access may not be “fair”.

Public Class Methods

new(max_size = 10) click to toggle source

Creates new readers-writers lock. The ‘max_size` argument limits how many readers may read simultaneously. A limit is necessary to prevent writer starvation.

# File lib/rwlock.rb, line 24
def initialize max_size = 10
  @write_lock = Mutex.new
  @q = SizedQueue.new(max_size)
end

Public Instance Methods

max() click to toggle source

Returns the set maximum number of simultaneous readers.

# File lib/rwlock.rb, line 84
def max
  @q.max
end
read_sync() { || ... } click to toggle source

Obtains reading lock and executes block, then releases reading lock. Many calls to RWLock#read_sync may execute in parallel, but wil not overlap with calls to RWLock#write_sync.

If the number of readers is currently at the maximum or a write operation is in progress, this method will wait. When a reading spot is available and no write operations are occurring, then the block will be executed.

rwl = RWLock.new
a = [1, 2, 3]

Thread.new do
  rwl.read_sync do
    puts a[1]
  end
end
# File lib/rwlock.rb, line 45
def read_sync
  @q.push true
  yield
ensure
  @q.pop
end
write_sync() { || ... } click to toggle source

Obtains writing lock and executes block, then releases writing lock. The block will have exclusive access to the lock, with no readers or other writers allowed to execute at the same time.

If any readers are executing, the method will wait until the current readers are finished then the block will be executed.

If another writer is executing, the method will wait until the current writer is finished. However, readers have first chance at access after a write.

rwl = RWLock.new
a = [1, 2, 3]

Thread.new do
  rwl.write_sync do
    a[1] += 1
  end
end
# File lib/rwlock.rb, line 71
def write_sync
  @write_lock.synchronize do
    @q.max.times { @q.push true }

    begin
      yield
    ensure
      @q.clear
    end
  end
end