concurrent-ruby 1.1.5 → 1.2.2

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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +84 -1
  3. data/Gemfile +5 -10
  4. data/{LICENSE.md → LICENSE.txt} +18 -20
  5. data/README.md +55 -31
  6. data/Rakefile +84 -92
  7. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +0 -0
  8. data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +52 -22
  9. data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
  10. data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +2 -1
  11. data/lib/{concurrent → concurrent-ruby/concurrent}/array.rb +6 -16
  12. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +10 -20
  13. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +2 -2
  14. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +7 -6
  15. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +5 -4
  16. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_markable_reference.rb +3 -0
  17. data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +135 -0
  18. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
  19. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
  20. data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
  21. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +1 -0
  22. data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
  23. data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +28 -0
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +11 -5
  25. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +11 -5
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -1
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +19 -3
  28. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
  29. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +9 -9
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +32 -14
  31. data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +111 -0
  32. data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +15 -4
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +1 -1
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +1 -1
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/lock_free_stack.rb +2 -0
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +3 -3
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +16 -8
  39. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +2 -2
  42. data/lib/concurrent-ruby/concurrent/concern/logging.rb +116 -0
  43. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  44. data/lib/concurrent-ruby/concurrent/configuration.rb +105 -0
  45. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +2 -2
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +5 -0
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +1 -0
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +34 -37
  49. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +4 -4
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +2 -2
  51. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +29 -15
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +21 -9
  53. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +19 -2
  55. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +6 -6
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +1 -1
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +5 -2
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +1 -0
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +2 -1
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +0 -1
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/hash.rb +1 -10
  65. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +10 -2
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +2 -1
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/map.rb +44 -31
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +13 -3
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +1 -1
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +2 -1
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/promises.rb +7 -6
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/re_include.rb +2 -0
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +30 -17
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/set.rb +17 -19
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +13 -3
  77. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -1
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +1 -3
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +11 -0
  80. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
  81. data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +3 -1
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -7
  85. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mutex_lockable_object.rb +18 -5
  86. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +12 -44
  87. data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
  88. data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +101 -0
  89. data/lib/concurrent-ruby/concurrent/synchronization.rb +13 -0
  90. data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +47 -0
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +2 -39
  92. data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +52 -0
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +1 -1
  94. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +11 -34
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -5
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -57
  97. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +5 -16
  98. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +19 -0
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_extension_loader.rb +8 -10
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +1 -0
  101. data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +110 -0
  102. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  103. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  104. metadata +127 -129
  105. data/lib/concurrent/atomic/abstract_thread_local_var.rb +0 -66
  106. data/lib/concurrent/atomic/atomic_reference.rb +0 -204
  107. data/lib/concurrent/atomic/java_thread_local_var.rb +0 -37
  108. data/lib/concurrent/atomic/ruby_thread_local_var.rb +0 -161
  109. data/lib/concurrent/atomic/thread_local_var.rb +0 -104
  110. data/lib/concurrent/concern/logging.rb +0 -32
  111. data/lib/concurrent/concurrent_ruby.jar +0 -0
  112. data/lib/concurrent/configuration.rb +0 -184
  113. data/lib/concurrent/synchronization/jruby_object.rb +0 -45
  114. data/lib/concurrent/synchronization/mri_object.rb +0 -44
  115. data/lib/concurrent/synchronization/rbx_lockable_object.rb +0 -65
  116. data/lib/concurrent/synchronization/rbx_object.rb +0 -49
  117. data/lib/concurrent/synchronization/truffleruby_object.rb +0 -47
  118. data/lib/concurrent/synchronization/volatile.rb +0 -36
  119. data/lib/concurrent/synchronization.rb +0 -30
  120. data/lib/concurrent/thread_safe/synchronized_delegator.rb +0 -50
  121. data/lib/concurrent/thread_safe/util/data_structures.rb +0 -63
  122. data/lib/concurrent/utility/at_exit.rb +0 -97
  123. data/lib/concurrent/utility/monotonic_time.rb +0 -58
  124. data/lib/concurrent/utility/processor_counter.rb +0 -158
  125. data/lib/concurrent/version.rb +0 -3
  126. data/lib/concurrent-ruby.rb +0 -1
  127. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +1 -1
  128. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +0 -0
  129. data/lib/{concurrent → concurrent-ruby/concurrent}/atomics.rb +0 -0
  130. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  131. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +0 -0
  132. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  133. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +1 -1
  134. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  135. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  136. /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +0 -0
  137. /data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +0 -0
  138. /data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +0 -0
  139. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  140. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  141. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  142. /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  143. /data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  144. /data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +0 -0
  145. /data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  146. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  147. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  148. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  149. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  150. /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  151. /data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +0 -0
