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
@@ -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