concurrent-ruby 1.0.5 → 1.1.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +155 -0
- data/Gemfile +37 -0
- data/LICENSE.txt +18 -18
- data/README.md +260 -103
- data/Rakefile +329 -0
- data/ext/concurrent-ruby/ConcurrentRubyService.java +17 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/AtomicReferenceLibrary.java +175 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java +248 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicBooleanLibrary.java +93 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java +113 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/JavaSemaphoreLibrary.java +189 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/SynchronizationLibrary.java +307 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMap.java +31 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/ConcurrentHashMapV8.java +3863 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/LongAdder.java +203 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/Striped64.java +342 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/ConcurrentHashMapV8.java +3800 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/LongAdder.java +204 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166e/nounsafe/Striped64.java +291 -0
- data/ext/concurrent-ruby/com/concurrent_ruby/ext/jsr166y/ThreadLocalRandom.java +199 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/agent.rb +7 -7
- data/lib/concurrent-ruby/concurrent/array.rb +66 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/async.rb +28 -24
- data/lib/{concurrent → concurrent-ruby/concurrent}/atom.rb +10 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_boolean.rb +26 -22
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/atomic_fixnum.rb +27 -23
- data/lib/concurrent-ruby/concurrent/atomic/atomic_markable_reference.rb +164 -0
- data/lib/concurrent-ruby/concurrent/atomic/atomic_reference.rb +205 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/count_down_latch.rb +7 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/cyclic_barrier.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/event.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_count_down_latch.rb +9 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_boolean.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_count_down_latch.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_semaphore.rb +18 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/read_write_lock.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/reentrant_read_write_lock.rb +7 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/ruby_thread_local_var.rb +60 -40
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/semaphore.rb +34 -13
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/thread_local_var.rb +8 -8
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/mutex_atomic.rb +3 -8
- data/lib/{concurrent → concurrent-ruby/concurrent}/atomic_reference/numeric_cas_wrapper.rb +1 -1
- data/lib/concurrent-ruby/concurrent/atomics.rb +10 -0
- data/lib/concurrent-ruby/concurrent/collection/lock_free_stack.rb +158 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/atomic_reference_map_backend.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/mri_map_backend.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/non_concurrent_map_backend.rb +1 -2
- data/lib/concurrent-ruby/concurrent/collection/map/truffleruby_map_backend.rb +14 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/non_concurrent_priority_queue.rb +30 -30
- data/lib/{concurrent → concurrent-ruby/concurrent}/collection/ruby_non_concurrent_priority_queue.rb +11 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/dereferenceable.rb +3 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/logging.rb +6 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/concern/observable.rb +7 -7
- data/lib/concurrent-ruby/concurrent/concurrent_ruby.jar +0 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/configuration.rb +15 -15
- data/lib/{concurrent → concurrent-ruby/concurrent}/constants.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/dataflow.rb +2 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/delay.rb +9 -7
- data/lib/{concurrent → concurrent-ruby/concurrent}/exchanger.rb +21 -25
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/abstract_executor_service.rb +35 -38
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/cached_thread_pool.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/executor_service.rb +17 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/fixed_thread_pool.rb +47 -33
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_executor_service.rb +20 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_single_thread_executor.rb +4 -3
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/java_thread_pool_executor.rb +29 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_executor_service.rb +10 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_single_thread_executor.rb +0 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/ruby_thread_pool_executor.rb +46 -42
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/safe_task_executor.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/simple_executor_service.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/single_thread_executor.rb +3 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/thread_pool_executor.rb +7 -6
- data/lib/{concurrent → concurrent-ruby/concurrent}/executor/timer_set.rb +14 -17
- data/lib/{concurrent → concurrent-ruby/concurrent}/future.rb +4 -1
- data/lib/concurrent-ruby/concurrent/hash.rb +59 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/immutable_struct.rb +9 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/ivar.rb +5 -6
- data/lib/concurrent-ruby/concurrent/map.rb +346 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/maybe.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/mutable_struct.rb +27 -16
- data/lib/{concurrent → concurrent-ruby/concurrent}/mvar.rb +2 -2
- data/lib/{concurrent → concurrent-ruby/concurrent}/promise.rb +54 -21
- data/lib/concurrent-ruby/concurrent/promises.rb +2167 -0
- data/lib/concurrent-ruby/concurrent/re_include.rb +58 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/scheduled_task.rb +29 -16
- data/lib/concurrent-ruby/concurrent/set.rb +74 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/settable_struct.rb +12 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_lockable_object.rb +5 -5
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_struct.rb +18 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/condition.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_object.rb +1 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lock.rb +2 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/lockable_object.rb +8 -10
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/mri_object.rb +1 -0
- data/lib/concurrent-ruby/concurrent/synchronization/mutex_lockable_object.rb +88 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/object.rb +53 -23
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_lockable_object.rb +6 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/rbx_object.rb +1 -0
- data/lib/concurrent-ruby/concurrent/synchronization/truffleruby_object.rb +47 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/volatile.rb +11 -9
- data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization.rb +4 -5
- data/lib/concurrent-ruby/concurrent/thread_safe/util/data_structures.rb +88 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/striped64.rb +9 -4
- data/lib/{concurrent → concurrent-ruby/concurrent}/timer_task.rb +15 -35
- data/lib/{concurrent → concurrent-ruby/concurrent}/tuple.rb +1 -1
- data/lib/{concurrent → concurrent-ruby/concurrent}/tvar.rb +21 -58
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/engine.rb +4 -4
- data/lib/concurrent-ruby/concurrent/utility/monotonic_time.rb +90 -0
- data/lib/concurrent-ruby/concurrent/utility/native_extension_loader.rb +79 -0
- data/lib/{concurrent → concurrent-ruby/concurrent}/utility/processor_counter.rb +5 -35
- data/lib/concurrent-ruby/concurrent/version.rb +3 -0
- data/lib/concurrent-ruby/concurrent-ruby.rb +5 -0
- data/lib/{concurrent.rb → concurrent-ruby/concurrent.rb} +24 -20
- metadata +149 -134
- data/lib/concurrent/array.rb +0 -39
- data/lib/concurrent/atomic/atomic_reference.rb +0 -51
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +0 -8
- data/lib/concurrent/atomic_reference/direct_update.rb +0 -81
- data/lib/concurrent/atomic_reference/jruby+truffle.rb +0 -2
- data/lib/concurrent/atomic_reference/jruby.rb +0 -16
- data/lib/concurrent/atomic_reference/rbx.rb +0 -22
- data/lib/concurrent/atomic_reference/ruby.rb +0 -32
- data/lib/concurrent/atomics.rb +0 -53
- data/lib/concurrent/edge.rb +0 -26
- data/lib/concurrent/hash.rb +0 -36
- data/lib/concurrent/lazy_register.rb +0 -81
- data/lib/concurrent/map.rb +0 -240
- data/lib/concurrent/synchronization/mri_lockable_object.rb +0 -71
- data/lib/concurrent/synchronization/truffle_lockable_object.rb +0 -9
- data/lib/concurrent/synchronization/truffle_object.rb +0 -31
- data/lib/concurrent/thread_safe/util/array_hash_rbx.rb +0 -30
- data/lib/concurrent/utility/at_exit.rb +0 -97
- data/lib/concurrent/utility/monotonic_time.rb +0 -58
- data/lib/concurrent/utility/native_extension_loader.rb +0 -73
- data/lib/concurrent/version.rb +0 -4
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/abstract_thread_local_var.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/java_thread_local_var.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/atomic/mutex_atomic_fixnum.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_notify_observer_set.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/copy_on_write_observer_set.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/java_non_concurrent_priority_queue.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/collection/map/synchronized_map_backend.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/deprecation.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/concern/obligation.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/errors.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/immediate_executor.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/indirect_immediate_executor.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serial_executor_service.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executor/serialized_execution_delegator.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/executors.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/options.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/abstract_object.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/synchronization/jruby_lockable_object.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/synchronized_delegator.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/adder.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/cheap_lockable.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/power_of_two_tuple.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/volatile.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util/xor_shift_random.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/thread_safe/util.rb +0 -0
- /data/lib/{concurrent → concurrent-ruby/concurrent}/utility/native_integer.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
|
-
|
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
|
-
@
|
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
|
-
|
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
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
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,3 +1,4 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
1
2
|
require 'concurrent/executor/ruby_single_thread_executor'
|
2
3
|
|
3
4
|
module Concurrent
|
@@ -14,7 +15,7 @@ module Concurrent
|
|
14
15
|
end
|
15
16
|
private_constant :SingleThreadExecutorImplementation
|
16
17
|
|
17
|
-
# @!macro
|
18
|
+
# @!macro single_thread_executor
|
18
19
|
#
|
19
20
|
# A thread pool with a single thread an unlimited queue. Should the thread
|
20
21
|
# die for any reason it will be removed and replaced, thus ensuring that
|
@@ -35,7 +36,7 @@ module Concurrent
|
|
35
36
|
# @!macro abstract_executor_service_public_api
|
36
37
|
class SingleThreadExecutor < SingleThreadExecutorImplementation
|
37
38
|
|
38
|
-
# @!macro
|
39
|
+
# @!macro single_thread_executor_method_initialize
|
39
40
|
#
|
40
41
|
# Create a new thread pool.
|
41
42
|
#
|
@@ -15,7 +15,7 @@ module Concurrent
|
|
15
15
|
end
|
16
16
|
private_constant :ThreadPoolExecutorImplementation
|
17
17
|
|
18
|
-
# @!macro
|
18
|
+
# @!macro thread_pool_executor
|
19
19
|
#
|
20
20
|
# An abstraction composed of one or more threads and a task queue. Tasks
|
21
21
|
# (blocks or `proc` objects) are submitted to the pool and added to the queue.
|
@@ -55,12 +55,12 @@ module Concurrent
|
|
55
55
|
# @!macro thread_pool_executor_public_api
|
56
56
|
class ThreadPoolExecutor < ThreadPoolExecutorImplementation
|
57
57
|
|
58
|
-
# @!macro
|
58
|
+
# @!macro thread_pool_executor_method_initialize
|
59
59
|
#
|
60
60
|
# Create a new thread pool.
|
61
|
-
#
|
61
|
+
#
|
62
62
|
# @param [Hash] opts the options which configure the thread pool.
|
63
|
-
#
|
63
|
+
#
|
64
64
|
# @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
|
65
65
|
# number of threads to be created
|
66
66
|
# @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) When a new task is submitted
|
@@ -73,12 +73,13 @@ 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
|
80
81
|
# in `FALLBACK_POLICIES`
|
81
|
-
#
|
82
|
+
#
|
82
83
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
|
83
84
|
|
84
85
|
# @!method initialize(opts = {})
|
@@ -20,7 +20,7 @@ module Concurrent
|
|
20
20
|
|
21
21
|
# Create a new set of timed tasks.
|
22
22
|
#
|
23
|
-
# @!macro
|
23
|
+
# @!macro executor_options
|
24
24
|
#
|
25
25
|
# @param [Hash] opts the options used to specify the executor on which to perform actions
|
26
26
|
# @option opts [Executor] :executor when set use the given `Executor` instance.
|
@@ -48,11 +48,9 @@ module Concurrent
|
|
48
48
|
def post(delay, *args, &task)
|
49
49
|
raise ArgumentError.new('no block given') unless block_given?
|
50
50
|
return false unless running?
|
51
|
-
opts = {
|
52
|
-
|
53
|
-
|
54
|
-
timer_set: self
|
55
|
-
}
|
51
|
+
opts = { executor: @task_executor,
|
52
|
+
args: args,
|
53
|
+
timer_set: self }
|
56
54
|
task = ScheduledTask.execute(delay, opts, &task) # may raise exception
|
57
55
|
task.unscheduled? ? false : task
|
58
56
|
end
|
@@ -74,12 +72,11 @@ module Concurrent
|
|
74
72
|
# @param [Hash] opts the options to create the object with.
|
75
73
|
# @!visibility private
|
76
74
|
def ns_initialize(opts)
|
77
|
-
@queue
|
78
|
-
@task_executor
|
79
|
-
@timer_executor
|
80
|
-
@condition
|
81
|
-
@ruby_pid
|
82
|
-
self.auto_terminate = opts.fetch(:auto_terminate, true)
|
75
|
+
@queue = Collection::NonConcurrentPriorityQueue.new(order: :min)
|
76
|
+
@task_executor = Options.executor_from_options(opts) || Concurrent.global_io_executor
|
77
|
+
@timer_executor = SingleThreadExecutor.new
|
78
|
+
@condition = Event.new
|
79
|
+
@ruby_pid = $$ # detects if Ruby has forked
|
83
80
|
end
|
84
81
|
|
85
82
|
# Post the task to the internal queue.
|
@@ -90,7 +87,7 @@ module Concurrent
|
|
90
87
|
#
|
91
88
|
# @!visibility private
|
92
89
|
def post_task(task)
|
93
|
-
synchronize{ ns_post_task(task) }
|
90
|
+
synchronize { ns_post_task(task) }
|
94
91
|
end
|
95
92
|
|
96
93
|
# @!visibility private
|
@@ -98,7 +95,7 @@ module Concurrent
|
|
98
95
|
return false unless ns_running?
|
99
96
|
ns_reset_if_forked
|
100
97
|
if (task.initial_delay) <= 0.01
|
101
|
-
task.executor.post{ task.process_task }
|
98
|
+
task.executor.post { task.process_task }
|
102
99
|
else
|
103
100
|
@queue.push(task)
|
104
101
|
# only post the process method when the queue is empty
|
@@ -116,7 +113,7 @@ module Concurrent
|
|
116
113
|
#
|
117
114
|
# @!visibility private
|
118
115
|
def remove_task(task)
|
119
|
-
synchronize{ @queue.delete(task) }
|
116
|
+
synchronize { @queue.delete(task) }
|
120
117
|
end
|
121
118
|
|
122
119
|
# `ExecutorService` callback called during shutdown.
|
@@ -148,7 +145,7 @@ module Concurrent
|
|
148
145
|
task = synchronize { @condition.reset; @queue.peek }
|
149
146
|
break unless task
|
150
147
|
|
151
|
-
now
|
148
|
+
now = Concurrent.monotonic_time
|
152
149
|
diff = task.schedule_time - now
|
153
150
|
|
154
151
|
if diff <= 0
|
@@ -165,7 +162,7 @@ module Concurrent
|
|
165
162
|
# queue now must have the same pop time, or a closer one, as
|
166
163
|
# when we peeked).
|
167
164
|
task = synchronize { @queue.pop }
|
168
|
-
task.executor.post{ task.process_task }
|
165
|
+
task.executor.post { task.process_task }
|
169
166
|
else
|
170
167
|
@condition.wait([diff, 60].min)
|
171
168
|
end
|
@@ -6,9 +6,12 @@ require 'concurrent/executor/safe_task_executor'
|
|
6
6
|
|
7
7
|
require 'concurrent/options'
|
8
8
|
|
9
|
+
# TODO (pitr-ch 14-Mar-2017): deprecate, Future, Promise, etc.
|
10
|
+
|
11
|
+
|
9
12
|
module Concurrent
|
10
13
|
|
11
|
-
# {include:file:
|
14
|
+
# {include:file:docs-source/future.md}
|
12
15
|
#
|
13
16
|
# @!macro copy_options
|
14
17
|
#
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
2
|
+
require 'concurrent/thread_safe/util'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# @!macro concurrent_hash
|
7
|
+
#
|
8
|
+
# A thread-safe subclass of Hash. This version locks against the object
|
9
|
+
# itself for every method call, ensuring only one thread can be reading
|
10
|
+
# or writing at a time. This includes iteration methods like `#each`,
|
11
|
+
# which takes the lock repeatedly when reading an item.
|
12
|
+
#
|
13
|
+
# @see http://ruby-doc.org/core/Hash.html Ruby standard library `Hash`
|
14
|
+
|
15
|
+
# @!macro internal_implementation_note
|
16
|
+
HashImplementation = case
|
17
|
+
when Concurrent.on_cruby?
|
18
|
+
# Hash is thread-safe in practice because CRuby runs
|
19
|
+
# threads one at a time and does not do context
|
20
|
+
# switching during the execution of C functions.
|
21
|
+
::Hash
|
22
|
+
|
23
|
+
when Concurrent.on_jruby?
|
24
|
+
require 'jruby/synchronized'
|
25
|
+
|
26
|
+
class JRubyHash < ::Hash
|
27
|
+
include JRuby::Synchronized
|
28
|
+
end
|
29
|
+
JRubyHash
|
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
|
+
when Concurrent.on_truffleruby?
|
41
|
+
require 'concurrent/thread_safe/util/data_structures'
|
42
|
+
|
43
|
+
class TruffleRubyHash < ::Hash
|
44
|
+
end
|
45
|
+
|
46
|
+
ThreadSafe::Util.make_synchronized_on_truffleruby TruffleRubyHash
|
47
|
+
TruffleRubyHash
|
48
|
+
|
49
|
+
else
|
50
|
+
warn 'Possibly unsupported Ruby implementation'
|
51
|
+
::Hash
|
52
|
+
end
|
53
|
+
private_constant :HashImplementation
|
54
|
+
|
55
|
+
# @!macro concurrent_hash
|
56
|
+
class Hash < HashImplementation
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -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
|
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
|
@@ -98,10 +98,10 @@ module Concurrent
|
|
98
98
|
observer
|
99
99
|
end
|
100
100
|
|
101
|
-
# @!macro
|
101
|
+
# @!macro ivar_set_method
|
102
102
|
# Set the `IVar` to a value and wake or notify all threads waiting on it.
|
103
103
|
#
|
104
|
-
# @!macro
|
104
|
+
# @!macro ivar_set_parameters_and_exceptions
|
105
105
|
# @param [Object] value the value to store in the `IVar`
|
106
106
|
# @yield A block operation to use for setting the value
|
107
107
|
# @raise [ArgumentError] if both a value and a block are given
|
@@ -124,7 +124,7 @@ module Concurrent
|
|
124
124
|
self
|
125
125
|
end
|
126
126
|
|
127
|
-
# @!macro
|
127
|
+
# @!macro ivar_fail_method
|
128
128
|
# Set the `IVar` to failed due to some error and wake or notify all threads waiting on it.
|
129
129
|
#
|
130
130
|
# @param [Object] reason for the failure
|
@@ -157,9 +157,8 @@ module Concurrent
|
|
157
157
|
self.observers = Collection::CopyOnWriteObserverSet.new
|
158
158
|
set_deref_options(opts)
|
159
159
|
|
160
|
-
|
161
|
-
|
162
|
-
else
|
160
|
+
@state = :pending
|
161
|
+
if value != NULL
|
163
162
|
ns_complete_without_notification(true, value, nil)
|
164
163
|
end
|
165
164
|
end
|