concurrent-ruby 1.0.5 → 1.1.10

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 (164) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +155 -0
  3. data/Gemfile +37 -0
  4. data/LICENSE.txt +18 -18
  5. data/README.md +260 -103
  6. data/Rakefile +329 -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 → concurrent-ruby/concurrent}/agent.rb +7 -7
  23. data/lib/concurrent-ruby/concurrent/array.rb +66 -0
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +28 -24
  25. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +10 -10
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +26 -22
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +27 -23
  28. data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
  29. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +7 -7
  31. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
  32. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +9 -6
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +2 -0
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -0
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +18 -2
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +7 -7
  39. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +34 -13
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +8 -8
  42. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +3 -8
  43. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +1 -1
  44. data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
  45. data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +3 -3
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +1 -2
  49. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +30 -30
  51. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +3 -3
  53. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +6 -1
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +7 -7
  55. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +15 -15
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +1 -1
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +2 -1
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +9 -7
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +21 -25
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +35 -38
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +5 -5
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +17 -17
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +47 -33
  65. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +20 -17
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +29 -9
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +5 -5
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +3 -2
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +7 -6
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +14 -17
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +4 -1
  77. data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +5 -6
  80. data/lib/concurrent-ruby/concurrent/map.rb +346 -0
  81. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +27 -16
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +2 -2
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +54 -21
  85. data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
  86. data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
  87. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +29 -16
  88. data/lib/concurrent-ruby/concurrent/set.rb +74 -0
  89. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +12 -1
  90. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -5
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +18 -4
  92. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
  94. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -10
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
  97. data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
  98. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +53 -23
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +6 -0
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
  101. data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
  102. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +11 -9
  103. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +4 -5
  104. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
  105. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +9 -4
  106. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +15 -35
  107. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -1
  108. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -58
  109. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +4 -4
  110. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  111. data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
  112. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -35
  113. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  114. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  115. data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +24 -20
  116. metadata +149 -134
  117. data/lib/concurrent/array.rb +0 -39
  118. data/lib/concurrent/atomic/atomic_reference.rb +0 -51
  119. data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
  120. data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
  121. data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
  122. data/lib/concurrent/atomic_reference/jruby.rb +0 -16
  123. data/lib/concurrent/atomic_reference/rbx.rb +0 -22
  124. data/lib/concurrent/atomic_reference/ruby.rb +0 -32
  125. data/lib/concurrent/atomics.rb +0 -53
  126. data/lib/concurrent/edge.rb +0 -26
  127. data/lib/concurrent/hash.rb +0 -36
  128. data/lib/concurrent/lazy_register.rb +0 -81
  129. data/lib/concurrent/map.rb +0 -240
  130. data/lib/concurrent/synchronization/mri_lockable_object.rb +0 -71
  131. data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
  132. data/lib/concurrent/synchronization/truffle_object.rb +0 -31
  133. data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
  134. data/lib/concurrent/utility/at_exit.rb +0 -97
  135. data/lib/concurrent/utility/monotonic_time.rb +0 -58
  136. data/lib/concurrent/utility/native_extension_loader.rb +0 -73
  137. data/lib/concurrent/version.rb +0 -4
  138. /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
  139. /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
  140. /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
  141. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
  142. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
  143. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  144. /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  145. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  146. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  147. /data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
  148. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  149. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  150. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  151. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
  152. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  153. /data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  154. /data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  155. /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
  156. /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
  157. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
  158. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  159. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
  160. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  161. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  162. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  163. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  164. /data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
