@@ -108,23 +108,23 @@ def with_write_lock
108
108
def acquire_read_lock
109
109
while ( true )
110
110
c = @counter . value
111
- raise ResourceLimitError . new ( 'Too many reader threads' ) if ( c & MAX_READERS ) == MAX_READERS
111
+ raise ResourceLimitError . new ( 'Too many reader threads' ) if max_readers? ( c )
112
112
113
113
# If a writer is waiting when we first queue up, we need to wait
114
- if c >= WAITING_WRITER
114
+ if waiting_writer? ( c )
115
115
# But it is possible that the writer could finish and decrement @counter right here...
116
116
@reader_mutex . synchronize do
117
117
# So check again inside the synchronized section
118
- @reader_q . wait ( @reader_mutex ) if @counter . value >= WAITING_WRITER
118
+ @reader_q . wait ( @reader_mutex ) if waiting_writer?
119
119
end
120
120
121
121
# after a reader has waited once, they are allowed to "barge" ahead of waiting writers
122
122
# but if a writer is *running*, the reader still needs to wait (naturally)
123
123
while ( true )
124
124
c = @counter . value
125
- if c >= RUNNING_WRITER
125
+ if running_writer? ( c )
126
126
@reader_mutex . synchronize do
127
- @reader_q . wait ( @reader_mutex ) if @counter . value >= RUNNING_WRITER
127
+ @reader_q . wait ( @reader_mutex ) if running_writer?
128
128
end
129
129
else
130
130
return if @counter . compare_and_swap ( c , c +1 )
@@ -145,7 +145,7 @@ def release_read_lock
145
145
c = @counter . value
146
146
if @counter . compare_and_swap ( c , c -1 )
147
147
# If one or more writers were waiting, and we were the last reader, wake a writer up
148
- if c >= WAITING_WRITER && ( c & MAX_READERS ) == 1
148
+ if waiting_writer? ( c ) && running_readers ( c ) == 1
149
149
@writer_mutex . synchronize { @writer_q . signal }
150
150
end
151
151
break
@@ -163,7 +163,7 @@ def release_read_lock
163
163
def acquire_write_lock
164
164
while ( true )
165
165
c = @counter . value
166
- raise ResourceLimitError . new ( 'Too many writer threads' ) if ( c & MAX_WRITERS ) == MAX_WRITERS
166
+ raise ResourceLimitError . new ( 'Too many writer threads' ) if max_writers? ( c )
167
167
168
168
if c == 0 # no readers OR writers running
169
169
# if we successfully swap the RUNNING_WRITER bit on, then we can go ahead
@@ -177,7 +177,7 @@ def acquire_write_lock
177
177
# So we have to do another check inside the synchronized section
178
178
# If a writer OR reader is running, then go to sleep
179
179
c = @counter . value
180
- @writer_q . wait ( @writer_mutex ) if ( c >= RUNNING_WRITER ) || ( ( c & MAX_READERS ) > 0 )
180
+ @writer_q . wait ( @writer_mutex ) if running_writer? ( c ) || running_readers? ( c )
181
181
end
182
182
183
183
# We just came out of a wait
@@ -203,7 +203,7 @@ def release_write_lock
203
203
c = @counter . value
204
204
if @counter . compare_and_swap ( c , c -RUNNING_WRITER )
205
205
@reader_mutex . synchronize { @reader_q . broadcast }
206
- if ( c & MAX_WRITERS ) > 0 # if any writers are waiting...
206
+ if waiting_writers ( c ) > 0 # if any writers are waiting...
207
207
@writer_mutex . synchronize { @writer_q . signal }
208
208
end
209
209
break
@@ -216,15 +216,66 @@ def release_write_lock
216
216
# writer counts.
217
217
def to_s
218
218
c = @counter . value
219
- s = if c >= RUNNING_WRITER
219
+ s = if running_writer? ( c )
220
220
"1 writer running, "
221
- elsif ( c & MAX_READERS ) > 0
222
- "#{ c & MAX_READERS } readers running, "
221
+ elsif running_readers ( c ) > 0
222
+ "#{ running_readers ( c ) } readers running, "
223
223
else
224
224
""
225
225
end
226
226
227
- "#<ReadWriteLock:#{ object_id . to_s ( 16 ) } #{ s } #{ ( c & MAX_WRITERS ) / WAITING_WRITER } writers waiting>"
227
+ "#<ReadWriteLock:#{ object_id . to_s ( 16 ) } #{ s } #{ waiting_writers ( c ) } writers waiting>"
228
+ end
229
+
230
+ # Queries if the write lock is held by any thread.
231
+ #
232
+ # @return [Boolean] true if the write lock is held else false`
233
+ def write_locked?
234
+ @counter . value >= RUNNING_WRITER
235
+ end
236
+
237
+ # Queries whether any threads are waiting to acquire the read or write lock.
238
+ #
239
+ # @return [Boolean] true if any threads are waiting for a lock else false
240
+ def has_waiters?
241
+ waiting_writer? ( @counter . value )
242
+ end
243
+
244
+ private
245
+
246
+ # @!visibility private
247
+ def running_readers ( c )
248
+ c & MAX_READERS
249
+ end
250
+
251
+ # @!visibility private
252
+ def running_readers? ( c )
253
+ ( c & MAX_READERS ) > 0
254
+ end
255
+
256
+ # @!visibility private
257
+ def running_writer? ( c = @counter . value )
258
+ c >= RUNNING_WRITER
259
+ end
260
+
261
+ # @!visibility private
262
+ def waiting_writers ( c )
263
+ ( c & MAX_WRITERS ) / WAITING_WRITER
264
+ end
265
+
266
+ # @!visibility private
267
+ def waiting_writer? ( c = @counter . value )
268
+ c >= WAITING_WRITER
269
+ end
270
+
271
+ # @!visibility private
272
+ def max_readers? ( c )
273
+ ( c & MAX_READERS ) == MAX_READERS
274
+ end
275
+
276
+ # @!visibility private
277
+ def max_writers? ( c )
278
+ ( c & MAX_WRITERS ) == MAX_WRITERS
228
279
end
229
280
end
230
281
end
0 commit comments