o-concurrent-ruby 1.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +542 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +404 -0
  6. data/Rakefile +307 -0
  7. data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
  10. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
  11. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
  12. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
  13. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
  14. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
  15. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
  16. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
  17. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
  18. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
  19. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
  20. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
  21. data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
  22. data/lib/concurrent-ruby/concurrent/agent.rb +587 -0
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/concurrent-ruby/concurrent/async.rb +449 -0
  25. data/lib/concurrent-ruby/concurrent/atom.rb +222 -0
  26. data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +66 -0
  27. data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +126 -0
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +143 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  30. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  31. data/lib/concurrent-ruby/concurrent/atomic/count_down_latch.rb +100 -0
  32. data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +128 -0
  33. data/lib/concurrent-ruby/concurrent/atomic/event.rb +109 -0
  34. data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +42 -0
  35. data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +37 -0
  36. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +62 -0
  37. data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +75 -0
  38. data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +44 -0
  39. data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +131 -0
  40. data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +254 -0
  41. data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +377 -0
  42. data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +181 -0
  43. data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +166 -0
  44. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +104 -0
  45. data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +56 -0
  46. data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +28 -0
  47. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  48. data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +107 -0
  49. data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +111 -0
  50. data/lib/concurrent-ruby/concurrent/collection/java_non_concurrent_priority_queue.rb +84 -0
  51. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  52. data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +927 -0
  53. data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +66 -0
  54. data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +140 -0
  55. data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +82 -0
  56. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  57. data/lib/concurrent-ruby/concurrent/collection/non_concurrent_priority_queue.rb +143 -0
  58. data/lib/concurrent-ruby/concurrent/collection/ruby_non_concurrent_priority_queue.rb +160 -0
  59. data/lib/concurrent-ruby/concurrent/concern/deprecation.rb +34 -0
  60. data/lib/concurrent-ruby/concurrent/concern/dereferenceable.rb +73 -0
  61. data/lib/concurrent-ruby/concurrent/concern/logging.rb +32 -0
  62. data/lib/concurrent-ruby/concurrent/concern/obligation.rb +220 -0
  63. data/lib/concurrent-ruby/concurrent/concern/observable.rb +110 -0
  64. data/lib/concurrent-ruby/concurrent/configuration.rb +188 -0
  65. data/lib/concurrent-ruby/concurrent/constants.rb +8 -0
  66. data/lib/concurrent-ruby/concurrent/dataflow.rb +81 -0
  67. data/lib/concurrent-ruby/concurrent/delay.rb +199 -0
  68. data/lib/concurrent-ruby/concurrent/errors.rb +69 -0
  69. data/lib/concurrent-ruby/concurrent/exchanger.rb +352 -0
  70. data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +131 -0
  71. data/lib/concurrent-ruby/concurrent/executor/cached_thread_pool.rb +62 -0
  72. data/lib/concurrent-ruby/concurrent/executor/executor_service.rb +185 -0
  73. data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +220 -0
  74. data/lib/concurrent-ruby/concurrent/executor/immediate_executor.rb +66 -0
  75. data/lib/concurrent-ruby/concurrent/executor/indirect_immediate_executor.rb +44 -0
  76. data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +103 -0
  77. data/lib/concurrent-ruby/concurrent/executor/java_single_thread_executor.rb +30 -0
  78. data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +140 -0
  79. data/lib/concurrent-ruby/concurrent/executor/ruby_executor_service.rb +82 -0
  80. data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +21 -0
  81. data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +368 -0
  82. data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +35 -0
  83. data/lib/concurrent-ruby/concurrent/executor/serial_executor_service.rb +34 -0
  84. data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +107 -0
  85. data/lib/concurrent-ruby/concurrent/executor/serialized_execution_delegator.rb +28 -0
  86. data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +100 -0
  87. data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +57 -0
  88. data/lib/concurrent-ruby/concurrent/executor/thread_pool_executor.rb +88 -0
  89. data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +172 -0
  90. data/lib/concurrent-ruby/concurrent/executors.rb +20 -0
  91. data/lib/concurrent-ruby/concurrent/future.rb +141 -0
  92. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  93. data/lib/concurrent-ruby/concurrent/immutable_struct.rb +101 -0
  94. data/lib/concurrent-ruby/concurrent/ivar.rb +207 -0
  95. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  96. data/lib/concurrent-ruby/concurrent/maybe.rb +229 -0
  97. data/lib/concurrent-ruby/concurrent/mutable_struct.rb +239 -0
  98. data/lib/concurrent-ruby/concurrent/mvar.rb +242 -0
  99. data/lib/concurrent-ruby/concurrent/options.rb +42 -0
  100. data/lib/concurrent-ruby/concurrent/promise.rb +580 -0
  101. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  102. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  103. data/lib/concurrent-ruby/concurrent/scheduled_task.rb +331 -0
  104. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  105. data/lib/concurrent-ruby/concurrent/settable_struct.rb +139 -0
  106. data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +98 -0
  107. data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +24 -0
  108. data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +171 -0
  109. data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +60 -0
  110. data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +13 -0
  111. data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +45 -0
  112. data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +36 -0
  113. data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +72 -0
  114. data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +44 -0
  115. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  116. data/lib/concurrent-ruby/concurrent/synchronization/object.rb +183 -0
  117. data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +71 -0
  118. data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +49 -0
  119. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  120. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +36 -0
  121. data/lib/concurrent-ruby/concurrent/synchronization.rb +30 -0
  122. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +50 -0
  123. data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +74 -0
  124. data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +118 -0
  125. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  126. data/lib/concurrent-ruby/concurrent/thread_safe/util/power_of_two_tuple.rb +38 -0
  127. data/lib/concurrent-ruby/concurrent/thread_safe/util/striped64.rb +246 -0
  128. data/lib/concurrent-ruby/concurrent/thread_safe/util/volatile.rb +75 -0
  129. data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +50 -0
  130. data/lib/concurrent-ruby/concurrent/thread_safe/util.rb +16 -0
  131. data/lib/concurrent-ruby/concurrent/timer_task.rb +311 -0
  132. data/lib/concurrent-ruby/concurrent/tuple.rb +86 -0
  133. data/lib/concurrent-ruby/concurrent/tvar.rb +221 -0
  134. data/lib/concurrent-ruby/concurrent/utility/engine.rb +56 -0
  135. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  136. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  137. data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +53 -0
  138. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +130 -0
  139. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  140. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  141. data/lib/concurrent-ruby/concurrent.rb +134 -0
  142. metadata +192 -0
