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
@@ -28,16 +28,31 @@ module Concurrent
28
28
  # But when a Thread is GC'd, we need to drop the reference to its thread-local
29
29
  # array, so we don't leak memory
30
30
 
31
- # @!visibility private
32
- FREE = []
33
- LOCK = Mutex.new
34
- ARRAYS = {} # used as a hash set
35
- @@next = 0
36
- private_constant :FREE, :LOCK, :ARRAYS
31
+ FREE = []
32
+ LOCK = Mutex.new
33
+ THREAD_LOCAL_ARRAYS = {} # used as a hash set
34
+
35
+ # synchronize when not on MRI
36
+ # on MRI using lock in finalizer leads to "can't be called from trap context" error
37
+ # so the code is carefully written to be tread-safe on MRI relying on GIL
38
+
39
+ if Concurrent.on_cruby?
40
+ # @!visibility private
41
+ def self.semi_sync(&block)
42
+ block.call
43
+ end
44
+ else
45
+ # @!visibility private
46
+ def self.semi_sync(&block)
47
+ LOCK.synchronize(&block)
48
+ end
49
+ end
50
+
51
+ private_constant :FREE, :LOCK, :THREAD_LOCAL_ARRAYS
37
52
 
38
53
  # @!macro thread_local_var_method_get
39
54
  def value
40
- if array = get_threadlocal_array
55
+ if (array = get_threadlocal_array)
41
56
  value = array[@index]
42
57
  if value.nil?
43
58
  default
@@ -57,10 +72,10 @@ module Concurrent
57
72
  # We could keep the thread-local arrays in a hash, keyed by Thread
58
73
  # But why? That would require locking
59
74
  # Using Ruby's built-in thread-local storage is faster
60
- unless array = get_threadlocal_array(me)
75
+ unless (array = get_threadlocal_array(me))
61
76
  array = set_threadlocal_array([], me)
62
- LOCK.synchronize { ARRAYS[array.object_id] = array }
63
- ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array))
77
+ self.class.semi_sync { THREAD_LOCAL_ARRAYS[array.object_id] = array }
78
+ ObjectSpace.define_finalizer(me, self.class.thread_finalizer(array.object_id))
64
79
  end
65
80
  array[@index] = (value.nil? ? NULL : value)
66
81
  value
@@ -70,48 +85,52 @@ module Concurrent
70
85
 
71
86
  # @!visibility private
72
87
  def allocate_storage
73
- @index = LOCK.synchronize do
74
- FREE.pop || begin
75
- result = @@next
76
- @@next += 1
77
- result
78
- end
79
- end
80
- ObjectSpace.define_finalizer(self, self.class.threadlocal_finalizer(@index))
88
+ @index = FREE.pop || next_index
89
+
90
+ ObjectSpace.define_finalizer(self, self.class.thread_local_finalizer(@index))
81
91
  end
82
92
 
83
93
  # @!visibility private
84
- def self.threadlocal_finalizer(index)
94
+ def self.thread_local_finalizer(index)
85
95
  proc do
86
- Thread.new do # avoid error: can't be called from trap context
87
- LOCK.synchronize do
88
- FREE.push(index)
89
- # The cost of GC'ing a TLV is linear in the number of threads using TLVs
90
- # But that is natural! More threads means more storage is used per TLV
91
- # So naturally more CPU time is required to free more storage
92
- ARRAYS.each_value do |array|
93
- array[index] = nil
94
- end
95
- end
96
+ semi_sync do
97
+ # The cost of GC'ing a TLV is linear in the number of threads using TLVs
98
+ # But that is natural! More threads means more storage is used per TLV
99
+ # So naturally more CPU time is required to free more storage
100
+ #
101
+ # DO NOT use each_value which might conflict with new pair assignment
102
+ # into the hash in #value= method
103
+ THREAD_LOCAL_ARRAYS.values.each { |array| array[index] = nil }
104
+ # free index has to be published after the arrays are cleared
105
+ FREE.push(index)
96
106
  end
97
107
  end
98
108
  end
99
109
 
100
110
  # @!visibility private
101
- def self.thread_finalizer(array)
111
+ def self.thread_finalizer(id)
102
112
  proc do
103
- Thread.new do # avoid error: can't be called from trap context
104
- LOCK.synchronize do
105
- # The thread which used this thread-local array is now gone
106
- # So don't hold onto a reference to the array (thus blocking GC)
107
- ARRAYS.delete(array.object_id)
108
- end
113
+ semi_sync do
114
+ # The thread which used this thread-local array is now gone
115
+ # So don't hold onto a reference to the array (thus blocking GC)
116
+ THREAD_LOCAL_ARRAYS.delete(id)
109
117
  end