@@ -23,6 +23,9 @@ module Concurrent
23
23
  # @!macro thread_pool_executor_constant_default_thread_timeout
24
24
  DEFAULT_THREAD_IDLETIMEOUT = 60
25
25
 
26
+ # @!macro thread_pool_executor_constant_default_synchronous
27
+ DEFAULT_SYNCHRONOUS = false
28
+
26
29
  # @!macro thread_pool_executor_attr_reader_max_length
27
30
  attr_reader :max_length
28
31
 
@@ -35,6 +38,9 @@ module Concurrent
35
38
  # @!macro thread_pool_executor_attr_reader_max_queue
36
39
  attr_reader :max_queue
37
40
 
41
+ # @!macro thread_pool_executor_attr_reader_synchronous
42
+ attr_reader :synchronous
43
+
38
44
  # @!macro thread_pool_executor_method_initialize
39
45
  def initialize(opts = {})
40
46
  super(opts)
@@ -87,13 +93,8 @@ module Concurrent
87
93
  end
88
94
 
89
95
  # @!visibility private
90
- def ready_worker(worker)
91
- synchronize { ns_ready_worker worker }
92
- end
93
-
94
- # @!visibility private
95
- def worker_not_old_enough(worker)
96
- synchronize { ns_worker_not_old_enough worker }
96
+ def ready_worker(worker, last_message)
97
+ synchronize { ns_ready_worker worker, last_message }
97
98
  end
98
99
 
99
100
  # @!visibility private
@@ -106,6 +107,11 @@ module Concurrent
106
107
  synchronize { @completed_task_count += 1 }
107
108
  end
108
109
 
110
+ # @!macro thread_pool_executor_method_prune_pool
111
+ def prune_pool
112
+ synchronize { ns_prune_pool }
113
+ end
114
+
109
115
  private
110
116
 
111
117
  # @!visibility private
@@ -114,16 +120,16 @@ module Concurrent
114
120
  @max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
115
121
  @idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
116
122
  @max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
123
+ @synchronous = opts.fetch(:synchronous, DEFAULT_SYNCHRONOUS)
117
124
  @fallback_policy = opts.fetch(:fallback_policy, :abort)
118
- raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
119
125
 
126
+ raise ArgumentError.new("`synchronous` cannot be set unless `max_queue` is 0") if @synchronous && @max_queue > 0
127
+ raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
120
128
  raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE
121
129
  raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE
122
130
  raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE
123
131
  raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length
124
132
 
125
- self.auto_terminate = opts.fetch(:auto_terminate, true)
126
-
127
133
  @pool = [] # all workers
128
134
  @ready = [] # used as a stash (most idle worker is at the start)
129
135
  @queue = [] # used as queue
@@ -131,6 +137,7 @@ module Concurrent
131
137
  @scheduled_task_count = 0
132
138
  @completed_task_count = 0
133
139
  @largest_length = 0
140
+ @workers_counter = 0
134
141
  @ruby_pid = $$ # detects if Ruby has forked
135
142
 
136
143
  @gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented
@@ -149,10 +156,11 @@ module Concurrent
149
156
  if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task)
150
157
  @scheduled_task_count += 1
151
158
  else
152
- handle_fallback(*args, &task)
159
+ return fallback_action(*args, &task)
153
160
  end
154
161
 
155
162
  ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
163
+ nil
156
164
  end
157
165
 
158
166
  # @!visibility private
@@ -185,7 +193,7 @@ module Concurrent
185
193
  # @!visibility private
186
194
  def ns_assign_worker(*args, &task)
187
195
  # keep growing if the pool is not at the minimum yet
