Class: Rex::ReadWriteLock

Inherits:
Object
  • Object
show all
Defined in:
lib/rex/sync/read_write_lock.rb

Overview

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.

Instance Method Summary collapse

Constructor Details

#initializeReadWriteLock

Initializes a reader/writer lock instance.


24
25
26
27
28
29
30
# File 'lib/rex/sync/read_write_lock.rb', line 24

def initialize
  @read_sync_mutex  = Mutex.new
  @write_sync_mutex = Mutex.new
  @exclusive_mutex  = Mutex.new
  @readers          = 0
  @writer           = false
end

Instance Method Details

#lock_readObject

Acquires the read lock for the calling thread.


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/rex/sync/read_write_lock.rb', line 35

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_writeObject

Acquire the exclusive write lock.


114
115
116
117
118
119
120
121
122
123
124
125
126
# File 'lib/rex/sync/read_write_lock.rb', line 114

def lock_write
  write_sync_mutex.lock

  begin
    @writer = true

    exclusive_mutex.lock

    @owner  = Thread.current
  ensure
    write_sync_mutex.unlock
  end
end

#synchronize_readObject

Synchronize a block for read access.


147
148
149
150
151
152
153
154
# File 'lib/rex/sync/read_write_lock.rb', line 147

def synchronize_read
  lock_read
  begin
    yield
  ensure
    unlock_read
  end
end

#synchronize_writeObject

Synchronize a block for write access.


159
160
161
162
163
164
165
166
# File 'lib/rex/sync/read_write_lock.rb', line 159

def synchronize_write
  lock_write
  begin
    yield
  ensure
    unlock_write
  end
end

#unlock_readObject

Releases the read lock for the calling thread.


72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/rex/sync/read_write_lock.rb', line 72

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_writeObject

Release the exclusive write lock.


131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/rex/sync/read_write_lock.rb', line 131

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