concurrent-ruby 1.0.5 → 1.1.10

Sign up to get free protection for your applications and to get access to all the features.
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