188
- worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
196
+ worker, _ = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
189
197
  if worker
190
198
  worker << [task, args]
191
199
  true
@@ -202,6 +210,8 @@ module Concurrent
202
210
  #
203
211
  # @!visibility private
204
212
  def ns_enqueue(*args, &task)
213
+ return false if @synchronous
214
+
205
215
  if !ns_limited_queue? || @queue.size < @max_queue
206
216
  @queue << [task, args]
207
217
  true
@@ -214,7 +224,7 @@ module Concurrent
214
224
  def ns_worker_died(worker)
215
225
  ns_remove_busy_worker worker
216
226
  replacement_worker = ns_add_busy_worker
217
- ns_ready_worker replacement_worker, false if replacement_worker
227
+ ns_ready_worker replacement_worker, Concurrent.monotonic_time, false if replacement_worker
218
228
  end
219
229
 
220
230
  # creates new worker which has to receive work to do after it's added
@@ -224,7 +234,8 @@ module Concurrent
224
234
  def ns_add_busy_worker
225
235
  return if @pool.size >= @max_length
226
236
 
227
- @pool << (worker = Worker.new(self))
237
+ @workers_counter += 1
238
+ @pool << (worker = Worker.new(self, @workers_counter))
228
239
  @largest_length = @pool.length if @pool.length > @largest_length
229
240
  worker
230
241
  end
@@ -232,29 +243,21 @@ module Concurrent
232
243
  # handle ready worker, giving it new job or assigning back to @ready
233
244
  #
234
245
  # @!visibility private
235
- def ns_ready_worker(worker, success = true)
246
+ def ns_ready_worker(worker, last_message, success = true)
236
247
  task_and_args = @queue.shift
237
248
  if task_and_args
238
249
  worker << task_and_args
239
250
  else
240
251
  # stop workers when !running?, do not return them to @ready
241
252
  if running?
242
- @ready.push(worker)
253
+ raise unless last_message
254
+ @ready.push([worker, last_message])
243
255
  else
244
256
  worker.stop
245
257
  end
246
258
  end
247
259
  end
248
260
 
249
- # returns back worker to @ready which was not idle for enough time
250
- #
251
- # @!visibility private
252
- def ns_worker_not_old_enough(worker)
253
- # let's put workers coming from idle_test back to the start (as the oldest worker)
254
- @ready.unshift(worker)
255
- true
256
- end
257
-
258
261
  # removes a worker which is not in not tracked in @ready
259
262
  #
260
263
  # @!visibility private
@@ -268,10 +271,17 @@ module Concurrent
268
271
  #
269
272
  # @!visibility private
270
273
  def ns_prune_pool
271
- return if @pool.size <= @min_length
272
-
273
- last_used = @ready.shift
274
- last_used << :idle_test if last_used
274
+ now = Concurrent.monotonic_time
275
+ stopped_workers = 0
276
+ while !@ready.empty? && (@pool.size - stopped_workers > @min_length)
277
+ worker, last_message = @ready.first
278
+ if now - last_message > self.idletime
279
+ stopped_workers += 1
280
+ @ready.shift
281
+ worker << :stop
282
+ else break
283
+ end
284
+ end
275
285
 
276
286
  @next_gc_time = Concurrent.monotonic_time + @gc_interval
277
287
  end
@@ -284,6 +294,7 @@ module Concurrent
284
294
  @scheduled_task_count = 0
285
295
  @completed_task_count = 0
286
296
  @largest_length = 0
297
+ @workers_counter = 0
287
298
  @ruby_pid = $$
288
299
  end
289
300
  end
@@ -292,11 +303,15 @@ module Concurrent
292
303
  class Worker
293
304
  include Concern::Logging
294
305
 
295
- def initialize(pool)
306
+ def initialize(pool, id)
296
307
  # instance variables accessed only under pool's lock so no need to sync here again
297
308
  @queue = Queue.new
298
309
  @pool = pool
299
310
  @thread = create_worker @queue, pool, pool.idletime
311
+
312
+ if @thread.respond_to?(:name=)
313
+ @thread.name = [pool.name, 'worker', id].compact.join('-')
314
+ end
300
315
  end
301
316
 