110
118
  end
111
119
  end
112
120
 
113
121
  private
114
122
 
123
+ # noinspection RubyClassVariableUsageInspection
124
+ @@next = 0
125
+ # noinspection RubyClassVariableUsageInspection
126
+ def next_index
127
+ LOCK.synchronize do
128
+ result = @@next
129
+ @@next += 1
130
+ result
131
+ end
132
+ end
133
+
115
134
  if Thread.instance_methods.include?(:thread_variable_get)
116
135
 
117
136
  def get_threadlocal_array(thread = Thread.current)
@@ -136,21 +155,22 @@ module Concurrent
136
155
  # This exists only for use in testing
137
156
  # @!visibility private
138
157
  def value_for(thread)
139
- if array = get_threadlocal_array(thread)
158
+ if (array = get_threadlocal_array(thread))
140
159
  value = array[@index]
141
160
  if value.nil?
142
- default_for(thread)
161
+ get_default
143
162
  elsif value.equal?(NULL)
144
163
  nil
145
164
  else
146
165
  value
147
166
  end
148
167
  else
149
- default_for(thread)
168
+ get_default
150
169
  end
151
170
  end
152
171
 
153
- def default_for(thread)
172
+ # @!visibility private
173
+ def get_default
154
174
  if @default_block
155
175
  raise "Cannot use default_for with default block"
156
176
  else
@@ -5,7 +5,7 @@ module Concurrent
5
5
 
6
6
  ###################################################################
7
7
 
8
- # @!macro [new] semaphore_method_initialize
8
+ # @!macro semaphore_method_initialize
9
9
  #
10
10
  # Create a new `Semaphore` with the initial `count`.
11
11
  #
@@ -13,35 +13,39 @@ module Concurrent
13
13
  #
14
14
  # @raise [ArgumentError] if `count` is not an integer or is less than zero
15
15
 
16
- # @!macro [new] semaphore_method_acquire
16
+ # @!macro semaphore_method_acquire
17
17
  #
18
18
  # Acquires the given number of permits from this semaphore,
19
- # blocking until all are available.
19
+ # blocking until all are available. If a block is given,
20
+ # yields to it and releases the permits afterwards.
20
21
  #
21
22
  # @param [Fixnum] permits Number of permits to acquire
22
23
  #
23
24
  # @raise [ArgumentError] if `permits` is not an integer or is less than
24
25
  # one
25
26
  #
26
- # @return [nil]
27
+ # @return [nil, BasicObject] Without a block, `nil` is returned. If a block
28
+ # is given, its return value is returned.
27
29
 
28
- # @!macro [new] semaphore_method_available_permits
30
+ # @!macro semaphore_method_available_permits
29
31
  #
30
32
  # Returns the current number of permits available in this semaphore.
31
33
  #
32
34
  # @return [Integer]
33
35
 
34
- # @!macro [new] semaphore_method_drain_permits
36
+ # @!macro semaphore_method_drain_permits
35
37
  #
36
38
  # Acquires and returns all permits that are immediately available.
37
39
  #
38
40
  # @return [Integer]
39
41
 
40
- # @!macro [new] semaphore_method_try_acquire
42
+ # @!macro semaphore_method_try_acquire
41
43
  #
42
44
  # Acquires the given number of permits from this semaphore,
43
45
  # only if all are available at the time of invocation or within
44
- # `timeout` interval
46
+ # `timeout` interval. If a block is given, yields to it if the permits
47
+ # were successfully acquired, and releases them afterward, returning the
48
+ # block's return value.
45
49
  #
46
50
  # @param [Fixnum] permits the number of permits to acquire
47
51
  #
@@ -51,10 +55,12 @@ module Concurrent
51
55
  # @raise [ArgumentError] if `permits` is not an integer or is less than
52
56
  # one
53
57
  #
54
- # @return [Boolean] `false` if no permits are available, `true` when
55
- # acquired a permit
58
+ # @return [true, false, nil, BasicObject] `false` if no permits are
59
+ # available, `true` when acquired a permit. If a block is given, the
60
+ # block's return value is returned if the permits were acquired; if not,
61
+ # `nil` is returned.
56
62
 
57
- # @!macro [new] semaphore_method_release
63
+ # @!macro semaphore_method_release
58
64
  #
59
65
  # Releases the given number of permits, returning them to the semaphore.
60
66
  #