@@ -0,0 +1,164 @@
1
+ module Concurrent
2
+ # An atomic reference which maintains an object reference along with a mark bit
3
+ # that can be updated atomically.
4
+ #
5
+ # @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/atomic/AtomicMarkableReference.html
6
+ # java.util.concurrent.atomic.AtomicMarkableReference
7
+ class AtomicMarkableReference < ::Concurrent::Synchronization::Object
8
+
9
+ attr_atomic(:reference)
10
+ private :reference, :reference=, :swap_reference, :compare_and_set_reference, :update_reference
11
+
12
+ def initialize(value = nil, mark = false)
13
+ super()
14
+ self.reference = immutable_array(value, mark)
15
+ end
16
+
17
+ # Atomically sets the value and mark to the given updated value and
18
+ # mark given both:
19
+ # - the current value == the expected value &&
20
+ # - the current mark == the expected mark
21
+ #
22
+ # @param [Object] expected_val the expected value
23
+ # @param [Object] new_val the new value
24
+ # @param [Boolean] expected_mark the expected mark
25
+ # @param [Boolean] new_mark the new mark
26
+ #
27
+ # @return [Boolean] `true` if successful. A `false` return indicates
28
+ # that the actual value was not equal to the expected value or the
29
+ # actual mark was not equal to the expected mark
30
+ def compare_and_set(expected_val, new_val, expected_mark, new_mark)
31
+ # Memoize a valid reference to the current AtomicReference for
32
+ # later comparison.
33
+ current = reference
34
+ curr_val, curr_mark = current
35
+
36
+ # Ensure that that the expected marks match.
37
+ return false unless expected_mark == curr_mark
38
+
39
+ if expected_val.is_a? Numeric
40
+ # If the object is a numeric, we need to ensure we are comparing
41
+ # the numerical values
42
+ return false unless expected_val == curr_val
43
+ else
44
+ # Otherwise, we need to ensure we are comparing the object identity.
45
+ # Theoretically, this could be incorrect if a user monkey-patched
46
+ # `Object#equal?`, but they should know that they are playing with
47
+ # fire at that point.
48
+ return false unless expected_val.equal? curr_val
49
+ end
50
+
51
+ prospect = immutable_array(new_val, new_mark)
52
+
53
+ compare_and_set_reference current, prospect
54
+ end
55
+
56
+ alias_method :compare_and_swap, :compare_and_set
57
+
58
+ # Gets the current reference and marked values.
59
+ #
60
+ # @return [Array] the current reference and marked values
61
+ def get
62
+ reference
63
+ end
64
+
65
+ # Gets the current value of the reference
66
+ #
67
+ # @return [Object] the current value of the reference
68
+ def value
69
+ reference[0]
70
+ end
71
+
72
+ # Gets the current marked value
73
+ #
74
+ # @return [Boolean] the current marked value
75
+ def mark
76
+ reference[1]
77
+ end
78
+
79
+ alias_method :marked?, :mark
80
+
81
+ # _Unconditionally_ sets to the given value of both the reference and
82
+ # the mark.
83
+ #
84
+ # @param [Object] new_val the new value
85
+ # @param [Boolean] new_mark the new mark
86
+ #
87
+ # @return [Array] both the new value and the new mark
88
+ def set(new_val, new_mark)
89
+ self.reference = immutable_array(new_val, new_mark)
90
+ end
91
+
92
+ # Pass the current value and marked state to the given block, replacing it
93
+ # with the block's results. May retry if the value changes during the
94
+ # block's execution.
95
+ #
96
+ # @yield [Object] Calculate a new value and marked state for the atomic
97
+ # reference using given (old) value and (old) marked
98
+ # @yieldparam [Object] old_val the starting value of the atomic reference
99
+ # @yieldparam [Boolean] old_mark the starting state of marked
100
+ #
101
+ # @return [Array] the new value and new mark
102
+ def update
103
+ loop do
104
+ old_val, old_mark = reference
105
+ new_val, new_mark = yield old_val, old_mark
106
+
107
+ if compare_and_set old_val, new_val, old_mark, new_mark
108
+ return immutable_array(new_val, new_mark)
109
+ end
110
+ end
111
+ end
112
+
113
+ # Pass the current value to the given block, replacing it
114
+ # with the block's result. Raise an exception if the update
115
+ # fails.
116
+ #
117
+ # @yield [Object] Calculate a new value and marked state for the atomic
118
+ # reference using given (old) value and (old) marked
119
+ # @yieldparam [Object] old_val the starting value of the atomic reference
120
+ # @yieldparam [Boolean] old_mark the starting state of marked
121
+ #
122
+ # @return [Array] the new value and marked state
123
+ #
124
+ # @raise [Concurrent::ConcurrentUpdateError] if the update fails
125
+ def try_update!
126
+ old_val, old_mark = reference
127
+ new_val, new_mark = yield old_val, old_mark
128
+
129
+ unless compare_and_set old_val, new_val, old_mark, new_mark
130
+ fail ::Concurrent::ConcurrentUpdateError,
131
+ 'AtomicMarkableReference: Update failed due to race condition.',
132
+ 'Note: If you would like to guarantee an update, please use ' +
133
+ 'the `AtomicMarkableReference#update` method.'
134
+ end
135
+
136
+ immutable_array(new_val, new_mark)
137
+ end
138
+
139
+ # Pass the current value to the given block, replacing it with the
140
+ # block's result. Simply return nil if update fails.
141
+ #
142
+ # @yield [Object] Calculate a new value and marked state for the atomic
143
+ # reference using given (old) value and (old) marked
144
+ # @yieldparam [Object] old_val the starting value of the atomic reference
145
+ # @yieldparam [Boolean] old_mark the starting state of marked
146
+ #
147
+ # @return [Array] the new value and marked state, or nil if
148
+ # the update failed
149
+ def try_update
150
+ old_val, old_mark = reference
151
+ new_val, new_mark = yield old_val, old_mark
152
+
153
+ return unless compare_and_set old_val, new_val, old_mark, new_mark
154
+
155
+ immutable_array(new_val, new_mark)
156
+ end
157
+
158
+ private
159
+
160
+ def immutable_array(*args)
161
+ args.freeze
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,205 @@
1
+ require 'concurrent/synchronization'
2
+ require 'concurrent/utility/engine'
3
+ require 'concurrent/atomic_reference/numeric_cas_wrapper'
4
+
5
+ # Shim for TruffleRuby::AtomicReference
6
+ if Concurrent.on_truffleruby? && !defined?(TruffleRuby::AtomicReference)
7
+ # @!visibility private
8
+ module TruffleRuby
9
+ AtomicReference = Truffle::AtomicReference
10
+ end
11
+ end
12
+
13
+ module Concurrent
14
+
15
+ # Define update methods that use direct paths
16
+ #
17
+ # @!visibility private
18
+ # @!macro internal_implementation_note
19
+ module AtomicDirectUpdate
20
+
21
+ # @!macro atomic_reference_method_update
22
+ #
23
+ # Pass the current value to the given block, replacing it
24
+ # with the block's result. May retry if the value changes
25
+ # during the block's execution.
26
+ #
27
+ # @yield [Object] Calculate a new value for the atomic reference using
28
+ # given (old) value
29
+ # @yieldparam [Object] old_value the starting value of the atomic reference
30
+ # @return [Object] the new value
31
+ def update
32
+ true until compare_and_set(old_value = get, new_value = yield(old_value))
33
+ new_value
34
+ end
35
+
36
+ # @!macro atomic_reference_method_try_update
37
+ #
38
+ # Pass the current value to the given block, replacing it
39
+ # with the block's result. Return nil if the update fails.
40
+ #
41
+ # @yield [Object] Calculate a new value for the atomic reference using
42
+ # given (old) value
43
+ # @yieldparam [Object] old_value the starting value of the atomic reference
44
+ # @note This method was altered to avoid raising an exception by default.
45
+ # Instead, this method now returns `nil` in case of failure. For more info,
46
+ # please see: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
47
+ # @return [Object] the new value, or nil if update failed
48
+ def try_update
49
+ old_value = get
50
+ new_value = yield old_value
51
+
52
+ return unless compare_and_set old_value, new_value
53
+
54
+ new_value
55
+ end
56
+
57
+ # @!macro atomic_reference_method_try_update!
58
+ #
59
+ # Pass the current value to the given block, replacing it
60
+ # with the block's result. Raise an exception if the update
61
+ # fails.
62
+ #
63
+ # @yield [Object] Calculate a new value for the atomic reference using
64
+ # given (old) value
65
+ # @yieldparam [Object] old_value the starting value of the atomic reference
66
+ # @note This behavior mimics the behavior of the original
67
+ # `AtomicReference#try_update` API. The reason this was changed was to
68
+ # avoid raising exceptions (which are inherently slow) by default. For more
69
+ # info: https://github.com/ruby-concurrency/concurrent-ruby/pull/336
70
+ # @return [Object] the new value
71
+ # @raise [Concurrent::ConcurrentUpdateError] if the update fails
72
+ def try_update!
73
+ old_value = get
74
+ new_value = yield old_value
75
+ unless compare_and_set(old_value, new_value)
76
+ if $VERBOSE
77
+ raise ConcurrentUpdateError, "Update failed"
78
+ else
79
+ raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
80
+ end
81
+ end
82
+ new_value
83
+ end
84
+ end
85
+
86
+ require 'concurrent/atomic_reference/mutex_atomic'
87
+
88
+ # @!macro atomic_reference
89
+ #
90
+ # An object reference that may be updated atomically. All read and write
91
+ # operations have java volatile semantic.
92
+ #
93
+ # @!macro thread_safe_variable_comparison
94
+ #
95
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.html
96
+ # @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html
97
+ #
98
+ # @!method initialize(value = nil)
99
+ # @!macro atomic_reference_method_initialize
100
+ # @param [Object] value The initial value.
101
+ #
102
+ # @!method get
103
+ # @!macro atomic_reference_method_get
104
+ # Gets the current value.
105
+ # @return [Object] the current value
106
+ #
107
+ # @!method set(new_value)
108
+ # @!macro atomic_reference_method_set
109
+ # Sets to the given value.
110
+ # @param [Object] new_value the new value
111
+ # @return [Object] the new value
112
+ #
113
+ # @!method get_and_set(new_value)
114
+ # @!macro atomic_reference_method_get_and_set
115
+ # Atomically sets to the given value and returns the old value.
116
+ # @param [Object] new_value the new value
117
+ # @return [Object] the old value
118
+ #
119
+ # @!method compare_and_set(old_value, new_value)
120
+ # @!macro atomic_reference_method_compare_and_set
121
+ #
122
+ # Atomically sets the value to the given updated value if
123
+ # the current value == the expected value.
124
+ #
125
+ # @param [Object] old_value the expected value
126
+ # @param [Object] new_value the new value
127
+ #
128
+ # @return [Boolean] `true` if successful. A `false` return indicates
129
+ # that the actual value was not equal to the expected value.
130
+ #
131
+ # @!method update
132
+ # @!macro atomic_reference_method_update
133
+ #
134
+ # @!method try_update
135
+ # @!macro atomic_reference_method_try_update
136
+ #
137
+ # @!method try_update!
138
+ # @!macro atomic_reference_method_try_update!
139
+
140
+
141
+ # @!macro internal_implementation_note
142
+ class ConcurrentUpdateError < ThreadError
143
+ # frozen pre-allocated backtrace to speed ConcurrentUpdateError
144
+ CONC_UP_ERR_BACKTRACE = ['backtrace elided; set verbose to enable'].freeze
145
+ end
146
+
147
+ # @!macro internal_implementation_note
148
+ AtomicReferenceImplementation = case
149
+ when Concurrent.on_cruby? && Concurrent.c_extensions_loaded?
150
+ # @!visibility private
151
+ # @!macro internal_implementation_note
152
+ class CAtomicReference
153
+ include AtomicDirectUpdate
154
+ include AtomicNumericCompareAndSetWrapper
155
+ alias_method :compare_and_swap, :compare_and_set
156
+ end
157
+ CAtomicReference
158
+ when Concurrent.on_jruby?
159
+ # @!visibility private
160
+ # @!macro internal_implementation_note
161
+ class JavaAtomicReference
162
+ include AtomicDirectUpdate
163
+ end
164
+ JavaAtomicReference
165
+ when Concurrent.on_truffleruby?
166
+ class TruffleRubyAtomicReference < TruffleRuby::AtomicReference
167
+ include AtomicDirectUpdate
168
+ alias_method :value, :get
169
+ alias_method :value=, :set
170
+ alias_method :compare_and_swap, :compare_and_set
171
+ alias_method :swap, :get_and_set
172
+ end
173
+ TruffleRubyAtomicReference
174
+ when Concurrent.on_rbx?
175
+ # @note Extends `Rubinius::AtomicReference` version adding aliases
176
+ # and numeric logic.
177
+ #
178
+ # @!visibility private
179
+ # @!macro internal_implementation_note
180
+ class RbxAtomicReference < Rubinius::AtomicReference
181
+ alias_method :_compare_and_set, :compare_and_set
182
+ include AtomicDirectUpdate
183
+ include AtomicNumericCompareAndSetWrapper
184
+ alias_method :value, :get
185
+ alias_method :value=, :set
186
+ alias_method :swap, :get_and_set
187
+ alias_method :compare_and_swap, :compare_and_set
188
+ end
189
+ RbxAtomicReference
190
+ else
191
+ MutexAtomicReference
192
+ end
193
+ private_constant :AtomicReferenceImplementation
194
+
195
+ # @!macro atomic_reference
196
+ class AtomicReference < AtomicReferenceImplementation
197
+
198
+ # @return [String] Short string representation.
199
+ def to_s
200
+ format '%s value:%s>', super[0..-2], get
201
+ end
202
+
203
+ alias_method :inspect, :to_s
204
+ end
205
+ end
@@ -1,12 +1,12 @@
1
+ require 'concurrent/utility/engine'
1
2
  require 'concurrent/atomic/mutex_count_down_latch'