302
317
  def <<(message)
@@ -315,19 +330,10 @@ module Concurrent
315
330
 
316
331
  def create_worker(queue, pool, idletime)
317
332
  Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime|
318
- last_message = Concurrent.monotonic_time
319
333
  catch(:stop) do
320
334
  loop do
321
335
 
322
336
  case message = my_queue.pop
323
- when :idle_test
324
- if (Concurrent.monotonic_time - last_message) > my_idletime
325
- my_pool.remove_busy_worker(self)
326
- throw :stop
327
- else
328
- my_pool.worker_not_old_enough(self)
329
- end
330
-
331
337
  when :stop
332
338
  my_pool.remove_busy_worker(self)
333
339
  throw :stop
@@ -335,9 +341,7 @@ module Concurrent
335
341
  else
336
342
  task, args = message
337
343
  run_task my_pool, task, args
338
- last_message = Concurrent.monotonic_time
339
-
340
- my_pool.ready_worker(self)
344
+ my_pool.ready_worker(self, Concurrent.monotonic_time)
341
345
  end
342
346
  end
343
347
  end
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/lockable_object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -16,10 +16,10 @@ module Concurrent
16
16
 
17
17
  # @return [Array]
18
18
  def execute(*args)
19
- synchronize do
20
- success = false
21
- value = reason = nil
19
+ success = true
20
+ value = reason = nil
22
21
 
22
+ synchronize do
23
23
  begin
24
24
  value = @task.call(*args)
25
25
  success = true
@@ -27,9 +27,9 @@ module Concurrent
27
27
  reason = ex
28
28
  success = false
29
29
  end
30
-
31
- [success, value, reason]
32
30
  end
31
+
32
+ [success, value, reason]
33
33
  end
34
34
  end
35
35
  end
@@ -1,6 +1,6 @@
1
1
  require 'concurrent/errors'
2
2
  require 'concurrent/concern/logging'
3
- require 'concurrent/synchronization'
3
+ require 'concurrent/synchronization/lockable_object'
4
4
 
5
5
  module Concurrent
6
6
 
@@ -1,5 +1,8 @@
1
- require 'concurrent/atomics'
1
+ require 'concurrent/atomic/atomic_boolean'
2
+ require 'concurrent/atomic/atomic_fixnum'
3
+ require 'concurrent/atomic/event'
2
4
  require 'concurrent/executor/executor_service'
5
+ require 'concurrent/executor/ruby_executor_service'
3
6
 
4
7
  module Concurrent
5
8
 
@@ -91,7 +94,7 @@ module Concurrent
91
94
 
92
95
  private
93
96
 
94
- def ns_initialize
97
+ def ns_initialize(*args)
95
98
  @running = Concurrent::AtomicBoolean.new(true)
96
99
  @stopped = Concurrent::Event.new
97
100
  @count = Concurrent::AtomicFixnum.new(0)
@@ -1,3 +1,4 @@
1
+ require 'concurrent/utility/engine'
1
2
  require 'concurrent/executor/ruby_single_thread_executor'
2
3
 
3
4
  module Concurrent
@@ -73,7 +73,8 @@ module Concurrent
73
73
  # @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
74
74
  # tasks that are received when the queue size has reached
75
75
  # `max_queue` or the executor has shut down
76
- #
76
+ # @option opts [Boolean] :synchronous (DEFAULT_SYNCHRONOUS) whether or not a value of 0
77
+ # for :max_queue means the queue must perform direct hand-off rather than unbounded.
77
78
  # @raise [ArgumentError] if `:max_threads` is less than one
78
79
  # @raise [ArgumentError] if `:min_threads` is less than zero
79
80
  # @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
@@ -77,7 +77,6 @@ module Concurrent
77
77
  @timer_executor = SingleThreadExecutor.new
78
78
  @condition = Event.new
79
79
  @ruby_pid = $$ # detects if Ruby has forked
80
- self.auto_terminate = opts.fetch(:auto_terminate, true)
81
80
  end
82
81
 
83
82
  # Post the task to the internal queue.
@@ -10,7 +10,7 @@ module Concurrent
10
10
  # or writing at a time. This includes iteration methods like `#each`,