@@ -66,7 +72,7 @@ module Concurrent
66
72
 
67
73
  ###################################################################
68
74
 
69
- # @!macro [new] semaphore_public_api
75
+ # @!macro semaphore_public_api
70
76
  #
71
77
  # @!method initialize(count)
72
78
  # @!macro semaphore_method_initialize
@@ -98,7 +104,7 @@ module Concurrent
98
104
  end
99
105
  private_constant :SemaphoreImplementation
100
106
 
101
- # @!macro [attach] semaphore
107
+ # @!macro semaphore
102
108
  #
103
109
  # A counting semaphore. Conceptually, a semaphore maintains a set of
104
110
  # permits. Each {#acquire} blocks if necessary until a permit is
@@ -106,6 +112,8 @@ module Concurrent
106
112
  # releasing a blocking acquirer.
107
113
  # However, no actual permit objects are used; the Semaphore just keeps a
108
114
  # count of the number available and acts accordingly.
115
+ # Alternatively, permits may be acquired within a block, and automatically
116
+ # released after the block finishes executing.
109
117
  #
110
118
  # @!macro semaphore_public_api
111
119
  # @example
@@ -140,6 +148,19 @@ module Concurrent
140
148
  # # Thread 4 releasing semaphore
141
149
  # # Thread 1 acquired semaphore
142
150
  #
151
+ # @example
152
+ # semaphore = Concurrent::Semaphore.new(1)
153
+ #
154
+ # puts semaphore.available_permits
155
+ # semaphore.acquire do
156
+ # puts semaphore.available_permits
157
+ # end
158
+ # puts semaphore.available_permits
159
+ #
160
+ # # prints:
161
+ # # 1
162
+ # # 0
163
+ # # 1
143
164
  class Semaphore < SemaphoreImplementation
144
165
  end
145
166
  end
@@ -1,12 +1,12 @@
1
+ require 'concurrent/utility/engine'
1
2
  require 'concurrent/atomic/ruby_thread_local_var'
2
3
  require 'concurrent/atomic/java_thread_local_var'
3
- require 'concurrent/utility/engine'
4
4
 
5
5
  module Concurrent
6
6
 
7
7
  ###################################################################
8
8
 
9
- # @!macro [new] thread_local_var_method_initialize
9
+ # @!macro thread_local_var_method_initialize
10
10
  #
11
11
  # Creates a thread local variable.
12
12
  #
@@ -14,20 +14,20 @@ module Concurrent
14
14
  # @param [Proc] default_block Optional block that gets called to obtain the
15
15
  # default value for each thread
16
16
 
17
- # @!macro [new] thread_local_var_method_get
17
+ # @!macro thread_local_var_method_get
18
18
  #
19
19
  # Returns the value in the current thread's copy of this thread-local variable.
20
20
  #
21
21
  # @return [Object] the current value
22
22
 
23
- # @!macro [new] thread_local_var_method_set
23
+ # @!macro thread_local_var_method_set
24
24
  #
25
25
  # Sets the current thread's copy of this thread-local variable to the specified value.
26
26
  #
27
27
  # @param [Object] value the value to set
28
28
  # @return [Object] the new value
29
29
 
30
- # @!macro [new] thread_local_var_method_bind
30
+ # @!macro thread_local_var_method_bind
31
31
  #
32
32
  # Bind the given value to thread local storage during
33
33
  # execution of the given block.
@@ -39,9 +39,9 @@ module Concurrent
39
39
 
40
40
  ###################################################################
41
41
 
42
- # @!macro [new] thread_local_var_public_api
42
+ # @!macro thread_local_var_public_api
43
43
  #
44
- # @!method initialize(default = nil)
44
+ # @!method initialize(default = nil, &default_block)
45
45
  # @!macro thread_local_var_method_initialize
46
46
  #
47
47
  # @!method value
@@ -65,7 +65,7 @@ module Concurrent
65
65
  end
66
66
  private_constant :ThreadLocalVarImplementation
67
67
 
68
- # @!macro [attach] thread_local_var
68
+ # @!macro thread_local_var
69
69
  #
70
70
  # A `ThreadLocalVar` is a variable where the value is different for each thread.
71
71
  # Each variable may have a default value, but when you modify the variable only
@@ -1,16 +1,11 @@
1
- require 'concurrent/synchronization'
2
- require 'concurrent/atomic_reference/direct_update'
3
- require 'concurrent/atomic_reference/numeric_cas_wrapper'
4
-
5
1
  module Concurrent
6
2
 