2
3
  require 'concurrent/atomic/java_count_down_latch'
3
- require 'concurrent/utility/engine'
4
4
 
5
5
  module Concurrent
6
6
 
7
7
  ###################################################################
8
8
 
9
- # @!macro [new] count_down_latch_method_initialize
9
+ # @!macro count_down_latch_method_initialize
10
10
  #
11
11
  # Create a new `CountDownLatch` with the initial `count`.
12
12
  #
@@ -14,7 +14,7 @@ module Concurrent
14
14
  #
15
15
  # @raise [ArgumentError] if `count` is not an integer or is less than zero
16
16
 
17
- # @!macro [new] count_down_latch_method_wait
17
+ # @!macro count_down_latch_method_wait
18
18
  #
19
19
  # Block on the latch until the counter reaches zero or until `timeout` is reached.
20
20
  #
@@ -22,12 +22,12 @@ module Concurrent
22
22
  # to block indefinitely
23
23
  # @return [Boolean] `true` if the `count` reaches zero else false on `timeout`
24
24
 
25
- # @!macro [new] count_down_latch_method_count_down
25
+ # @!macro count_down_latch_method_count_down
26
26
  #
27
27
  # Signal the latch to decrement the counter. Will signal all blocked threads when
28
28
  # the `count` reaches zero.