11
11
  # which takes the lock repeatedly when reading an item.
12
12
  #
13
- # @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash`
13
+ # @see http://ruby-doc.org/core/Hash.html Ruby standard library `Hash`
14
14
 
15
15
  # @!macro internal_implementation_note
16
16
  HashImplementation = case
@@ -28,15 +28,6 @@ module Concurrent
28
28
  end
29
29
  JRubyHash
30
30
 
31
- when Concurrent.on_rbx?
32
- require 'monitor'
33
- require 'concurrent/thread_safe/util/data_structures'
34
-
35
- class RbxHash < ::Hash
36
- end
37
- ThreadSafe::Util.make_synchronized_on_rbx RbxHash
38
- RbxHash
39
-
40
31
  when Concurrent.on_truffleruby?
41
32
  require 'concurrent/thread_safe/util/data_structures'
42
33
 
@@ -1,11 +1,11 @@
1
1
  require 'concurrent/synchronization/abstract_struct'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
6
6
  # A thread-safe, immutable variation of Ruby's standard `Struct`.
7
7
  #
8
- # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
8
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
9
9
  module ImmutableStruct
10
10
  include Synchronization::AbstractStruct
11
11
 
@@ -70,6 +70,14 @@ module Concurrent
70
70
  ns_select(&block)
71
71
  end
72
72
 
73
+ private
74
+
75
+ # @!visibility private
76
+ def initialize_copy(original)
77
+ super(original)
78
+ ns_initialize_copy
79
+ end
80
+
73
81
  # @!macro struct_new
74
82
  def self.new(*args, &block)
75
83
  clazz_name = nil
@@ -3,7 +3,8 @@ require 'concurrent/errors'
3
3
  require 'concurrent/collection/copy_on_write_observer_set'
4
4
  require 'concurrent/concern/obligation'
5
5
  require 'concurrent/concern/observable'
6
- require 'concurrent/synchronization'
6
+ require 'concurrent/executor/safe_task_executor'
7
+ require 'concurrent/synchronization/lockable_object'
7
8
 
8
9
  module Concurrent
9
10
 
@@ -1,6 +1,5 @@
1
1
  require 'thread'
2
2
  require 'concurrent/constants'
3
- require 'concurrent/synchronization'
4
3
  require 'concurrent/utility/engine'
5
4
 
6
5
  module Concurrent
@@ -10,14 +9,20 @@ module Concurrent
10
9
  # @!visibility private
11
10
  MapImplementation = case
12
11
  when Concurrent.on_jruby?
12
+ require 'concurrent/utility/native_extension_loader'
13
13
  # noinspection RubyResolve
14
14
  JRubyMapBackend
15
15
  when Concurrent.on_cruby?
16
16
  require 'concurrent/collection/map/mri_map_backend'
17
17
  MriMapBackend
18
- when Concurrent.on_rbx? || Concurrent.on_truffleruby?
19
- require 'concurrent/collection/map/atomic_reference_map_backend'
20
- AtomicReferenceMapBackend
18
+ when Concurrent.on_truffleruby?
19
+ if defined?(::TruffleRuby::ConcurrentMap)
20
+ require 'concurrent/collection/map/truffleruby_map_backend'
21
+ TruffleRubyMapBackend
22
+ else
23
+ require 'concurrent/collection/map/atomic_reference_map_backend'
24
+ AtomicReferenceMapBackend
25
+ end
21
26
  else
22
27
  warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
23
28
  require 'concurrent/collection/map/synchronized_map_backend'
@@ -41,6 +46,12 @@ module Concurrent
41
46
  # @note Atomic methods taking a block do not allow the `self` instance
42
47
  # to be used within the block. Doing so will cause a deadlock.
43
48
 
49
+ # @!method []=(key, value)
50
+ # Set a value with key
51
+ # @param [Object] key
52
+ # @param [Object] value
53
+ # @return [Object] the new value
54
+
44
55
  # @!method compute_if_absent(key)
45
56
  # Compute and store new value for key if the key is absent.
46
57
  # @param [Object] key
@@ -114,37 +125,41 @@ module Concurrent
114
125
  # @return [true, false] true if deleted
115
126
  # @!macro map.atomic_method
