concurrent-ruby 1.1.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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +65 -1
  3. data/Gemfile +6 -10
  4. data/{LICENSE.md → LICENSE.txt} +18 -20
  5. data/README.md +46 -23
  6. data/Rakefile +46 -44
  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/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +0 -0
  10. data/lib/{concurrent → concurrent-ruby/concurrent}/array.rb +6 -6
  11. data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +10 -20
  12. data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +1 -1
  13. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
  14. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +2 -2
  15. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +0 -0
  16. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_markable_reference.rb +0 -0
  17. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_reference.rb +1 -0
  18. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +1 -1
  19. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +0 -0
  20. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +2 -2
  21. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +0 -0
  22. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
  23. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +0 -0
  24. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
  25. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +0 -0
  26. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +18 -2
  27. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +0 -0
  28. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +4 -6
  29. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
  30. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +26 -5
  31. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +1 -1
  32. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +0 -0
  33. data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +0 -0
  34. data/lib/{concurrent → concurrent-ruby/concurrent}/atomics.rb +0 -0
  35. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
  36. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
  37. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
  38. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/lock_free_stack.rb +0 -0
  39. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +0 -0
  40. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
  41. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +0 -0
  42. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
  43. data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
  44. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +1 -1
  45. data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
  46. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
  47. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +2 -2
  48. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +0 -0
  49. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
  50. data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +0 -0
  51. data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
  52. data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +13 -9
  53. data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +0 -0
  54. data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +0 -0
  55. data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +0 -0
  56. data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
  57. data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +0 -0
  58. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +33 -36
  59. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +4 -4
  60. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +2 -2
  61. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +29 -15
  62. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
  63. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
  64. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +19 -7
  65. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
  66. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +19 -2
  67. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
  68. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
  69. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
  70. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +5 -5
  71. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
  72. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
  73. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
  74. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
  75. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +1 -0
  76. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +2 -1
  77. data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +0 -1
  78. data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
  79. data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +0 -0
  80. data/lib/{concurrent → concurrent-ruby/concurrent}/hash.rb +1 -1
  81. data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
  82. data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +0 -0
  83. data/lib/{concurrent → concurrent-ruby/concurrent}/map.rb +14 -5
  84. data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +0 -0
  85. data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +12 -2
  86. data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +0 -0
  87. data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
  88. data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +1 -0
  89. data/lib/{concurrent → concurrent-ruby/concurrent}/promises.rb +0 -0
  90. data/lib/{concurrent → concurrent-ruby/concurrent}/re_include.rb +0 -0
  91. data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +29 -16
  92. data/lib/{concurrent → concurrent-ruby/concurrent}/set.rb +19 -11
  93. data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +11 -1
  94. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +0 -0
  95. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
  96. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +11 -0
  97. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +0 -0
  98. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
  99. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +0 -0
  100. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +0 -0
  101. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +3 -5
  102. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +0 -0
  103. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mutex_lockable_object.rb +12 -0
  104. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +0 -0
  105. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +6 -0
  106. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +0 -0
  107. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/truffleruby_object.rb +0 -0
  108. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +0 -0
  109. data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +0 -0
  110. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
  111. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
  112. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
  113. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/data_structures.rb +26 -1
  114. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
  115. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +1 -1
  116. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
  117. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
  118. data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
  119. data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +11 -34
  120. data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +0 -0
  121. data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +19 -56
  122. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +0 -0
  123. data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
  124. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_extension_loader.rb +0 -0
  125. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.rb +0 -0
  126. data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -33
  127. data/lib/concurrent-ruby/concurrent/version.rb +3 -0
  128. data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
  129. data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +0 -0
  130. metadata +132 -132
  131. data/lib/concurrent/concurrent_ruby.jar +0 -0
  132. data/lib/concurrent/utility/at_exit.rb +0 -97
  133. data/lib/concurrent/utility/monotonic_time.rb +0 -58
  134. data/lib/concurrent/version.rb +0 -3
  135. data/lib/concurrent-ruby.rb +0 -1
@@ -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
@@ -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
@@ -91,7 +91,7 @@ module Concurrent
91
91
 
92
92
  private
93
93
 
94
- def ns_initialize
94
+ def ns_initialize(*args)
95
95
  @running = Concurrent::AtomicBoolean.new(true)
96
96
  @stopped = Concurrent::Event.new
97
97
  @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
@@ -5,7 +5,7 @@ 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
@@ -15,7 +15,10 @@ module Concurrent
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?
18
+ when Concurrent.on_truffleruby? && defined?(::TruffleRuby::ConcurrentMap)
19
+ require 'concurrent/collection/map/truffleruby_map_backend'
20
+ TruffleRubyMapBackend
21
+ when Concurrent.on_truffleruby? || Concurrent.on_rbx?
19
22
  require 'concurrent/collection/map/atomic_reference_map_backend'
20
23
  AtomicReferenceMapBackend
21
24
  else
@@ -114,7 +117,7 @@ module Concurrent
114
117
  # @return [true, false] true if deleted
115
118
  # @!macro map.atomic_method
116
119
 