29
29
 
30
- # @!macro [attach] count_down_latch_method_count
30
+ # @!macro count_down_latch_method_count
31
31
  #
32
32
  # The current value of the counter.
33
33
  #
@@ -35,7 +35,7 @@ module Concurrent
35
35
 
36
36
  ###################################################################
37
37
 
38
- # @!macro [new] count_down_latch_public_api
38
+ # @!macro count_down_latch_public_api
39
39
  #
40
40
  # @!method initialize(count = 1)
41
41
  # @!macro count_down_latch_method_initialize
@@ -61,7 +61,7 @@ module Concurrent
61
61
  end
62
62
  private_constant :CountDownLatchImplementation
63
63
 
64
- # @!macro [attach] count_down_latch
64
+ # @!macro count_down_latch
65
65
  #
66
66
  # A synchronization object that allows one thread to wait on multiple other threads.
67
67
  # The thread that will wait creates a `CountDownLatch` and sets the initial value
@@ -23,7 +23,7 @@ module Concurrent
23
23
  # # use main as well
24
24
  # process.call 2
25
25
  #
26
- # # here we can be sure that all jobs are processed
26
+ # # here we can be sure that all jobs are processed
27
27
  class CyclicBarrier < Synchronization::LockableObject
28
28
 
29
29
  # @!visibility private