116
127
 
128
+ # NonConcurrentMapBackend handles default_proc natively
129
+ unless defined?(Collection::NonConcurrentMapBackend) and self < Collection::NonConcurrentMapBackend
117
130
 
118
- def initialize(options = nil, &block)
119
- if options.kind_of?(::Hash)
120
- validate_options_hash!(options)
121
- else
122
- options = nil
123
- end
131
+ # @param [Hash, nil] options options to set the :initial_capacity or :load_factor. Ignored on some Rubies.
132
+ # @param [Proc] default_proc Optional block to compute the default value if the key is not set, like `Hash#default_proc`
133
+ def initialize(options = nil, &default_proc)
134
+ if options.kind_of?(::Hash)
135
+ validate_options_hash!(options)
136
+ else
137
+ options = nil
138
+ end
124
139
 
125
- super(options)
126
- @default_proc = block
127
- end
140
+ super(options)
141
+ @default_proc = default_proc
142
+ end
128
143
 
129
- # Get a value with key
130
- # @param [Object] key
131
- # @return [Object] the value
132
- def [](key)
133
- if value = super # non-falsy value is an existing mapping, return it right away
134
- value
135
- # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
136
- # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
137
- # would be returned)
138
- # note: nil == value check is not technically necessary
139
- elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
140
- @default_proc.call(self, key)
141
- else
142
- value
144
+ # Get a value with key
145
+ # @param [Object] key
146
+ # @return [Object] the value
147
+ def [](key)
148
+ if value = super # non-falsy value is an existing mapping, return it right away
149
+ value
150
+ # re-check is done with get_or_default(key, NULL) instead of a simple !key?(key) in order to avoid a race condition, whereby by the time the current thread gets to the key?(key) call
151
+ # a key => value mapping might have already been created by a different thread (key?(key) would then return true, this elsif branch wouldn't be taken and an incorrent +nil+ value
152
+ # would be returned)
153
+ # note: nil == value check is not technically necessary
154
+ elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
155
+ @default_proc.call(self, key)
156
+ else
157
+ value
158
+ end
143
159
  end
144
160
  end
145
161
 
146
162
  alias_method :get, :[]
147
- # TODO (pitr-ch 30-Oct-2018): doc
148
163
  alias_method :put, :[]=
149
164
 
150
165
  # Get a value with key, or default_value when key is absent,
@@ -187,7 +202,6 @@ module Concurrent
187
202
  # @yieldparam key [Object]
188
203
  # @yieldreturn [Object] default value
189
204
  # @return [Object] the value or default value
190
- # @!macro map.atomic_method_with_block
191
205
  def fetch_or_store(key, default_value = NULL)
192
206
  fetch(key) do
193
207
  put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
@@ -197,7 +211,7 @@ module Concurrent
197
211
  # Insert value into map with key if key is absent in one atomic step.
198
212
  # @param [Object] key
199
213
  # @param [Object] value
200
- # @return [Object, nil] the value or nil when key was present
214
+ # @return [Object, nil] the previous value when key was present or nil when there was no key
201
215
  def put_if_absent(key, value)
202
216
  computed = false
203
217
  result = compute_if_absent(key) do
@@ -271,7 +285,6 @@ module Concurrent
271
285
  each_pair { |k, v| return k if v == value }
272
286
  nil
273
287
  end unless method_defined?(:key)
274
- alias_method :index, :key if RUBY_VERSION < '1.9'
275
288
 
276
289
  # Is map empty?
277
290
  # @return [true, false]
@@ -1,4 +1,4 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
 
3
3
  module Concurrent
4
4
 
@@ -1,12 +1,12 @@
1
1
  require 'concurrent/synchronization/abstract_struct'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/lockable_object'
3
3
 
4
4
  module Concurrent
5
5
 
6
6
  # An thread-safe variation of Ruby's standard `Struct`. Values can be set at
7
7
  # construction or safely changed at any time during the object's lifecycle.
8
8
  #
9
- # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
9
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
10
10
  module MutableStruct
11
11
  include Synchronization::AbstractStruct
12
12
 
@@ -40,7 +40,7 @@ module Concurrent
40
40
  # struct. Unset parameters default to nil. Passing more parameters than number of attributes