7
- # @!macro atomic_reference
8
- #
9
3
  # @!visibility private
10
4
  # @!macro internal_implementation_note
11
5
  class MutexAtomicReference < Synchronization::LockableObject
12
- include Concurrent::AtomicDirectUpdate
13
- include Concurrent::AtomicNumericCompareAndSetWrapper
6
+ include AtomicDirectUpdate
7
+ include AtomicNumericCompareAndSetWrapper
8
+ alias_method :compare_and_swap, :compare_and_set
14
9
 
15
10
  # @!macro atomic_reference_method_initialize
16
11
  def initialize(value = nil)
@@ -23,6 +23,6 @@ module Concurrent
23
23
  _compare_and_set(old_value, new_value)
24
24
  end
25
25
  end
26
- alias_method :compare_and_swap, :compare_and_set
26
+
27
27
  end
28
28
  end
@@ -0,0 +1,10 @@
1
+ require 'concurrent/atomic/atomic_reference'
2
+ require 'concurrent/atomic/atomic_boolean'
3
+ require 'concurrent/atomic/atomic_fixnum'
4
+ require 'concurrent/atomic/cyclic_barrier'
5
+ require 'concurrent/atomic/count_down_latch'
6
+ require 'concurrent/atomic/event'
7
+ require 'concurrent/atomic/read_write_lock'
8
+ require 'concurrent/atomic/reentrant_read_write_lock'
9
+ require 'concurrent/atomic/semaphore'
10
+ require 'concurrent/atomic/thread_local_var'
@@ -0,0 +1,158 @@
1
+ module Concurrent
2
+
3
+ # @!macro warn.edge
4
+ class LockFreeStack < Synchronization::Object
5
+
6
+ safe_initialization!
7
+
8
+ class Node
9
+ # TODO (pitr-ch 20-Dec-2016): Could be unified with Stack class?
10
+
11
+ # @return [Node]
12
+ attr_reader :next_node
13
+
14
+ # @return [Object]
15
+ attr_reader :value
16
+
17
+ # @!visibility private
18
+ # allow to nil-ify to free GC when the entry is no longer relevant, not synchronised
19
+ attr_writer :value
20
+
21
+ def initialize(value, next_node)
22
+ @value = value
23
+ @next_node = next_node
24
+ end
25
+
26
+ singleton_class.send :alias_method, :[], :new
27
+ end
28
+
29
+ # The singleton for empty node
30
+ EMPTY = Node[nil, nil]
31
+ def EMPTY.next_node
32
+ self
33
+ end
34
+
35
+ attr_atomic(:head)
36
+ private :head, :head=, :swap_head, :compare_and_set_head, :update_head
37
+
38
+ # @!visibility private
39
+ def self.of1(value)
40
+ new Node[value, EMPTY]
41
+ end
42
+
43
+ # @!visibility private
44
+ def self.of2(value1, value2)
45
+ new Node[value1, Node[value2, EMPTY]]
46
+ end
47
+
48
+ # @param [Node] head
49
+ def initialize(head = EMPTY)
50
+ super()
51
+ self.head = head
52
+ end
53
+
54
+ # @param [Node] head
55
+ # @return [true, false]
56
+ def empty?(head = head())
57
+ head.equal? EMPTY
58
+ end
59
+
60
+ # @param [Node] head
61
+ # @param [Object] value
62
+ # @return [true, false]
63
+ def compare_and_push(head, value)
64
+ compare_and_set_head head, Node[value, head]
65
+ end
66
+
67
+ # @param [Object] value
68
+ # @return [self]
69
+ def push(value)
70
+ while true
71
+ current_head = head
72
+ return self if compare_and_set_head current_head, Node[value, current_head]
73
+ end
74
+ end
75
+
76
+ # @return [Node]
77
+ def peek
78
+ head
79
+ end
80
+
81
+ # @param [Node] head
82
+ # @return [true, false]
83
+ def compare_and_pop(head)
84
+ compare_and_set_head head, head.next_node
85
+ end
86
+
87
+ # @return [Object]
88
+ def pop
89
+ while true
90
+ current_head = head
91
+ return current_head.value if compare_and_set_head current_head, current_head.next_node
92
+ end
93
+ end
94
+
95
+ # @param [Node] head
96
+ # @return [true, false]
97
+ def compare_and_clear(head)
98
+ compare_and_set_head head, EMPTY
99
+ end
100
+
101
+ include Enumerable
102
+
103
+ # @param [Node] head
104
+ # @return [self]
105
+ def each(head = nil)
106
+ return to_enum(:each, head) unless block_given?
107
+ it = head || peek
108
+ until it.equal?(EMPTY)
109
+ yield it.value
110
+ it = it.next_node
111
+ end
112
+ self
113
+ end
114
+
115
+ # @return [true, false]
116
+ def clear
117
+ while true
118
+ current_head = head
119
+ return false if current_head == EMPTY
120
+ return true if compare_and_set_head current_head, EMPTY
121
+ end
122
+ end
123
+
124
+ # @param [Node] head
125
+ # @return [true, false]
126
+ def clear_if(head)
127
+ compare_and_set_head head, EMPTY
128
+ end
129
+
130
+ # @param [Node] head
131
+ # @param [Node] new_head
132
+ # @return [true, false]
133
+ def replace_if(head, new_head)
134
+ compare_and_set_head head, new_head
135
+ end
136
+
137
+ # @return [self]
138
+ # @yield over the cleared stack
139
+ # @yieldparam [Object] value
140
+ def clear_each(&block)
141
+ while true
142
+ current_head = head
143
+ return self if current_head == EMPTY
144
+ if compare_and_set_head current_head, EMPTY
145
+ each current_head, &block
146
+ return self
147
+ end
148
+ end
149
+ end
150
+
151
+ # @return [String] Short string representation.
152
+ def to_s
153
+ format '%s %s>', super[0..-2], to_a.to_s
154
+ end
155
+
156
+ alias_method :inspect, :to_s
157
+ end
158
+ end
@@ -289,7 +289,7 @@ module Concurrent
289
289
  end