@@ -0,0 +1,131 @@
1
+ require 'concurrent/synchronization'
2
+ require 'concurrent/utility/native_integer'
3
+
4
+ module Concurrent
5
+
6
+ # @!macro semaphore
7
+ # @!visibility private
8
+ # @!macro internal_implementation_note
9
+ class MutexSemaphore < Synchronization::LockableObject
10
+
11
+ # @!macro semaphore_method_initialize
12
+ def initialize(count)
13
+ Utility::NativeInteger.ensure_integer_and_bounds count
14
+
15
+ super()
16
+ synchronize { ns_initialize count }
17
+ end
18
+
19
+ # @!macro semaphore_method_acquire
20
+ def acquire(permits = 1)
21
+ Utility::NativeInteger.ensure_integer_and_bounds permits
22
+ Utility::NativeInteger.ensure_positive permits
23
+
24
+ synchronize do
25
+ try_acquire_timed(permits, nil)
26
+ end
27
+
28
+ return unless block_given?
29
+
30
+ begin
31
+ yield
32
+ ensure
33
+ release(permits)
34
+ end
35
+ end
36
+
37
+ # @!macro semaphore_method_available_permits
38
+ def available_permits
39
+ synchronize { @free }
40
+ end
41
+
42
+ # @!macro semaphore_method_drain_permits
43
+ #
44
+ # Acquires and returns all permits that are immediately available.
45
+ #
46
+ # @return [Integer]
47
+ def drain_permits
48
+ synchronize do
49
+ @free.tap { |_| @free = 0 }
50
+ end
51
+ end
52
+
53
+ # @!macro semaphore_method_try_acquire
54
+ def try_acquire(permits = 1, timeout = nil)
55
+ Utility::NativeInteger.ensure_integer_and_bounds permits
56
+ Utility::NativeInteger.ensure_positive permits
57
+
58
+ acquired = synchronize do
59
+ if timeout.nil?
60
+ try_acquire_now(permits)
61
+ else
62
+ try_acquire_timed(permits, timeout)
63
+ end
64
+ end
65
+
66
+ return acquired unless block_given?
67
+ return unless acquired
68
+
69
+ begin
70
+ yield
71
+ ensure
72
+ release(permits)
73
+ end
74
+ end
75
+
76
+ # @!macro semaphore_method_release
77
+ def release(permits = 1)
78
+ Utility::NativeInteger.ensure_integer_and_bounds permits
79
+ Utility::NativeInteger.ensure_positive permits
80
+
81
+ synchronize do
82
+ @free += permits
83
+ permits.times { ns_signal }
84
+ end
85
+ nil
86
+ end
87
+
88
+ # Shrinks the number of available permits by the indicated reduction.
89
+ #
90
+ # @param [Fixnum] reduction Number of permits to remove.
91
+ #
92
+ # @raise [ArgumentError] if `reduction` is not an integer or is negative
93
+ #
94
+ # @raise [ArgumentError] if `@free` - `@reduction` is less than zero
95
+ #
96
+ # @return [nil]
97
+ #
98
+ # @!visibility private
99
+ def reduce_permits(reduction)
100
+ Utility::NativeInteger.ensure_integer_and_bounds reduction
101
+ Utility::NativeInteger.ensure_positive reduction
102
+
103
+ synchronize { @free -= reduction }
104
+ nil
105
+ end
106
+
107
+ protected
108
+
109
+ # @!visibility private
110
+ def ns_initialize(count)
111
+ @free = count
112
+ end
113
+
114
+ private
115
+
116
+ # @!visibility private
117
+ def try_acquire_now(permits)
118
+ if @free >= permits
119
+ @free -= permits
120
+ true
121
+ else
122
+ false
123
+ end
124
+ end
125
+
126
+ # @!visibility private
127
+ def try_acquire_timed(permits, timeout)
128
+ ns_wait_until(timeout) { try_acquire_now(permits) }
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,254 @@
1
+ require 'thread'
2
+ require 'concurrent/atomic/atomic_fixnum'
3
+ require 'concurrent/errors'
4
+ require 'concurrent/synchronization'
5
+
6
+ module Concurrent
7
+
8
+ # Ruby read-write lock implementation
9
+ #
10
+ # Allows any number of concurrent readers, but only one concurrent writer
11
+ # (And if the "write" lock is taken, any readers who come along will have to wait)
12
+ #
13
+ # If readers are already active when a writer comes along, the writer will wait for
14
+ # all the readers to finish before going ahead.
15
+ # Any additional readers that come when the writer is already waiting, will also
16
+ # wait (so writers are not starved).
17
+ #
18
+ # This implementation is based on `java.util.concurrent.ReentrantReadWriteLock`.
19
+ #
20
+ # @example
21
+ # lock = Concurrent::ReadWriteLock.new
22
+ # lock.with_read_lock { data.retrieve }
23
+ # lock.with_write_lock { data.modify! }
24
+ #
25
+ # @note Do **not** try to acquire the write lock while already holding a read lock
26
+ # **or** try to acquire the write lock while you already have it.
27
+ # This will lead to deadlock
28
+ #
29
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/ReentrantReadWriteLock.html java.util.concurrent.ReentrantReadWriteLock
30
+ class ReadWriteLock < Synchronization::Object
31
+
32
+ # @!visibility private
33
+ WAITING_WRITER = 1 << 15
34
+
35
+ # @!visibility private
36
+ RUNNING_WRITER = 1 << 29
37
+
38
+ # @!visibility private
39
+ MAX_READERS = WAITING_WRITER - 1
40
+
41
+ # @!visibility private
42
+ MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1
43
+
44
+ safe_initialization!
45
+
46
+ # Implementation notes:
47
+ # A goal is to make the uncontended path for both readers/writers lock-free
48
+ # Only if there is reader-writer or writer-writer contention, should locks be used
49
+ # Internal state is represented by a single integer ("counter"), and updated
50
+ # using atomic compare-and-swap operations
51
+ # When the counter is 0, the lock is free
52
+ # Each reader increments the counter by 1 when acquiring a read lock
53
+ # (and decrements by 1 when releasing the read lock)
54
+ # The counter is increased by (1 << 15) for each writer waiting to acquire the
55
+ # write lock, and by (1 << 29) if the write lock is taken
56
+
57
+ # Create a new `ReadWriteLock` in the unlocked state.
58
+ def initialize
59
+ super()
60
+ @Counter = AtomicFixnum.new(0) # single integer which represents lock state
61
+ @ReadLock = Synchronization::Lock.new
62
+ @WriteLock = Synchronization::Lock.new
63
+ end
64
+
65
+ # Execute a block operation within a read lock.
66
+ #
67
+ # @yield the task to be performed within the lock.
68
+ #
69
+ # @return [Object] the result of the block operation.
70
+ #
71
+ # @raise [ArgumentError] when no block is given.
72
+ # @raise [Concurrent::ResourceLimitError] if the maximum number of readers
73
+ # is exceeded.
74
+ def with_read_lock
75
+ raise ArgumentError.new('no block given') unless block_given?
76
+ acquire_read_lock
77
+ begin
78
+ yield
79
+ ensure
80
+ release_read_lock
81
+ end
82
+ end
83
+
84
+ # Execute a block operation within a write lock.
85
+ #
86
+ # @yield the task to be performed within the lock.
87
+ #
88
+ # @return [Object] the result of the block operation.
89
+ #
90
+ # @raise [ArgumentError] when no block is given.
91
+ # @raise [Concurrent::ResourceLimitError] if the maximum number of readers
92
+ # is exceeded.
93
+ def with_write_lock
94
+ raise ArgumentError.new('no block given') unless block_given?
95
+ acquire_write_lock
96
+ begin
97
+ yield
98
+ ensure
99
+ release_write_lock
100
+ end
101
+ end
102
+
103
+ # Acquire a read lock. If a write lock has been acquired will block until
104
+ # it is released. Will not block if other read locks have been acquired.
105
+ #
106
+ # @return [Boolean] true if the lock is successfully acquired
107
+ #
108
+ # @raise [Concurrent::ResourceLimitError] if the maximum number of readers
109
+ # is exceeded.
110
+ def acquire_read_lock
111
+ while true
112
+ c = @Counter.value
113
+ raise ResourceLimitError.new('Too many reader threads') if max_readers?(c)
114
+
115
+ # If a writer is waiting when we first queue up, we need to wait
116
+ if waiting_writer?(c)
117
+ @ReadLock.wait_until { !waiting_writer? }
118
+
119
+ # after a reader has waited once, they are allowed to "barge" ahead of waiting writers
120
+ # but if a writer is *running*, the reader still needs to wait (naturally)
121
+ while true
122
+ c = @Counter.value
123
+ if running_writer?(c)
124
+ @ReadLock.wait_until { !running_writer? }
125
+ else
126
+ return if @Counter.compare_and_set(c, c+1)
127
+ end
128
+ end
129
+ else
130
+ break if @Counter.compare_and_set(c, c+1)
131
+ end
132
+ end
133
+ true
134
+ end
135
+
136
+ # Release a previously acquired read lock.
137
+ #
138
+ # @return [Boolean] true if the lock is successfully released
139
+ def release_read_lock
140
+ while true
141
+ c = @Counter.value
142
+ if @Counter.compare_and_set(c, c-1)
143
+ # If one or more writers were waiting, and we were the last reader, wake a writer up
144
+ if waiting_writer?(c) && running_readers(c) == 1
145
+ @WriteLock.signal
146
+ end
147
+ break
148
+ end
149
+ end
150
+ true
151
+ end
152
+
153
+ # Acquire a write lock. Will block and wait for all active readers and writers.
154
+ #
155
+ # @return [Boolean] true if the lock is successfully acquired
156
+ #
157
+ # @raise [Concurrent::ResourceLimitError] if the maximum number of writers
158
+ # is exceeded.
159
+ def acquire_write_lock
160
+ while true
161
+ c = @Counter.value
162
+ raise ResourceLimitError.new('Too many writer threads') if max_writers?(c)
163
+
164
+ if c == 0 # no readers OR writers running
165
+ # if we successfully swap the RUNNING_WRITER bit on, then we can go ahead
166
+ break if @Counter.compare_and_set(0, RUNNING_WRITER)
167
+ elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
168
+ while true
169
+ # Now we have successfully incremented, so no more readers will be able to increment
170
+ # (they will wait instead)
171
+ # However, readers OR writers could decrement right here, OR another writer could increment
172
+ @WriteLock.wait_until do
173
+ # So we have to do another check inside the synchronized section
174
+ # If a writer OR reader is running, then go to sleep
175
+ c = @Counter.value
176
+ !running_writer?(c) && !running_readers?(c)
177
+ end
178
+
179
+ # We just came out of a wait
180
+ # If we successfully turn the RUNNING_WRITER bit on with an atomic swap,
181
+ # Then we are OK to stop waiting and go ahead
182
+ # Otherwise go back and wait again
183
+ c = @Counter.value
184
+ break if !running_writer?(c) && !running_readers?(c) && @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER)
185
+ end
186
+ break
187
+ end
188
+ end
189
+ true
190
+ end
191
+
192
+ # Release a previously acquired write lock.
193
+ #
194
+ # @return [Boolean] true if the lock is successfully released
195
+ def release_write_lock
196
+ return true unless running_writer?
197
+ c = @Counter.update { |counter| counter - RUNNING_WRITER }
198
+ @ReadLock.broadcast
199
+ @WriteLock.signal if waiting_writers(c) > 0
200
+ true
201
+ end
202
+
203
+ # Queries if the write lock is held by any thread.
204
+ #
205
+ # @return [Boolean] true if the write lock is held else false`
206
+ def write_locked?
207
+ @Counter.value >= RUNNING_WRITER
208
+ end
209
+
210
+ # Queries whether any threads are waiting to acquire the read or write lock.
211
+ #
212
+ # @return [Boolean] true if any threads are waiting for a lock else false
213
+ def has_waiters?
214
+ waiting_writer?(@Counter.value)
215
+ end
216
+
217
+ private
218
+
219
+ # @!visibility private
220
+ def running_readers(c = @Counter.value)
221
+ c & MAX_READERS
222
+ end
223
+
224
+ # @!visibility private
225
+ def running_readers?(c = @Counter.value)
226
+ (c & MAX_READERS) > 0
227
+ end
228
+
229
+ # @!visibility private
230
+ def running_writer?(c = @Counter.value)
231
+ c >= RUNNING_WRITER
232
+ end
233
+
234
+ # @!visibility private
235
+ def waiting_writers(c = @Counter.value)
236
+ (c & MAX_WRITERS) / WAITING_WRITER
237
+ end
238
+
239
+ # @!visibility private
240
+ def waiting_writer?(c = @Counter.value)
241
+ c >= WAITING_WRITER
242
+ end
243
+
244
+ # @!visibility private
245
+ def max_readers?(c = @Counter.value)
246
+ (c & MAX_READERS) == MAX_READERS
247
+ end
248
+
249
+ # @!visibility private
250
+ def max_writers?(c = @Counter.value)
251
+ (c & MAX_WRITERS) == MAX_WRITERS
252
+ end
253
+ end
254
+ end