117
-
120
+ #
118
121
  def initialize(options = nil, &block)
119
122
  if options.kind_of?(::Hash)
120
123
  validate_options_hash!(options)
@@ -143,8 +146,15 @@ module Concurrent
143
146
  end
144
147
  end
145
148
 
149
+ # Set a value with key
150
+ # @param [Object] key
151
+ # @param [Object] value
152
+ # @return [Object] the new value
153
+ def []=(key, value)
154
+ super
155
+ end
156
+
146
157
  alias_method :get, :[]
147
- # TODO (pitr-ch 30-Oct-2018): doc
148
158
  alias_method :put, :[]=
149
159
 
150
160
  # Get a value with key, or default_value when key is absent,
@@ -197,7 +207,7 @@ module Concurrent
197
207
  # Insert value into map with key if key is absent in one atomic step.
198
208
  # @param [Object] key
199
209
  # @param [Object] value
200
- # @return [Object, nil] the value or nil when key was present
210
+ # @return [Object, nil] the previous value when key was present or nil when there was no key
201
211
  def put_if_absent(key, value)
202
212
  computed = false
203
213
  result = compute_if_absent(key) do
@@ -271,7 +281,6 @@ module Concurrent
271
281
  each_pair { |k, v| return k if v == value }
272
282
  nil
273
283
  end unless method_defined?(:key)
274
- alias_method :index, :key if RUBY_VERSION < '1.9'
275
284
 
276
285
  # Is map empty?
277
286
  # @return [true, false]
@@ -6,7 +6,7 @@ module Concurrent
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
@@ -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
@@ -58,29 +58,42 @@ module Concurrent
58
58
  # @example Basic usage
59
59
  #
60
60
  # require 'concurrent'
61
- # require 'thread' # for Queue
62
- # require 'open-uri' # for open(uri)
61
+ # require 'csv'
62
+ # require 'open-uri'
63
63
  #
64
64
  # class Ticker
65
- # def get_year_end_closing(symbol, year)
66
- # uri = "http://ichart.finance.yahoo.com/table.csv?s=#{symbol}&a=11&b=01&c=#{year}&d=11&e=31&f=#{year}&g=m"
67
- # data = open(uri) {|f| f.collect{|line| line.strip } }
68
- # data[1].split(',')[4].to_f
69
- # end
65
+ # def get_year_end_closing(symbol, year, api_key)
66
+ # uri = "https://www.alphavantage.co/query?function=TIME_SERIES_MONTHLY&symbol=#{symbol}&apikey=#{api_key}&datatype=csv"
67
+ # data = []
68
+ # csv = URI.parse(uri).read
69
+ # if csv.include?('call frequency')
70
+ # return :rate_limit_exceeded
71
+ # end
72
+ # CSV.parse(csv, headers: true) do |row|
73
+ # data << row['close'].to_f if row['timestamp'].include?(year.to_s)
74
+ # end
75
+ # year_end = data.first
76
+ # year_end
77
+ # rescue => e
78
+ # p e
79
+ # end
70
80
  # end
71
81
  #
82
+ # api_key = ENV['ALPHAVANTAGE_KEY']
83
+ # abort(error_message) unless api_key
84
+ #
72
85
  # # Future
73
- # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013) }
86
+ # price = Concurrent::Future.execute{ Ticker.new.get_year_end_closing('TWTR', 2013, api_key) }
74
87
  # price.state #=> :pending
75
- # sleep(1) # do other stuff
76
- # price.value #=> 63.65
77
- # price.state #=> :fulfilled
88
+ # price.pending? #=> true
89
+ # price.value(0) #=> nil (does not block)
78
90
  #
79
- # # ScheduledTask
80
- # task = Concurrent::ScheduledTask.execute(2){ Ticker.new.get_year_end_closing('INTC', 2013) }
81
- # task.state #=> :pending
82
- # sleep(3) # do other stuff
83
- # task.value #=> 25.96
91
+ # sleep(1) # do other stuff
92
+ #
93
+ # price.value #=> 63.65 (after blocking if necessary)
94
+ # price.state #=> :fulfilled
95
+ # price.fulfilled? #=> true
96
+ # price.value #=> 63.65
84
97
  #
85
98
  # @example Successful task execution
86
99
  #
@@ -11,21 +11,27 @@ module Concurrent
11
11
  # or writing at a time. This includes iteration methods like `#each`.
12
12
  #
13
13
  # @note `a += b` is **not** a **thread-safe** operation on
14
- # `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
15
- # which is union of `a` and `b`, then it writes the union to `a`.
16
- # The read and write are independent operations they do not form a single atomic
17
- # operation therefore when two `+=` operations are executed concurrently updates
18
- # may be lost. Use `#merge` instead.
14
+ # `Concurrent::Set`. It reads Set `a`, then it creates new `Concurrent::Set`
15
+ # which is union of `a` and `b`, then it writes the union to `a`.
16
+ # The read and write are independent operations they do not form a single atomic
17
+ # operation therefore when two `+=` operations are executed concurrently updates
18
+ # may be lost. Use `#merge` instead.
19
19
  #