290
290
  end
291
291
  elsif cas_hash(my_hash, my_hash | WAITING)
292
- force_aquire_lock(table, i)
292
+ force_acquire_lock(table, i)
293
293
  break
294
294
  end
295
295
  end
@@ -330,7 +330,7 @@ module Concurrent
330
330
  end
331
331
 
332
332
  private
333
- def force_aquire_lock(table, i)
333
+ def force_acquire_lock(table, i)
334
334
  cheap_synchronize do
335
335
  if equal?(table.volatile_get(i)) && (hash & WAITING) == WAITING
336
336
  cheap_wait
@@ -831,7 +831,7 @@ module Concurrent
831
831
  # no lock needed (or available) if bin >= 0, because we're not popping values from locked_indexes until we've run through the whole table
832
832
  redo unless (bin >= 0 ? table.cas(i, nil, forwarder) : lock_and_clean_up_reverse_forwarders(table, old_table_size, new_table, i, forwarder))
833
833
  elsif Node.locked_hash?(node_hash = node.hash)
834
- locked_indexes ||= Array.new
834
+ locked_indexes ||= ::Array.new
835
835
  if bin < 0 && locked_arr_idx > 0
836
836
  locked_arr_idx -= 1
837
837
  i, locked_indexes[locked_arr_idx] = locked_indexes[locked_arr_idx], i # swap with another bin
@@ -19,7 +19,7 @@ module Concurrent
19
19
  end
20
20
 
21
21
  def compute_if_absent(key)
22
- if stored_value = _get(key) # fast non-blocking path for the most likely case
22
+ if NULL != (stored_value = @backend.fetch(key, NULL)) # fast non-blocking path for the most likely case
23
23
  stored_value
24
24
  else
25
25
  @write_lock.synchronize { super }
@@ -10,7 +10,7 @@ module Concurrent
10
10
 
11
11
  # WARNING: all public methods of the class must operate on the @backend
12
12
  # directly without calling each other. This is important because of the
13
- # SynchronizedMapBackend which uses a non-reentrant mutex for perfomance
13
+ # SynchronizedMapBackend which uses a non-reentrant mutex for performance
14
14
  # reasons.
15
15
  def initialize(options = nil)
16
16
  @backend = {}
@@ -95,7 +95,6 @@ module Concurrent
95
95
  end
96
96
 
97
97
  def each_pair
98
- return enum_for :each_pair unless block_given?
99
98
  dupped_backend.each_pair do |k, v|
100
99
  yield k, v
101
100
  end
@@ -0,0 +1,14 @@
1
+ module Concurrent
2
+
3
+ # @!visibility private
4
+ module Collection
5
+
6
+ # @!visibility private
7
+ class TruffleRubyMapBackend < TruffleRuby::ConcurrentMap
8
+ def initialize(options = nil)
9
+ options ||= {}
10
+ super(initial_capacity: options[:initial_capacity], load_factor: options[:load_factor])
11
+ end
12
+ end
13
+ end
14
+ end