11require 'thread'
22require 'concurrent/atomic/atomic_reference'
33require 'concurrent/errors'
4+ require 'concurrent/synchronization'
45
56module Concurrent
67
@@ -26,19 +27,19 @@ module Concurrent
2627 # This will lead to deadlock
2728 #
2829 # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock
29- class ReadWriteLock
30+ class ReadWriteLock < Synchronization :: Object
3031
3132 # @!visibility private
32- WAITING_WRITER = 1 << 15
33+ WAITING_WRITER = 1 << 15
3334
3435 # @!visibility private
35- RUNNING_WRITER = 1 << 30
36+ RUNNING_WRITER = 1 << 30
3637
3738 # @!visibility private
38- MAX_READERS = WAITING_WRITER - 1
39+ MAX_READERS = WAITING_WRITER - 1
3940
4041 # @!visibility private
41- MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1
42+ MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1
4243
4344 # Implementation notes:
4445 # A goal is to make the uncontended path for both readers/writers lock-free
@@ -53,11 +54,11 @@ class ReadWriteLock
5354
5455 # Create a new `ReadWriteLock` in the unlocked state.
5556 def initialize
56- @counter = AtomicReference . new ( 0 ) # single integer which represents lock state
57- @reader_q = ConditionVariable . new # queue for waiting readers
58- @reader_mutex = Mutex . new # to protect reader queue
59- @writer_q = ConditionVariable . new # queue for waiting writers
60- @writer_mutex = Mutex . new # to protect writer queue
57+ @Counter = AtomicReference . new ( 0 ) # single integer which represents lock state
58+ @ReadLock = Synchronization :: Lock . new
59+ @WriteLock = Synchronization :: Lock . new
60+ ensure_ivar_visibility!
61+ super ( )
6162 end
6263
6364 # Execute a block operation within a read lock.
@@ -106,47 +107,41 @@ def with_write_lock
106107 # @raise [Concurrent::ResourceLimitError] if the maximum number of readers
107108 # is exceeded.
108109 def acquire_read_lock
109- while ( true )
110- c = @counter . value
110+ while true
111+ c = @Counter . value
111112 raise ResourceLimitError . new ( 'Too many reader threads' ) if max_readers? ( c )
112113
113114 # If a writer is waiting when we first queue up, we need to wait
114115 if waiting_writer? ( c )
115- # But it is possible that the writer could finish and decrement @counter right here...
116- @reader_mutex . synchronize do
117- # So check again inside the synchronized section
118- @reader_q . wait ( @reader_mutex ) if waiting_writer?
119- end
116+ @ReadLock . wait_until { !waiting_writer? }
120117
121118 # after a reader has waited once, they are allowed to "barge" ahead of waiting writers
122119 # but if a writer is *running*, the reader still needs to wait (naturally)
123- while ( true )
124- c = @counter . value
120+ while true
121+ c = @Counter . value
125122 if running_writer? ( c )
126- @reader_mutex . synchronize do
127- @reader_q . wait ( @reader_mutex ) if running_writer?
128- end
123+ @ReadLock . wait_until { !running_writer? }
129124 else
130- return if @counter . compare_and_swap ( c , c +1 )
125+ return if @Counter . compare_and_swap ( c , c +1 )
131126 end
132127 end
133128 else
134- break if @counter . compare_and_swap ( c , c +1 )
129+ break if @Counter . compare_and_swap ( c , c +1 )
135130 end
136- end
131+ end
137132 true
138133 end
139134
140135 # Release a previously acquired read lock.
141136 #
142137 # @return [Boolean] true if the lock is successfully released
143138 def release_read_lock
144- while ( true )
145- c = @counter . value
146- if @counter . compare_and_swap ( c , c -1 )
139+ while true
140+ c = @Counter . value
141+ if @Counter . compare_and_swap ( c , c -1 )
147142 # If one or more writers were waiting, and we were the last reader, wake a writer up
148143 if waiting_writer? ( c ) && running_readers ( c ) == 1
149- @writer_mutex . synchronize { @writer_q . signal }
144+ @WriteLock . signal
150145 end
151146 break
152147 end
@@ -161,32 +156,31 @@ def release_read_lock
161156 # @raise [Concurrent::ResourceLimitError] if the maximum number of writers
162157 # is exceeded.
163158 def acquire_write_lock
164- while ( true )
165- c = @counter . value
159+ while true
160+ c = @Counter . value
166161 raise ResourceLimitError . new ( 'Too many writer threads' ) if max_writers? ( c )
167162
168163 if c == 0 # no readers OR writers running
169164 # if we successfully swap the RUNNING_WRITER bit on, then we can go ahead
170- break if @counter . compare_and_swap ( 0 , RUNNING_WRITER )
171- elsif @counter . compare_and_swap ( c , c +WAITING_WRITER )
172- while ( true )
165+ break if @Counter . compare_and_swap ( 0 , RUNNING_WRITER )
166+ elsif @Counter . compare_and_swap ( c , c +WAITING_WRITER )
167+ while true
173168 # Now we have successfully incremented, so no more readers will be able to increment
174169 # (they will wait instead)
175170 # However, readers OR writers could decrement right here, OR another writer could increment
176- @writer_mutex . synchronize do
171+ @WriteLock . wait_until do
177172 # So we have to do another check inside the synchronized section
178173 # If a writer OR reader is running, then go to sleep
179- c = @counter . value
180- @writer_q . wait ( @writer_mutex ) if running_writer? ( c ) || running_readers? ( c )
174+ c = @Counter . value
175+ ! running_writer? ( c ) && ! running_readers? ( c )
181176 end
182177
183178 # We just came out of a wait
184179 # If we successfully turn the RUNNING_WRITER bit on with an atomic swap,
185180 # Then we are OK to stop waiting and go ahead
186181 # Otherwise go back and wait again
187- c = @counter . value
188- break if !running_writer? ( c ) && !running_readers? ( c ) &&
189- @counter . compare_and_swap ( c , c +RUNNING_WRITER -WAITING_WRITER )
182+ c = @Counter . value
183+ break if !running_writer? ( c ) && !running_readers? ( c ) && @Counter . compare_and_swap ( c , c +RUNNING_WRITER -WAITING_WRITER )
190184 end
191185 break
192186 end
@@ -198,67 +192,60 @@ def acquire_write_lock
198192 #
199193 # @return [Boolean] true if the lock is successfully released
200194 def release_write_lock
201- while ( true )
202- c = @counter . value
203- if @counter . compare_and_swap ( c , c -RUNNING_WRITER )
204- @reader_mutex . synchronize { @reader_q . broadcast }
205- if waiting_writers ( c ) > 0 # if any writers are waiting...
206- @writer_mutex . synchronize { @writer_q . signal }
207- end
208- break
209- end
210- end
195+ c = @Counter . update { |c | c -RUNNING_WRITER }
196+ @ReadLock . broadcast
197+ @WriteLock . signal if waiting_writers ( c ) > 0
211198 true
212199 end
213200
214201 # Queries if the write lock is held by any thread.
215202 #
216203 # @return [Boolean] true if the write lock is held else false`
217204 def write_locked?
218- @counter . value >= RUNNING_WRITER
205+ @Counter . value >= RUNNING_WRITER
219206 end
220207
221208 # Queries whether any threads are waiting to acquire the read or write lock.
222209 #
223210 # @return [Boolean] true if any threads are waiting for a lock else false
224211 def has_waiters?
225- waiting_writer? ( @counter . value )
212+ waiting_writer? ( @Counter . value )
226213 end
227214
228215 private
229216
230217 # @!visibility private
231- def running_readers ( c = @counter . value )
218+ def running_readers ( c = @Counter . value )
232219 c & MAX_READERS
233220 end
234221
235222 # @!visibility private
236- def running_readers? ( c = @counter . value )
223+ def running_readers? ( c = @Counter . value )
237224 ( c & MAX_READERS ) > 0
238225 end
239226
240227 # @!visibility private
241- def running_writer? ( c = @counter . value )
228+ def running_writer? ( c = @Counter . value )
242229 c >= RUNNING_WRITER
243230 end
244231
245232 # @!visibility private
246- def waiting_writers ( c = @counter . value )
233+ def waiting_writers ( c = @Counter . value )
247234 ( c & MAX_WRITERS ) / WAITING_WRITER
248235 end
249236
250237 # @!visibility private
251- def waiting_writer? ( c = @counter . value )
238+ def waiting_writer? ( c = @Counter . value )
252239 c >= WAITING_WRITER
253240 end
254241
255242 # @!visibility private
256- def max_readers? ( c = @counter . value )
243+ def max_readers? ( c = @Counter . value )
257244 ( c & MAX_READERS ) == MAX_READERS
258245 end
259246
260247 # @!visibility private
261- def max_writers? ( c = @counter . value )
248+ def max_writers? ( c = @Counter . value )
262249 ( c & MAX_WRITERS ) == MAX_WRITERS
263250 end
264251 end
0 commit comments