20
20
  # @see http://ruby-doc.org/stdlib-2.4.0/libdoc/set/rdoc/Set.html Ruby standard library `Set`
21
21
 
22
-
23
22
  # @!macro internal_implementation_note
24
23
  SetImplementation = case
25
24
  when Concurrent.on_cruby?
26
- # Because MRI never runs code in parallel, the existing
27
- # non-thread-safe structures should usually work fine.
28
- ::Set
25
+ # The CRuby implementation of Set is written in Ruby itself and is
26
+ # not thread safe for certain methods.
27
+ require 'monitor'
28
+ require 'concurrent/thread_safe/util/data_structures'
29
+
30
+ class CRubySet < ::Set
31
+ end
32
+
33
+ ThreadSafe::Util.make_synchronized_on_cruby CRubySet
34
+ CRubySet
29
35
 
30
36
  when Concurrent.on_jruby?
31
37
  require 'jruby/synchronized'
@@ -33,6 +39,7 @@ module Concurrent
33
39
  class JRubySet < ::Set
34
40
  include JRuby::Synchronized
35
41
  end
42
+
36
43
  JRubySet
37
44
 
38
45
  when Concurrent.on_rbx?
@@ -41,7 +48,8 @@ module Concurrent
41
48
 
42
49
  class RbxSet < ::Set
43
50
  end
44
- ThreadSafe::Util.make_synchronized_on_rbx Concurrent::RbxSet
51
+
52
+ ThreadSafe::Util.make_synchronized_on_rbx RbxSet
45
53
  RbxSet
46
54
 
47
55
  when Concurrent.on_truffleruby?
@@ -50,7 +58,7 @@ module Concurrent
50
58
  class TruffleRubySet < ::Set
51
59
  end
52
60
 
53
- ThreadSafe::Util.make_synchronized_on_truffleruby Concurrent::TruffleRubySet
61
+ ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubySet
54
62
  TruffleRubySet
55
63
 
56
64
  else
@@ -9,7 +9,7 @@ module Concurrent
9
9
  # or any time thereafter. Attempting to assign a value to a member
10
10
  # that has already been set will result in a `Concurrent::ImmutabilityError`.
11
11
  #
12
- # @see http://ruby-doc.org/core-2.2.0/Struct.html Ruby standard library `Struct`
12
+ # @see http://ruby-doc.org/core/Struct.html Ruby standard library `Struct`
13
13
  # @see http://en.wikipedia.org/wiki/Final_(Java) Java `final` keyword
14
14
  module SettableStruct
15
15
  include Synchronization::AbstractStruct
@@ -91,6 +91,16 @@ module Concurrent
91
91
  raise NameError.new("no member '#{member}' in struct")
92
92
  end
93
93
 
94
+ private
95
+
96
+ # @!visibility private
97
+ def initialize_copy(original)
98
+ synchronize do
99
+ super(original)
100
+ ns_initialize_copy
101
+ end
102
+ end
103
+
94
104
  # @!macro struct_new
95
105
  def self.new(*args, &block)
96
106
  clazz_name = nil
@@ -115,6 +115,17 @@ module Concurrent
115
115
  self.class.new(*self.to_h.merge(other, &block).values)
116
116
  end
117
117
 
118
+ # @!visibility private
119
+ def ns_initialize_copy
120
+ @values = @values.map do |val|
121
+ begin
122
+ val.clone
123
+ rescue TypeError
124
+ val
125
+ end
126
+ end
127
+ end
128
+
118
129
  # @!visibility private
119
130
  def pr_underscore(clazz)
120
131
  word = clazz.to_s.dup # dup string to workaround JRuby 9.2.0.0 bug https://github.com/jruby/jruby/issues/5229
@@ -4,9 +4,7 @@ module Concurrent
4
4
  # @!visibility private
5
5
  # @!macro internal_implementation_note
6
6
  LockableObjectImplementation = case
7
- when Concurrent.on_cruby? && Concurrent.ruby_version(:<=, 1, 9, 3)
8
- MonitorLockableObject
9
- when Concurrent.on_cruby? && Concurrent.ruby_version(:>, 1, 9, 3)
7
+ when Concurrent.on_cruby?
10
8
  MutexLockableObject
11
9
  when Concurrent.on_jruby?
12
10
  JRubyLockableObject
@@ -26,8 +24,8 @@ module Concurrent
26
24
  # the classes using it. Use {Synchronization::Object} not this abstract class.
27
25
  #
28
26
  # @note this object does not support usage together with
29
- # [`Thread#wakeup`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-wakeup)
30
- # and [`Thread#raise`](http://ruby-doc.org/core-2.2.0/Thread.html#method-i-raise).
27
+ # [`Thread#wakeup`](http://ruby-doc.org/core/Thread.html#method-i-wakeup)
28
+ # and [`Thread#raise`](http://ruby-doc.org/core/Thread.html#method-i-raise).
31
29
  # `Thread#sleep` and `Thread#wakeup` will work as expected but mixing `Synchronization::Object#wait` and
32
30
  # `Thread#wakeup` will not work on all platforms.
33
31
  #