Class: Garcon::ReadWriteLock
- Defined in:
- lib/garcon/task/read_write_lock.rb
Overview
Do not try to acquire the write lock while already holding a read lock or try to acquire the write lock while you already have it. This will lead to deadlock
read-write Lock for cooking safety.
Allows any number of concurrent readers, but only one concurrent writer (And if the “write” lock is taken, any readers who come along will have to wait). If readers are already active when a writer comes along, the writer will wait for all the readers to finish before going ahead. Any additional readers that come when the writer is already waiting, will also wait (so writers are not starved).
Instance Method Summary collapse
-
#acquire_read_lock ⇒ Boolean
Acquire a read lock.
-
#acquire_write_lock ⇒ Boolean
Acquire a write lock.
-
#has_waiters? ⇒ Boolean
Queries whether any threads are waiting to acquire the read or write lock.
-
#initialize ⇒ ReadWriteLock
constructor
Create a new ‘ReadWriteLock` in the unlocked state.
-
#release_read_lock ⇒ Boolean
Release a previously acquired read lock.
-
#release_write_lock ⇒ Boolean
Release a previously acquired write lock.
-
#to_s ⇒ Object
Returns a string representing obj.
-
#with_read_lock { ... } ⇒ Object
Execute a block operation within a read lock.
-
#with_write_lock { ... } ⇒ Object
Execute a block operation within a write lock.
-
#write_locked? ⇒ Boolean
Queries if the write lock is held by any thread.
Constructor Details
#initialize ⇒ ReadWriteLock
Create a new ‘ReadWriteLock` in the unlocked state.
72 73 74 75 76 77 78 |
# File 'lib/garcon/task/read_write_lock.rb', line 72 def initialize @counter = AtomicMutex.new(0) # represents lock state @reader_q = ConditionVariable.new # queue for waiting readers @reader_mutex = Mutex.new # to protect reader queue @writer_q = ConditionVariable.new # queue for waiting writers @writer_mutex = Mutex.new # to protect writer queue end |
Instance Method Details
#acquire_read_lock ⇒ Boolean
Acquire a read lock. If a write lock has been acquired will block until it is released. Will not block if other read locks have been acquired.
134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/garcon/task/read_write_lock.rb', line 134 def acquire_read_lock while(true) c = @counter.value raise ResourceLimitError, 'Too many reader threads' if max_readers?(c) if waiting_writer?(c) @reader_mutex.synchronize do @reader_q.wait(@reader_mutex) if waiting_writer? end while(true) c = @counter.value if running_writer?(c) @reader_mutex.synchronize do @reader_q.wait(@reader_mutex) if running_writer? end else return if @counter.compare_and_swap(c,c+1) end end else break if @counter.compare_and_swap(c,c+1) end end true end |
#acquire_write_lock ⇒ Boolean
Acquire a write lock. Will block and wait for all active readers and writers.
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/garcon/task/read_write_lock.rb', line 188 def acquire_write_lock while(true) c = @counter.value raise ResourceLimitError, 'Too many writer threads' if max_writers?(c) if c == 0 break if @counter.compare_and_swap(0,RUNNING_WRITER) elsif @counter.compare_and_swap(c,c+WAITING_WRITER) while(true) @writer_mutex.synchronize do c = @counter.value if running_writer?(c) || running_readers?(c) @writer_q.wait(@writer_mutex) end end c = @counter.value break if !running_writer?(c) && !running_readers?(c) && @counter.compare_and_swap(c,c+RUNNING_WRITER-WAITING_WRITER) end break end end true end |
#has_waiters? ⇒ Boolean
Queries whether any threads are waiting to acquire the read or write lock.
263 264 265 |
# File 'lib/garcon/task/read_write_lock.rb', line 263 def has_waiters? waiting_writer?(@counter.value) end |
#release_read_lock ⇒ Boolean
Release a previously acquired read lock.
166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/garcon/task/read_write_lock.rb', line 166 def release_read_lock while(true) c = @counter.value if @counter.compare_and_swap(c,c-1) if waiting_writer?(c) && running_readers(c) == 1 @writer_mutex.synchronize { @writer_q.signal } end break end end true end |
#release_write_lock ⇒ Boolean
Release a previously acquired write lock.
219 220 221 222 223 224 225 226 227 228 229 230 231 |
# File 'lib/garcon/task/read_write_lock.rb', line 219 def release_write_lock while(true) c = @counter.value if @counter.compare_and_swap(c,c-RUNNING_WRITER) @reader_mutex.synchronize { @reader_q.broadcast } if waiting_writers(c) > 0 @writer_mutex.synchronize { @writer_q.signal } end break end end true end |
#to_s ⇒ Object
Returns a string representing obj. Includes the current reader and writer counts.
236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/garcon/task/read_write_lock.rb', line 236 def to_s c = @counter.value s = if running_writer?(c) "1 writer running, " elsif running_readers(c) > 0 "#{running_readers(c)} readers running, " else "" end "#<ReadWriteLock:#{object_id.to_s(16)} #{s}#{waiting_writers(c)} writers waiting>" end |
#with_read_lock { ... } ⇒ Object
Execute a block operation within a read lock.
93 94 95 96 97 98 99 100 101 |
# File 'lib/garcon/task/read_write_lock.rb', line 93 def with_read_lock raise ArgumentError, 'no block given' unless block_given? acquire_read_lock begin yield ensure release_read_lock end end |
#with_write_lock { ... } ⇒ Object
Execute a block operation within a write lock.
115 116 117 118 119 120 121 122 123 |
# File 'lib/garcon/task/read_write_lock.rb', line 115 def with_write_lock raise ArgumentError, 'no block given' unless block_given? acquire_write_lock begin yield ensure release_write_lock end end |
#write_locked? ⇒ Boolean
Queries if the write lock is held by any thread.
254 255 256 |
# File 'lib/garcon/task/read_write_lock.rb', line 254 def write_locked? @counter.value >= RUNNING_WRITER end |