@@ -19,7 +19,7 @@ module Concurrent
19
19
  # t1 = Thread.new do
20
20
  # puts "t1 is waiting"
21
21
  # event.wait(1)
22
- # puts "event ocurred"
22
+ # puts "event occurred"
23
23
  # end
24
24
  #
25
25
  # t2 = Thread.new do
@@ -30,9 +30,9 @@ module Concurrent
30
30
  # [t1, t2].each(&:join)
31
31
  #
32
32
  # # prints:
33
- # # t2 calling set
34
33
  # # t1 is waiting
35
- # # event ocurred
34
+ # # t2 calling set
35
+ # # event occurred
36
36
  class Event < Synchronization::LockableObject
37
37
 
38
38
  # Creates a new `Event` in the unset state. Threads calling `#wait` on the
@@ -9,20 +9,23 @@ if Concurrent.on_jruby?
9
9
 
10
10
  # @!macro count_down_latch_method_initialize
11
11
  def initialize(count = 1)
12
- unless count.is_a?(Fixnum) && count >= 0
13
- raise ArgumentError.new('count must be in integer greater than or equal zero')
14
- end
12
+ Utility::NativeInteger.ensure_integer_and_bounds(count)
13
+ Utility::NativeInteger.ensure_positive(count)
15
14
  @latch = java.util.concurrent.CountDownLatch.new(count)