41
41
  # will raise an `ArgumentError`.
42
42
  #
43
- # @see http://ruby-doc.org/core-2.2.0/Struct.html#method-c-new Ruby standard library `Struct#new`
43
+ # @see http://ruby-doc.org/core/Struct.html#method-c-new Ruby standard library `Struct#new`
44
44
 
45
45
  # @!macro struct_values
46
46
  #
@@ -196,6 +196,16 @@ module Concurrent
196
196
  raise NameError.new("no member '#{member}' in struct")
197
197
  end
198
198
 
199
+ private
200
+
201
+ # @!visibility private
202
+ def initialize_copy(original)
203
+ synchronize do
204
+ super(original)
205
+ ns_initialize_copy
206
+ end
207
+ end
208
+
199
209
  # @!macro struct_new
200
210
  def self.new(*args, &block)
201
211
  clazz_name = nil
@@ -1,5 +1,5 @@
1
1
  require 'concurrent/concern/dereferenceable'
2
- require 'concurrent/synchronization'
2
+ require 'concurrent/synchronization/object'
3
3
 
4
4
  module Concurrent
5
5
 
@@ -66,7 +66,7 @@ module Concurrent
66
66
  # Start by requiring promises
67
67
  #
68
68
  # ```ruby
69
- # require 'concurrent'
69
+ # require 'concurrent/promise'
70
70
  # ```
71
71
  #
72
72
  # Then create one
@@ -250,6 +250,7 @@ module Concurrent
250
250
  realize(@promise_body)
251
251
  end
252
252
  else
253
+ compare_and_set_state(:pending, :unscheduled)
253
254
  @parent.execute
254
255
  end
255
256
  self
@@ -1,7 +1,8 @@
1
- require 'concurrent/synchronization'
1
+ require 'concurrent/synchronization/object'
2
2
  require 'concurrent/atomic/atomic_boolean'
3
3
  require 'concurrent/atomic/atomic_fixnum'
4
4
  require 'concurrent/collection/lock_free_stack'
5
+ require 'concurrent/configuration'
5
6
  require 'concurrent/errors'
6
7
  require 'concurrent/re_include'
7
8
 
@@ -313,7 +314,7 @@ module Concurrent
313
314
  end
314
315
 
315
316
  # @!macro promises.shortcut.on
316
- # @return [Future]
317
+ # @return [Event]
317
318
  def any_event(*futures_and_or_events)
318
319
  any_event_on default_executor, *futures_and_or_events
319
320
  end
@@ -892,7 +893,7 @@ module Concurrent
892
893
  private
893
894
 
894
895
  def rejected_resolution(raise_on_reassign, state)
895
- Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
+ raise Concurrent::MultipleAssignmentError.new('Event can be resolved only once') if raise_on_reassign
896
897
  return false
897
898
  end
898
899
 
@@ -1298,7 +1299,7 @@ module Concurrent
1298
1299
 
1299
1300
  # @!macro promise.param.raise_on_reassign
1300
1301
  # @param [Boolean] raise_on_reassign should method raise exception if already resolved
1301
- # @return [self, false] false is returner when raise_on_reassign is false and the receiver
1302
+ # @return [self, false] false is returned when raise_on_reassign is false and the receiver
1302
1303
  # is already resolved.
1303
1304
  #
1304
1305
 
@@ -2074,8 +2075,8 @@ module Concurrent
2074
2075
 
2075
2076
  private
2076
2077
 
2077
- def resolvable?(countdown, future, index)
2078
- future.fulfilled? ||
2078
+ def resolvable?(countdown, event_or_future, index)
2079
+ (event_or_future.is_a?(Event) ? event_or_future.resolved? : event_or_future.fulfilled?) ||
2079
2080
  # inlined super from BlockedPromise
2080
2081
  countdown.zero?
2081
2082
  end
@@ -31,6 +31,8 @@ module Concurrent
31
31
  #
32
32
  # C1.new.respond_to? :a # => false
33
33
  # C2.new.respond_to? :a # => true
34
+ #
35
+ # @!visibility private
34
36
  module ReInclude
35
37
  # @!visibility private
36
38
  def included(base)