concurrent-ruby 1.1.10 → 1.3.7
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +76 -3
- data/Gemfile +6 -7
- data/README.md +28 -23
- data/Rakefile +90 -76
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +3 -38
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +10 -25
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +1 -1
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +1 -1
- data/lib/concurrent-ruby/concurrent/agent.rb +4 -3
- data/lib/concurrent-ruby/concurrent/array.rb +3 -13
- data/lib/concurrent-ruby/concurrent/async.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atom.rb +2 -2
- data/lib/concurrent-ruby/concurrent/atomic/atomic_boolean.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_fixnum.rb +5 -4
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +3 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +90 -153
- data/lib/concurrent-ruby/concurrent/atomic/cyclic_barrier.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/event.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/fiber_local_var.rb +109 -0
- data/lib/concurrent-ruby/concurrent/atomic/java_count_down_latch.rb +1 -0
- data/lib/concurrent-ruby/concurrent/atomic/locals.rb +189 -0
- data/lib/concurrent-ruby/concurrent/atomic/lock_local_var.rb +29 -0
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_boolean.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_atomic_fixnum.rb +11 -5
- data/lib/concurrent-ruby/concurrent/atomic/mutex_count_down_latch.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/mutex_semaphore.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomic/read_write_lock.rb +17 -3
- data/lib/concurrent-ruby/concurrent/atomic/reentrant_read_write_lock.rb +13 -4
- data/lib/concurrent-ruby/concurrent/atomic/semaphore.rb +6 -9
- data/lib/concurrent-ruby/concurrent/atomic/thread_local_var.rb +96 -89
- data/lib/concurrent-ruby/concurrent/atomic_reference/atomic_direct_update.rb +37 -0
- data/lib/concurrent-ruby/concurrent/atomic_reference/mutex_atomic.rb +16 -6
- data/lib/concurrent-ruby/concurrent/atomic_reference/numeric_cas_wrapper.rb +9 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_notify_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/copy_on_write_observer_set.rb +1 -1
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +2 -0
- data/lib/concurrent-ruby/concurrent/collection/map/mri_map_backend.rb +2 -2
- data/lib/concurrent-ruby/concurrent/collection/map/non_concurrent_map_backend.rb +16 -8
- data/lib/concurrent-ruby/concurrent/collection/map/synchronized_map_backend.rb +23 -20
- data/lib/concurrent-ruby/concurrent/collection/ruby_timeout_queue.rb +55 -0
- data/lib/concurrent-ruby/concurrent/collection/timeout_queue.rb +18 -0
- data/lib/concurrent-ruby/concurrent/concern/logging.rb +95 -6
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/concurrent-ruby/concurrent/configuration.rb +4 -87
- data/lib/concurrent-ruby/concurrent/delay.rb +3 -3
- data/lib/concurrent-ruby/concurrent/errors.rb +5 -0
- data/lib/concurrent-ruby/concurrent/exchanger.rb +1 -0
- data/lib/concurrent-ruby/concurrent/executor/abstract_executor_service.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/fixed_thread_pool.rb +6 -4
- data/lib/concurrent-ruby/concurrent/executor/java_executor_service.rb +7 -9
- data/lib/concurrent-ruby/concurrent/executor/java_thread_pool_executor.rb +7 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_single_thread_executor.rb +2 -0
- data/lib/concurrent-ruby/concurrent/executor/ruby_thread_pool_executor.rb +61 -31
- data/lib/concurrent-ruby/concurrent/executor/safe_task_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/serialized_execution.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/simple_executor_service.rb +4 -1
- data/lib/concurrent-ruby/concurrent/executor/single_thread_executor.rb +1 -1
- data/lib/concurrent-ruby/concurrent/executor/timer_set.rb +10 -3
- data/lib/concurrent-ruby/concurrent/executors.rb +0 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +5 -12
- data/lib/concurrent-ruby/concurrent/immutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/ivar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/map.rb +43 -39
- data/lib/concurrent-ruby/concurrent/maybe.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mutable_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/mvar.rb +5 -5
- data/lib/concurrent-ruby/concurrent/promise.rb +3 -3
- data/lib/concurrent-ruby/concurrent/promises.rb +40 -29
- data/lib/concurrent-ruby/concurrent/re_include.rb +2 -0
- data/lib/concurrent-ruby/concurrent/scheduled_task.rb +2 -2
- data/lib/concurrent-ruby/concurrent/set.rb +0 -10
- data/lib/concurrent-ruby/concurrent/settable_struct.rb +2 -2
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_lockable_object.rb +5 -1
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_object.rb +1 -3
- data/lib/concurrent-ruby/concurrent/synchronization/abstract_struct.rb +1 -1
- data/lib/concurrent-ruby/concurrent/synchronization/condition.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/full_memory_barrier.rb +29 -0
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_lockable_object.rb +3 -1
- data/lib/concurrent-ruby/concurrent/synchronization/lock.rb +2 -0
- data/lib/concurrent-ruby/concurrent/synchronization/lockable_object.rb +5 -2
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +6 -5
- data/lib/concurrent-ruby/concurrent/synchronization/object.rb +13 -45
- data/lib/concurrent-ruby/concurrent/synchronization/safe_initialization.rb +36 -0
- data/lib/concurrent-ruby/concurrent/synchronization/volatile.rb +77 -12
- data/lib/concurrent-ruby/concurrent/synchronization.rb +1 -18
- data/lib/concurrent-ruby/concurrent/thread_safe/synchronized_delegator.rb +36 -39
- data/lib/concurrent-ruby/concurrent/thread_safe/util/adder.rb +1 -1
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +1 -37
- data/lib/concurrent-ruby/concurrent/thread_safe/util/xor_shift_random.rb +1 -1
- data/lib/concurrent-ruby/concurrent/timer_task.rb +65 -10
- data/lib/concurrent-ruby/concurrent/tuple.rb +1 -5
- data/lib/concurrent-ruby/concurrent/tvar.rb +2 -1
- data/lib/concurrent-ruby/concurrent/utility/engine.rb +5 -16
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +3 -74
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +8 -10
- data/lib/concurrent-ruby/concurrent/utility/native_integer.rb +1 -0
- data/lib/concurrent-ruby/concurrent/utility/processor_counter.rb +148 -58
- data/lib/concurrent-ruby/concurrent/version.rb +1 -1
- metadata +14 -19
- data/lib/concurrent-ruby/concurrent/atomic/abstract_thread_local_var.rb +0 -66
- data/lib/concurrent-ruby/concurrent/atomic/java_thread_local_var.rb +0 -37
- data/lib/concurrent-ruby/concurrent/atomic/ruby_thread_local_var.rb +0 -181
- data/lib/concurrent-ruby/concurrent/collection/map/atomic_reference_map_backend.rb +0 -927
- data/lib/concurrent-ruby/concurrent/synchronization/jruby_object.rb +0 -45
- data/lib/concurrent-ruby/concurrent/synchronization/mri_object.rb +0 -44
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_lockable_object.rb +0 -71
- data/lib/concurrent-ruby/concurrent/synchronization/rbx_object.rb +0 -49
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +0 -47
- data/lib/concurrent-ruby/concurrent/thread_safe/util/cheap_lockable.rb +0 -118
|
@@ -39,6 +39,10 @@ module Concurrent
|
|
|
39
39
|
# The number of tasks that have been completed by the pool since construction.
|
|
40
40
|
# @return [Integer] The number of tasks that have been completed by the pool since construction.
|
|
41
41
|
|
|
42
|
+
# @!macro thread_pool_executor_method_active_count
|
|
43
|
+
# The number of threads that are actively executing tasks.
|
|
44
|
+
# @return [Integer] The number of threads that are actively executing tasks.
|
|
45
|
+
|
|
42
46
|
# @!macro thread_pool_executor_attr_reader_idletime
|
|
43
47
|
# The number of seconds that a thread may be idle before being reclaimed.
|
|
44
48
|
# @return [Integer] The number of seconds that a thread may be idle before being reclaimed.
|
|
@@ -77,10 +81,8 @@ module Concurrent
|
|
|
77
81
|
# What is being pruned is controlled by the min_threads and idletime
|
|
78
82
|
# parameters passed at pool creation time
|
|
79
83
|
#
|
|
80
|
-
# This is a no-op on
|
|
81
|
-
#
|
|
82
|
-
# this method explicitely in case your application post jobs in bursts (a
|
|
83
|
-
# lot of jobs and then nothing for long periods)
|
|
84
|
+
# This is a no-op on all pool implementations as they prune themselves
|
|
85
|
+
# automatically, and has been deprecated.
|
|
84
86
|
|
|
85
87
|
# @!macro thread_pool_executor_public_api
|
|
86
88
|
#
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
|
|
1
|
+
require 'concurrent/utility/engine'
|
|
2
2
|
|
|
3
|
+
if Concurrent.on_jruby?
|
|
3
4
|
require 'concurrent/errors'
|
|
4
|
-
require 'concurrent/utility/engine'
|
|
5
5
|
require 'concurrent/executor/abstract_executor_service'
|
|
6
6
|
|
|
7
7
|
module Concurrent
|
|
@@ -46,6 +46,7 @@ if Concurrent.on_jruby?
|
|
|
46
46
|
def kill
|
|
47
47
|
synchronize do
|
|
48
48
|
@executor.shutdownNow
|
|
49
|
+
wait_for_termination
|
|
49
50
|
nil
|
|
50
51
|
end
|
|
51
52
|
end
|
|
@@ -57,15 +58,11 @@ if Concurrent.on_jruby?
|
|
|
57
58
|
end
|
|
58
59
|
|
|
59
60
|
def ns_shuttingdown?
|
|
60
|
-
|
|
61
|
-
@executor.isTerminating
|
|
62
|
-
else
|
|
63
|
-
false
|
|
64
|
-
end
|
|
61
|
+
@executor.isShutdown && !@executor.isTerminated
|
|
65
62
|
end
|
|
66
63
|
|
|
67
64
|
def ns_shutdown?
|
|
68
|
-
@executor.
|
|
65
|
+
@executor.isTerminated
|
|
69
66
|
end
|
|
70
67
|
|
|
71
68
|
class Job
|
|
@@ -88,10 +85,11 @@ if Concurrent.on_jruby?
|
|
|
88
85
|
|
|
89
86
|
def initialize(daemonize = true)
|
|
90
87
|
@daemonize = daemonize
|
|
88
|
+
@java_thread_factory = java.util.concurrent.Executors.defaultThreadFactory
|
|
91
89
|
end
|
|
92
90
|
|
|
93
91
|
def newThread(runnable)
|
|
94
|
-
thread =
|
|
92
|
+
thread = @java_thread_factory.newThread(runnable)
|
|
95
93
|
thread.setDaemon(@daemonize)
|
|
96
94
|
return thread
|
|
97
95
|
end
|
|
@@ -8,6 +8,7 @@ if Concurrent.on_jruby?
|
|
|
8
8
|
# @!macro thread_pool_options
|
|
9
9
|
# @!visibility private
|
|
10
10
|
class JavaThreadPoolExecutor < JavaExecutorService
|
|
11
|
+
include Concern::Deprecation
|
|
11
12
|
|
|
12
13
|
# @!macro thread_pool_executor_constant_default_max_pool_size
|
|
13
14
|
DEFAULT_MAX_POOL_SIZE = java.lang.Integer::MAX_VALUE # 2147483647
|
|
@@ -73,6 +74,11 @@ if Concurrent.on_jruby?
|
|
|
73
74
|
@executor.getCompletedTaskCount
|
|
74
75
|
end
|
|
75
76
|
|
|
77
|
+
# @!macro thread_pool_executor_method_active_count
|
|
78
|
+
def active_count
|
|
79
|
+
@executor.getActiveCount
|
|
80
|
+
end
|
|
81
|
+
|
|
76
82
|
# @!macro thread_pool_executor_attr_reader_idletime
|
|
77
83
|
def idletime
|
|
78
84
|
@executor.getKeepAliveTime(java.util.concurrent.TimeUnit::SECONDS)
|
|
@@ -95,6 +101,7 @@ if Concurrent.on_jruby?
|
|
|
95
101
|
|
|
96
102
|
# @!macro thread_pool_executor_method_prune_pool
|
|
97
103
|
def prune_pool
|
|
104
|
+
deprecated "#prune_pool has no effect and will be removed in the next release."
|
|
98
105
|
end
|
|
99
106
|
|
|
100
107
|
private
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'concurrent/executor/ruby_thread_pool_executor'
|
|
2
|
+
require 'concurrent/executor/serial_executor_service'
|
|
2
3
|
|
|
3
4
|
module Concurrent
|
|
4
5
|
|
|
@@ -6,6 +7,7 @@ module Concurrent
|
|
|
6
7
|
# @!macro abstract_executor_service_public_api
|
|
7
8
|
# @!visibility private
|
|
8
9
|
class RubySingleThreadExecutor < RubyThreadPoolExecutor
|
|
10
|
+
include SerialExecutorService
|
|
9
11
|
|
|
10
12
|
# @!macro single_thread_executor_method_initialize
|
|
11
13
|
def initialize(opts = {})
|
|
@@ -3,6 +3,7 @@ require 'concurrent/atomic/event'
|
|
|
3
3
|
require 'concurrent/concern/logging'
|
|
4
4
|
require 'concurrent/executor/ruby_executor_service'
|
|
5
5
|
require 'concurrent/utility/monotonic_time'
|
|
6
|
+
require 'concurrent/collection/timeout_queue'
|
|
6
7
|
|
|
7
8
|
module Concurrent
|
|
8
9
|
|
|
@@ -10,6 +11,7 @@ module Concurrent
|
|
|
10
11
|
# @!macro thread_pool_options
|
|
11
12
|
# @!visibility private
|
|
12
13
|
class RubyThreadPoolExecutor < RubyExecutorService
|
|
14
|
+
include Concern::Deprecation
|
|
13
15
|
|
|
14
16
|
# @!macro thread_pool_executor_constant_default_max_pool_size
|
|
15
17
|
DEFAULT_MAX_POOL_SIZE = 2_147_483_647 # java.lang.Integer::MAX_VALUE
|
|
@@ -61,6 +63,13 @@ module Concurrent
|
|
|
61
63
|
synchronize { @completed_task_count }
|
|
62
64
|
end
|
|
63
65
|
|
|
66
|
+
# @!macro thread_pool_executor_method_active_count
|
|
67
|
+
def active_count
|
|
68
|
+
synchronize do
|
|
69
|
+
@pool.length - @ready.length
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
64
73
|
# @!macro executor_service_method_can_overflow_question
|
|
65
74
|
def can_overflow?
|
|
66
75
|
synchronize { ns_limited_queue? }
|
|
@@ -87,9 +96,28 @@ module Concurrent
|
|
|
87
96
|
end
|
|
88
97
|
end
|
|
89
98
|
|
|
99
|
+
# removes the worker if it can be pruned
|
|
100
|
+
#
|
|
101
|
+
# @return [true, false] if the worker was pruned
|
|
102
|
+
#
|
|
90
103
|
# @!visibility private
|
|
91
|
-
def
|
|
92
|
-
synchronize
|
|
104
|
+
def prune_worker(worker)
|
|
105
|
+
synchronize do
|
|
106
|
+
if ns_prunable_capacity > 0
|
|
107
|
+
remove_worker worker
|
|
108
|
+
true
|
|
109
|
+
else
|
|
110
|
+
false
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# @!visibility private
|
|
116
|
+
def remove_worker(worker)
|
|
117
|
+
synchronize do
|
|
118
|
+
ns_remove_ready_worker worker
|
|
119
|
+
ns_remove_busy_worker worker
|
|
120
|
+
end
|
|
93
121
|
end
|
|
94
122
|
|
|
95
123
|
# @!visibility private
|
|
@@ -109,7 +137,7 @@ module Concurrent
|
|
|
109
137
|
|
|
110
138
|
# @!macro thread_pool_executor_method_prune_pool
|
|
111
139
|
def prune_pool
|
|
112
|
-
|
|
140
|
+
deprecated "#prune_pool has no effect and will be removed in next the release, see https://github.com/ruby-concurrency/concurrent-ruby/pull/1082."
|
|
113
141
|
end
|
|
114
142
|
|
|
115
143
|
private
|
|
@@ -139,9 +167,6 @@ module Concurrent
|
|
|
139
167
|
@largest_length = 0
|
|
140
168
|
@workers_counter = 0
|
|
141
169
|
@ruby_pid = $$ # detects if Ruby has forked
|
|
142
|
-
|
|
143
|
-
@gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented
|
|
144
|
-
@next_gc_time = Concurrent.monotonic_time + @gc_interval
|
|
145
170
|
end
|
|
146
171
|
|
|
147
172
|
# @!visibility private
|
|
@@ -155,12 +180,10 @@ module Concurrent
|
|
|
155
180
|
|
|
156
181
|
if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task)
|
|
157
182
|
@scheduled_task_count += 1
|
|
183
|
+
nil
|
|
158
184
|
else
|
|
159
|
-
|
|
185
|
+
fallback_action(*args, &task)
|
|
160
186
|
end
|
|
161
|
-
|
|
162
|
-
ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
|
|
163
|
-
nil
|
|
164
187
|
end
|
|
165
188
|
|
|
166
189
|
# @!visibility private
|
|
@@ -211,7 +234,7 @@ module Concurrent
|
|
|
211
234
|
# @!visibility private
|
|
212
235
|
def ns_enqueue(*args, &task)
|
|
213
236
|
return false if @synchronous
|
|
214
|
-
|
|
237
|
+
|
|
215
238
|
if !ns_limited_queue? || @queue.size < @max_queue
|
|
216
239
|
@queue << [task, args]
|
|
217
240
|
true
|
|
@@ -258,7 +281,7 @@ module Concurrent
|
|
|
258
281
|
end
|
|
259
282
|
end
|
|
260
283
|
|
|
261
|
-
# removes a worker which is not
|
|
284
|
+
# removes a worker which is not tracked in @ready
|
|
262
285
|
#
|
|
263
286
|
# @!visibility private
|
|
264
287
|
def ns_remove_busy_worker(worker)
|
|
@@ -267,25 +290,27 @@ module Concurrent
|
|
|
267
290
|
true
|
|
268
291
|
end
|
|
269
292
|
|
|
270
|
-
# try oldest worker if it is idle for enough time, it's returned back at the start
|
|
271
|
-
#
|
|
272
293
|
# @!visibility private
|
|
273
|
-
def
|
|
274
|
-
|
|
275
|
-
|
|
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
|
|
294
|
+
def ns_remove_ready_worker(worker)
|
|
295
|
+
if index = @ready.index { |rw, _| rw == worker }
|
|
296
|
+
@ready.delete_at(index)
|
|
284
297
|
end
|
|
298
|
+
true
|
|
299
|
+
end
|
|
285
300
|
|
|
286
|
-
|
|
301
|
+
# @return [Integer] number of excess idle workers which can be removed without
|
|
302
|
+
# going below min_length, or all workers if not running
|
|
303
|
+
#
|
|
304
|
+
# @!visibility private
|
|
305
|
+
def ns_prunable_capacity
|
|
306
|
+
if running?
|
|
307
|
+
[@pool.size - @min_length, @ready.size].min
|
|
308
|
+
else
|
|
309
|
+
@pool.size
|
|
310
|
+
end
|
|
287
311
|
end
|
|
288
312
|
|
|
313
|
+
# @!visibility private
|
|
289
314
|
def ns_reset_if_forked
|
|
290
315
|
if $$ != @ruby_pid
|
|
291
316
|
@queue.clear
|
|
@@ -305,7 +330,7 @@ module Concurrent
|
|
|
305
330
|
|
|
306
331
|
def initialize(pool, id)
|
|
307
332
|
# instance variables accessed only under pool's lock so no need to sync here again
|
|
308
|
-
@queue =
|
|
333
|
+
@queue = Collection::TimeoutQueue.new
|
|
309
334
|
@pool = pool
|
|
310
335
|
@thread = create_worker @queue, pool, pool.idletime
|
|
311
336
|
|
|
@@ -331,17 +356,22 @@ module Concurrent
|
|
|
331
356
|
def create_worker(queue, pool, idletime)
|
|
332
357
|
Thread.new(queue, pool, idletime) do |my_queue, my_pool, my_idletime|
|
|
333
358
|
catch(:stop) do
|
|
334
|
-
|
|
359
|
+
prunable = true
|
|
335
360
|
|
|
336
|
-
|
|
361
|
+
loop do
|
|
362
|
+
timeout = prunable && my_pool.running? ? my_idletime : nil
|
|
363
|
+
case message = my_queue.pop(timeout: timeout)
|
|
364
|
+
when nil
|
|
365
|
+
throw :stop if my_pool.prune_worker(self)
|
|
366
|
+
prunable = false
|
|
337
367
|
when :stop
|
|
338
|
-
my_pool.
|
|
368
|
+
my_pool.remove_worker(self)
|
|
339
369
|
throw :stop
|
|
340
|
-
|
|
341
370
|
else
|
|
342
371
|
task, args = message
|
|
343
372
|
run_task my_pool, task, args
|
|
344
373
|
my_pool.ready_worker(self, Concurrent.monotonic_time)
|
|
374
|
+
prunable = true
|
|
345
375
|
end
|
|
346
376
|
end
|
|
347
377
|
end
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
require 'concurrent/
|
|
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
|
|
|
@@ -27,7 +27,7 @@ module Concurrent
|
|
|
27
27
|
# is received. This pattern has several issues. The thread itself is highly
|
|
28
28
|
# susceptible to errors during processing. Also, the thread itself must be
|
|
29
29
|
# constantly monitored and restarted should it die. `SingleThreadExecutor`
|
|
30
|
-
# encapsulates all these
|
|
30
|
+
# encapsulates all these behaviors. The task processor is highly resilient
|
|
31
31
|
# to errors from within tasks. Also, should the thread die it will
|
|
32
32
|
# automatically be restarted.
|
|
33
33
|
#
|
|
@@ -3,7 +3,7 @@ require 'concurrent/atomic/event'
|
|
|
3
3
|
require 'concurrent/collection/non_concurrent_priority_queue'
|
|
4
4
|
require 'concurrent/executor/executor_service'
|
|
5
5
|
require 'concurrent/executor/single_thread_executor'
|
|
6
|
-
|
|
6
|
+
require 'concurrent/errors'
|
|
7
7
|
require 'concurrent/options'
|
|
8
8
|
|
|
9
9
|
module Concurrent
|
|
@@ -61,6 +61,7 @@ module Concurrent
|
|
|
61
61
|
# not running.
|
|
62
62
|
def kill
|
|
63
63
|
shutdown
|
|
64
|
+
@timer_executor.kill
|
|
64
65
|
end
|
|
65
66
|
|
|
66
67
|
private :<<
|
|
@@ -122,7 +123,9 @@ module Concurrent
|
|
|
122
123
|
def ns_shutdown_execution
|
|
123
124
|
ns_reset_if_forked
|
|
124
125
|
@queue.clear
|
|
125
|
-
@
|
|
126
|
+
@condition.set
|
|
127
|
+
@condition.reset
|
|
128
|
+
@timer_executor.shutdown
|
|
126
129
|
stopped_event.set
|
|
127
130
|
end
|
|
128
131
|
|
|
@@ -162,7 +165,11 @@ module Concurrent
|
|
|
162
165
|
# queue now must have the same pop time, or a closer one, as
|
|
163
166
|
# when we peeked).
|
|
164
167
|
task = synchronize { @queue.pop }
|
|
165
|
-
|
|
168
|
+
begin
|
|
169
|
+
task.executor.post { task.process_task }
|
|
170
|
+
rescue RejectedExecutionError
|
|
171
|
+
# ignore and continue
|
|
172
|
+
end
|
|
166
173
|
else
|
|
167
174
|
@condition.wait([diff, 60].min)
|
|
168
175
|
end
|
|
@@ -10,7 +10,6 @@ require 'concurrent/executor/java_thread_pool_executor'
|
|
|
10
10
|
require 'concurrent/executor/ruby_executor_service'
|
|
11
11
|
require 'concurrent/executor/ruby_single_thread_executor'
|
|
12
12
|
require 'concurrent/executor/ruby_thread_pool_executor'
|
|
13
|
-
require 'concurrent/executor/cached_thread_pool'
|
|
14
13
|
require 'concurrent/executor/safe_task_executor'
|
|
15
14
|
require 'concurrent/executor/serial_executor_service'
|
|
16
15
|
require 'concurrent/executor/serialized_execution'
|
|
@@ -15,9 +15,11 @@ module Concurrent
|
|
|
15
15
|
# @!macro internal_implementation_note
|
|
16
16
|
HashImplementation = case
|
|
17
17
|
when Concurrent.on_cruby?
|
|
18
|
-
# Hash is thread-safe
|
|
19
|
-
#
|
|
20
|
-
#
|
|
18
|
+
# Hash is not fully thread-safe on CRuby, see
|
|
19
|
+
# https://bugs.ruby-lang.org/issues/19237
|
|
20
|
+
# https://github.com/ruby/ruby/commit/ffd52412ab
|
|
21
|
+
# https://github.com/ruby-concurrency/concurrent-ruby/issues/929
|
|
22
|
+
# So we will need to add synchronization here (similar to Concurrent::Map).
|
|
21
23
|
::Hash
|
|
22
24
|
|
|
23
25
|
when Concurrent.on_jruby?
|
|
@@ -28,15 +30,6 @@ module Concurrent
|
|
|
28
30
|
end
|
|
29
31
|
JRubyHash
|
|
30
32
|
|
|
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
33
|
when Concurrent.on_truffleruby?
|
|
41
34
|
require 'concurrent/thread_safe/util/data_structures'
|
|
42
35
|
|
|
@@ -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/
|
|
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,17 +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_truffleruby?
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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/synchronized_map_backend'
|
|
24
|
+
SynchronizedMapBackend
|
|
25
|
+
end
|
|
24
26
|
else
|
|
25
27
|
warn 'Concurrent::Map: unsupported Ruby engine, using a fully synchronized Concurrent::Map implementation'
|
|
26
28
|
require 'concurrent/collection/map/synchronized_map_backend'
|
|
@@ -44,6 +46,12 @@ module Concurrent
|
|
|
44
46
|
# @note Atomic methods taking a block do not allow the `self` instance
|
|
45
47
|
# to be used within the block. Doing so will cause a deadlock.
|
|
46
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
|
+
|
|
47
55
|
# @!method compute_if_absent(key)
|
|
48
56
|
# Compute and store new value for key if the key is absent.
|
|
49
57
|
# @param [Object] key
|
|
@@ -117,41 +125,38 @@ module Concurrent
|
|
|
117
125
|
# @return [true, false] true if deleted
|
|
118
126
|
# @!macro map.atomic_method
|
|
119
127
|
|
|
120
|
-
#
|
|
121
|
-
|
|
122
|
-
if options.kind_of?(::Hash)
|
|
123
|
-
validate_options_hash!(options)
|
|
124
|
-
else
|
|
125
|
-
options = nil
|
|
126
|
-
end
|
|
128
|
+
# NonConcurrentMapBackend handles default_proc natively
|
|
129
|
+
unless defined?(Collection::NonConcurrentMapBackend) and self < Collection::NonConcurrentMapBackend
|
|
127
130
|
|
|
128
|
-
|
|
129
|
-
@default_proc
|
|
130
|
-
|
|
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
|
|
131
139
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
# @return [Object] the value
|
|
135
|
-
def [](key)
|
|
136
|
-
if value = super # non-falsy value is an existing mapping, return it right away
|
|
137
|
-
value
|
|
138
|
-
# 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
|
|
139
|
-
# 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
|
|
140
|
-
# would be returned)
|
|
141
|
-
# note: nil == value check is not technically necessary
|
|
142
|
-
elsif @default_proc && nil == value && NULL == (value = get_or_default(key, NULL))
|
|
143
|
-
@default_proc.call(self, key)
|
|
144
|
-
else
|
|
145
|
-
value
|
|
140
|
+
super(options)
|
|
141
|
+
@default_proc = default_proc
|
|
146
142
|
end
|
|
147
|
-
end
|
|
148
143
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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 incorrect +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
|
|
159
|
+
end
|
|
155
160
|
end
|
|
156
161
|
|
|
157
162
|
alias_method :get, :[]
|
|
@@ -197,7 +202,6 @@ module Concurrent
|
|
|
197
202
|
# @yieldparam key [Object]
|
|
198
203
|
# @yieldreturn [Object] default value
|
|
199
204
|
# @return [Object] the value or default value
|
|
200
|
-
# @!macro map.atomic_method_with_block
|
|
201
205
|
def fetch_or_store(key, default_value = NULL)
|
|
202
206
|
fetch(key) do
|
|
203
207
|
put(key, block_given? ? yield(key) : (NULL == default_value ? raise_fetch_no_key : default_value))
|
|
@@ -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
|
|
|
@@ -9,7 +9,7 @@ module Concurrent
|
|
|
9
9
|
# queue of length one, or a special kind of mutable variable.
|
|
10
10
|
#
|
|
11
11
|
# On top of the fundamental `#put` and `#take` operations, we also provide a
|
|
12
|
-
# `#
|
|
12
|
+
# `#modify` that is atomic with respect to operations on the same instance.
|
|
13
13
|
# These operations all support timeouts.
|
|
14
14
|
#
|
|
15
15
|
# We also support non-blocking operations `#try_put!` and `#try_take!`, a
|
|
@@ -87,7 +87,7 @@ module Concurrent
|
|
|
87
87
|
@mutex.synchronize do
|
|
88
88
|
wait_for_full(timeout)
|
|
89
89
|
|
|
90
|
-
#
|
|
90
|
+
# If we timed out we'll still be empty
|
|
91
91
|
if unlocked_full?
|
|
92
92
|
yield @value
|
|
93
93
|
else
|
|
@@ -116,10 +116,10 @@ module Concurrent
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
# Atomically `take`, yield the value to a block for transformation, and then
|
|
119
|
-
# `put` the transformed value. Returns the
|
|
119
|
+
# `put` the transformed value. Returns the pre-transform value. A timeout can
|
|
120
120
|
# be set to limit the time spent blocked, in which case it returns `TIMEOUT`
|
|
121
121
|
# if the time is exceeded.
|
|
122
|
-
# @return [Object] the
|
|
122
|
+
# @return [Object] the pre-transform value, or `TIMEOUT`
|
|
123
123
|
def modify(timeout = nil)
|
|
124
124
|
raise ArgumentError.new('no block given') unless block_given?
|
|
125
125
|
|
|
@@ -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
|
|
@@ -103,7 +103,7 @@ module Concurrent
|
|
|
103
103
|
# - if parent is *rejected* the child will be *pending* (but will ultimately be *rejected*)
|
|
104
104
|
#
|
|
105
105
|
# Promises are executed asynchronously from the main thread. By the time a
|
|
106
|
-
# child Promise finishes
|
|
106
|
+
# child Promise finishes initialization it may be in a different state than its
|
|
107
107
|
# parent (by the time a child is created its parent may have completed
|
|
108
108
|
# execution and changed state). Despite being asynchronous, however, the order
|
|
109
109
|
# of execution of Promise objects in a chain (or tree) is strictly defined.
|
|
@@ -167,7 +167,7 @@ module Concurrent
|
|
|
167
167
|
# c2 = p.then(-> reason { raise 'Boom!' })
|
|
168
168
|
#
|
|
169
169
|
# c1.wait.state #=> :fulfilled
|
|
170
|
-
# c1.value #=>
|
|
170
|
+
# c1.value #=> 42
|
|
171
171
|
# c2.wait.state #=> :rejected
|
|
172
172
|
# c2.reason #=> #<RuntimeError: Boom!>
|
|
173
173
|
# ```
|