16
15
  end
17
16
 
18
17
  # @!macro count_down_latch_method_wait
19
18
  def wait(timeout = nil)
19
+ result = nil
20
20
  if timeout.nil?
21
- @latch.await
22
- true
21
+ Synchronization::JRuby.sleep_interruptibly { @latch.await }
22
+ result = true
23
23
  else
24
- @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
24
+ Synchronization::JRuby.sleep_interruptibly do
25
+ result = @latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
26
+ end
25
27
  end
28
+ result
26
29
  end
27
30
 
28
31
  # @!macro count_down_latch_method_count_down
@@ -50,6 +50,8 @@ module Concurrent
50
50
  @value = !!initial
51
51
  end
52
52
 
53
+ private
54
+
53
55
  # @!visibility private
54
56
  def ns_make_value(value)
55
57
  old = @value
@@ -1,4 +1,5 @@
1
1
  require 'concurrent/synchronization'
2
+ require 'concurrent/utility/native_integer'
2
3
 
3
4
  module Concurrent
4
5
 
@@ -23,7 +23,14 @@ module Concurrent
23
23
 
24
24
  synchronize do
25
25
  try_acquire_timed(permits, nil)
26
- nil
26
+ end
27
+
28
+ return unless block_given?
29
+
30
+ begin
31
+ yield
32
+ ensure
33
+ release(permits)
27
34
  end
28
35
  end
29
36
 
@@ -48,13 +55,22 @@ module Concurrent
48
55
  Utility::NativeInteger.ensure_integer_and_bounds permits
49
56
  Utility::NativeInteger.ensure_positive permits
50
57
 
51
- synchronize do
58
+ acquired = synchronize do
52
59
  if timeout.nil?
53
60
  try_acquire_now(permits)
54
61
  else
55
62
  try_acquire_timed(permits, timeout)
56
63
  end
57
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
58
74
  end
59
75
 
60
76
  # @!macro semaphore_method_release
@@ -193,7 +193,8 @@ module Concurrent
193
193
  #
194
194
  # @return [Boolean] true if the lock is successfully released
195
195
  def release_write_lock
196
- c = @Counter.update { |counter| counter-RUNNING_WRITER }
196
+ return true unless running_writer?
197
+ c = @Counter.update { |counter| counter - RUNNING_WRITER }
197
198
  @ReadLock.broadcast
198
199
  @WriteLock.signal if waiting_writers(c) > 0
199
200
  true
@@ -21,7 +21,9 @@ module Concurrent
21
21
  # also acquire a read lock OR a write lock more than once. Only when the read (or
22
22
  # write) lock is released as many times as it was acquired, will the thread
23
23
  # actually let it go, allowing other threads which might have been waiting
24
- # to proceed.
24
+ # to proceed. Therefore the lock can be upgraded by first acquiring
25
+ # read lock and then write lock and that the lock can be downgraded by first
26
+ # having both read and write lock a releasing just the write lock.
25
27
  #
26
28
  # If both read and write locks are acquired by the same thread, it is not strictly
27
29
  # necessary to release them in the same order they were acquired. In other words,
@@ -265,12 +267,10 @@ module Concurrent
265
267
  # running right now, AND no writers who came before us still waiting to
266
268
  # acquire the lock
267
269
  # Additionally, if any read locks have been taken, we must hold all of them
268
- if c == held
269
- # If we successfully swap the RUNNING_WRITER bit on, then we can go ahead
270
- if @Counter.compare_and_set(c, c+RUNNING_WRITER)
271
- @HeldCount.value = held + WRITE_LOCK_HELD
272
- return true
273
- end
270
+ if held > 0 && @Counter.compare_and_set(1, c+RUNNING_WRITER)
271
+ # If we are the only one reader and successfully swap the RUNNING_WRITER bit on, then we can go ahead
272
+ @HeldCount.value = held + WRITE_LOCK_HELD
273
+ return true
274
274
  elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
275
275
  while true
276
276
  # Now we have successfully incremented, so no more readers will be able to increment