concurrent-ruby 0.8.0.pre2-java → 0.9.0-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +114 -3
- data/README.md +111 -55
- data/lib/concurrent.rb +90 -14
- data/lib/concurrent/async.rb +143 -51
- data/lib/concurrent/atom.rb +131 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +27 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +234 -109
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +19 -7
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +71 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +229 -136
- data/lib/concurrent/scheduled_task.rb +341 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +34 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +26 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +92 -103
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +44 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +46 -66
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -48
- data/lib/concurrent/utility/processor_count.rb +0 -152
- data/lib/extension_helper.rb +0 -37
@@ -1,31 +1,29 @@
|
|
1
|
-
|
1
|
+
require 'concurrent/executor/executor_service'
|
2
2
|
|
3
3
|
module Concurrent
|
4
4
|
|
5
5
|
# @!macro single_thread_executor
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
# @!macro thread_pool_options
|
7
|
+
# @!macro abstract_executor_service_public_api
|
8
|
+
# @!visibility private
|
9
|
+
class RubySingleThreadExecutor < RubyExecutorService
|
10
|
+
include SerialExecutorService
|
9
11
|
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# @option opts [Symbol] :fallback_policy (:discard) the policy for
|
13
|
-
# handling new tasks that are received when the queue size has
|
14
|
-
# reached `max_queue` or after the executor has shut down
|
15
|
-
#
|
16
|
-
# @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
|
17
|
-
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
|
18
|
-
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
|
12
|
+
# @!macro single_thread_executor_method_initialize
|
19
13
|
def initialize(opts = {})
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def ns_initialize(opts)
|
20
20
|
@queue = Queue.new
|
21
21
|
@thread = nil
|
22
22
|
@fallback_policy = opts.fetch(:fallback_policy, :discard)
|
23
23
|
raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
|
24
|
-
|
24
|
+
self.auto_terminate = opts.fetch(:auto_terminate, true)
|
25
25
|
end
|
26
26
|
|
27
|
-
protected
|
28
|
-
|
29
27
|
# @!visibility private
|
30
28
|
def execute(*args, &task)
|
31
29
|
supervise
|
@@ -1,261 +1,345 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
|
-
require_relative 'executor'
|
4
3
|
require 'concurrent/atomic/event'
|
5
|
-
require 'concurrent/
|
4
|
+
require 'concurrent/concern/logging'
|
5
|
+
require 'concurrent/executor/executor_service'
|
6
|
+
require 'concurrent/utility/monotonic_time'
|
6
7
|
|
7
8
|
module Concurrent
|
8
9
|
|
9
10
|
# @!macro thread_pool_executor
|
10
|
-
|
11
|
-
|
11
|
+
# @!macro thread_pool_options
|
12
|
+
# @!visibility private
|
13
|
+
class RubyThreadPoolExecutor < RubyExecutorService
|
12
14
|
|
13
|
-
#
|
14
|
-
DEFAULT_MAX_POOL_SIZE =
|
15
|
+
# @!macro thread_pool_executor_constant_default_max_pool_size
|
16
|
+
DEFAULT_MAX_POOL_SIZE = 2_147_483_647 # java.lang.Integer::MAX_VALUE
|
15
17
|
|
16
|
-
#
|
18
|
+
# @!macro thread_pool_executor_constant_default_min_pool_size
|
17
19
|
DEFAULT_MIN_POOL_SIZE = 0
|
18
20
|
|
19
|
-
#
|
21
|
+
# @!macro thread_pool_executor_constant_default_max_queue_size
|
20
22
|
DEFAULT_MAX_QUEUE_SIZE = 0
|
21
23
|
|
22
|
-
#
|
23
|
-
# before being reclaimed.
|
24
|
+
# @!macro thread_pool_executor_constant_default_thread_timeout
|
24
25
|
DEFAULT_THREAD_IDLETIMEOUT = 60
|
25
26
|
|
26
|
-
#
|
27
|
+
# @!macro thread_pool_executor_attr_reader_max_length
|
27
28
|
attr_reader :max_length
|
28
29
|
|
29
|
-
#
|
30
|
+
# @!macro thread_pool_executor_attr_reader_min_length
|
30
31
|
attr_reader :min_length
|
31
32
|
|
32
|
-
#
|
33
|
+
# @!macro thread_pool_executor_attr_reader_largest_length
|
33
34
|
attr_reader :largest_length
|
34
35
|
|
35
|
-
#
|
36
|
+
# @!macro thread_pool_executor_attr_reader_scheduled_task_count
|
36
37
|
attr_reader :scheduled_task_count
|
37
38
|
|
38
|
-
#
|
39
|
+
# @!macro thread_pool_executor_attr_reader_completed_task_count
|
39
40
|
attr_reader :completed_task_count
|
40
41
|
|
41
|
-
#
|
42
|
+
# @!macro thread_pool_executor_attr_reader_idletime
|
42
43
|
attr_reader :idletime
|
43
44
|
|
44
|
-
#
|
45
|
-
# When the queue size reaches `max_queue` subsequent tasks will be rejected in
|
46
|
-
# accordance with the configured `fallback_policy`.
|
45
|
+
# @!macro thread_pool_executor_attr_reader_max_queue
|
47
46
|
attr_reader :max_queue
|
48
47
|
|
49
|
-
#
|
50
|
-
#
|
51
|
-
# @param [Hash] opts the options which configure the thread pool
|
52
|
-
#
|
53
|
-
# @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
|
54
|
-
# number of threads to be created
|
55
|
-
# @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) the minimum
|
56
|
-
# number of threads to be retained
|
57
|
-
# @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum
|
58
|
-
# number of seconds a thread may be idle before being reclaimed
|
59
|
-
# @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum
|
60
|
-
# number of tasks allowed in the work queue at any one time; a value of
|
61
|
-
# zero means the queue may grow without bound
|
62
|
-
# @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
|
63
|
-
# tasks that are received when the queue size has reached
|
64
|
-
# `max_queue` or the executor has shut down
|
65
|
-
#
|
66
|
-
# @raise [ArgumentError] if `:max_threads` is less than one
|
67
|
-
# @raise [ArgumentError] if `:min_threads` is less than zero
|
68
|
-
# @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
|
69
|
-
# in `FALLBACK_POLICIES`
|
70
|
-
#
|
71
|
-
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
|
48
|
+
# @!macro thread_pool_executor_method_initialize
|
72
49
|
def initialize(opts = {})
|
73
|
-
|
74
|
-
@max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
|
75
|
-
@idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
|
76
|
-
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
|
77
|
-
@fallback_policy = opts.fetch(:fallback_policy, opts.fetch(:overflow_policy, :abort))
|
78
|
-
warn '[DEPRECATED] :overflow_policy is deprecated terminology, please use :fallback_policy instead' if opts.has_key?(:overflow_policy)
|
79
|
-
|
80
|
-
raise ArgumentError.new('max_threads must be greater than zero') if @max_length <= 0
|
81
|
-
raise ArgumentError.new('min_threads cannot be less than zero') if @min_length < 0
|
82
|
-
raise ArgumentError.new("#{fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
|
83
|
-
raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
|
84
|
-
|
85
|
-
init_executor
|
86
|
-
|
87
|
-
@pool = []
|
88
|
-
@queue = Queue.new
|
89
|
-
@scheduled_task_count = 0
|
90
|
-
@completed_task_count = 0
|
91
|
-
@largest_length = 0
|
92
|
-
|
93
|
-
@gc_interval = opts.fetch(:gc_interval, 1).to_i # undocumented
|
94
|
-
@last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
|
50
|
+
super(opts)
|
95
51
|
end
|
96
52
|
|
97
|
-
# @!macro
|
53
|
+
# @!macro executor_service_method_can_overflow_question
|
98
54
|
def can_overflow?
|
99
|
-
|
55
|
+
synchronize { ns_limited_queue? }
|
100
56
|
end
|
101
57
|
|
102
|
-
#
|
103
|
-
#
|
104
|
-
# @return [Integer] the length
|
58
|
+
# @!macro thread_pool_executor_attr_reader_length
|
105
59
|
def length
|
106
|
-
|
60
|
+
synchronize { @pool.length }
|
107
61
|
end
|
108
62
|
|
109
|
-
|
110
|
-
|
111
|
-
# The number of tasks in the queue awaiting execution.
|
112
|
-
#
|
113
|
-
# @return [Integer] the queue_length
|
63
|
+
# @!macro thread_pool_executor_attr_reader_queue_length
|
114
64
|
def queue_length
|
115
|
-
|
65
|
+
synchronize { @queue.length }
|
116
66
|
end
|
117
67
|
|
118
|
-
#
|
119
|
-
# new tasks. A value of -1 indicates that the queue may grow without bound.
|
120
|
-
#
|
121
|
-
# @return [Integer] the remaining_capacity
|
68
|
+
# @!macro thread_pool_executor_attr_reader_remaining_capacity
|
122
69
|
def remaining_capacity
|
123
|
-
|
70
|
+
synchronize do
|
71
|
+
if ns_limited_queue?
|
72
|
+
@max_queue - @queue.length
|
73
|
+
else
|
74
|
+
-1
|
75
|
+
end
|
76
|
+
end
|
124
77
|
end
|
125
78
|
|
126
|
-
#
|
127
|
-
|
128
|
-
|
129
|
-
def status
|
130
|
-
warn '[DEPRECATED] `status` is deprecated and will be removed soon.'
|
131
|
-
mutex.synchronize { @pool.collect { |worker| worker.status } }
|
79
|
+
# @!visibility private
|
80
|
+
def remove_busy_worker(worker)
|
81
|
+
synchronize { ns_remove_busy_worker worker }
|
132
82
|
end
|
133
83
|
|
134
|
-
# Run on task completion.
|
135
|
-
#
|
136
84
|
# @!visibility private
|
137
|
-
def
|
138
|
-
|
139
|
-
@completed_task_count += 1 #if success
|
140
|
-
break unless running?
|
141
|
-
end
|
85
|
+
def ready_worker(worker)
|
86
|
+
synchronize { ns_ready_worker worker }
|
142
87
|
end
|
143
88
|
|
144
|
-
# Run when a thread worker exits.
|
145
|
-
#
|
146
89
|
# @!visibility private
|
147
|
-
def
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
end
|
90
|
+
def worker_not_old_enough(worker)
|
91
|
+
synchronize { ns_worker_not_old_enough worker }
|
92
|
+
end
|
93
|
+
|
94
|
+
# @!visibility private
|
95
|
+
def worker_died(worker)
|
96
|
+
synchronize { ns_worker_died worker }
|
155
97
|
end
|
156
98
|
|
157
99
|
protected
|
158
100
|
|
159
101
|
# @!visibility private
|
160
|
-
def
|
161
|
-
|
162
|
-
|
102
|
+
def ns_initialize(opts)
|
103
|
+
@min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
|
104
|
+
@max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
|
105
|
+
@idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
|
106
|
+
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
|
107
|
+
@fallback_policy = opts.fetch(:fallback_policy, opts.fetch(:overflow_policy, :abort))
|
108
|
+
raise ArgumentError.new("#{@fallback_policy} is not a valid fallback policy") unless FALLBACK_POLICIES.include?(@fallback_policy)
|
109
|
+
deprecated ':overflow_policy is deprecated terminology, please use :fallback_policy instead' if opts.has_key?(:overflow_policy)
|
110
|
+
|
111
|
+
raise ArgumentError.new("`max_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @max_length < DEFAULT_MIN_POOL_SIZE
|
112
|
+
raise ArgumentError.new("`max_threads` cannot be greater than #{DEFAULT_MAX_POOL_SIZE}") if @max_length > DEFAULT_MAX_POOL_SIZE
|
113
|
+
raise ArgumentError.new("`min_threads` cannot be less than #{DEFAULT_MIN_POOL_SIZE}") if @min_length < DEFAULT_MIN_POOL_SIZE
|
114
|
+
raise ArgumentError.new("`min_threads` cannot be more than `max_threads`") if min_length > max_length
|
115
|
+
|
116
|
+
self.auto_terminate = opts.fetch(:auto_terminate, true)
|
117
|
+
|
118
|
+
@pool = [] # all workers
|
119
|
+
@ready = [] # used as a stash (most idle worker is at the start)
|
120
|
+
@queue = [] # used as queue
|
121
|
+
# @ready or @queue is empty at all times
|
122
|
+
@scheduled_task_count = 0
|
123
|
+
@completed_task_count = 0
|
124
|
+
@largest_length = 0
|
125
|
+
|
126
|
+
@gc_interval = opts.fetch(:gc_interval, @idletime / 2.0).to_i # undocumented
|
127
|
+
@next_gc_time = Concurrent.monotonic_time + @gc_interval
|
128
|
+
end
|
129
|
+
|
130
|
+
# @!visibility private
|
131
|
+
def ns_limited_queue?
|
132
|
+
@max_queue != 0
|
133
|
+
end
|
134
|
+
|
135
|
+
# @!visibility private
|
136
|
+
def ns_execute(*args, &task)
|
137
|
+
if ns_assign_worker(*args, &task) || ns_enqueue(*args, &task)
|
163
138
|
@scheduled_task_count += 1
|
164
|
-
@queue << [args, task]
|
165
139
|
else
|
166
|
-
handle_fallback(*args, &task)
|
140
|
+
handle_fallback(*args, &task)
|
167
141
|
end
|
142
|
+
|
143
|
+
ns_prune_pool if @next_gc_time < Concurrent.monotonic_time
|
144
|
+
# raise unless @ready.empty? || @queue.empty? # assert
|
168
145
|
end
|
169
146
|
|
147
|
+
alias_method :execute, :ns_execute
|
148
|
+
|
170
149
|
# @!visibility private
|
171
|
-
def
|
150
|
+
def ns_shutdown_execution
|
172
151
|
if @pool.empty?
|
152
|
+
# nothing to do
|
173
153
|
stopped_event.set
|
174
|
-
else
|
175
|
-
@pool.length.times { @queue << :stop }
|
176
154
|
end
|
155
|
+
if @queue.empty?
|
156
|
+
# no more tasks will be accepted, just stop all workers
|
157
|
+
@pool.each(&:stop)
|
158
|
+
end
|
159
|
+
|
160
|
+
# raise unless @ready.empty? || @queue.empty? # assert
|
177
161
|
end
|
178
162
|
|
163
|
+
alias_method :shutdown_execution, :ns_shutdown_execution
|
164
|
+
|
179
165
|
# @!visibility private
|
180
|
-
def
|
181
|
-
|
182
|
-
|
166
|
+
def ns_kill_execution
|
167
|
+
# TODO log out unprocessed tasks in queue
|
168
|
+
# TODO try to shutdown first?
|
169
|
+
@pool.each &:kill
|
170
|
+
@pool.clear
|
171
|
+
@ready.clear
|
183
172
|
end
|
184
173
|
|
185
|
-
|
186
|
-
|
187
|
-
#
|
188
|
-
#
|
189
|
-
# @return [Boolean] true if the pool has enough capacity else false
|
174
|
+
alias_method :kill_execution, :ns_kill_execution
|
175
|
+
|
176
|
+
# tries to assign task to a worker, tries to get one from @ready or to create new one
|
177
|
+
# @return [true, false] if task is assigned to a worker
|
190
178
|
#
|
191
179
|
# @!visibility private
|
192
|
-
def
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
elsif @queue.empty? && @queue.num_waiting >= 1
|
199
|
-
additional = 0
|
200
|
-
elsif @pool.size == 0 && @min_length == 0
|
201
|
-
additional = 1
|
202
|
-
elsif @pool.size < @max_length || @max_length == 0
|
203
|
-
additional = 1
|
204
|
-
elsif @max_queue == 0 || @queue.size < @max_queue
|
205
|
-
additional = 0
|
180
|
+
def ns_assign_worker(*args, &task)
|
181
|
+
# keep growing if the pool is not at the minimum yet
|
182
|
+
worker = (@ready.pop if @pool.size >= @min_length) || ns_add_busy_worker
|
183
|
+
if worker
|
184
|
+
worker << [task, args]
|
185
|
+
true
|
206
186
|
else
|
207
|
-
|
187
|
+
false
|
208
188
|
end
|
189
|
+
rescue ThreadError
|
190
|
+
# Raised when the operating system refuses to create the new thread
|
191
|
+
return false
|
192
|
+
end
|
209
193
|
|
210
|
-
|
211
|
-
|
194
|
+
# tries to enqueue task
|
195
|
+
# @return [true, false] if enqueued
|
196
|
+
#
|
197
|
+
# @!visibility private
|
198
|
+
def ns_enqueue(*args, &task)
|
199
|
+
if !ns_limited_queue? || @queue.size < @max_queue
|
200
|
+
@queue << [task, args]
|
201
|
+
true
|
202
|
+
else
|
203
|
+
false
|
212
204
|
end
|
205
|
+
end
|
213
206
|
|
214
|
-
|
215
|
-
|
216
|
-
|
207
|
+
# @!visibility private
|
208
|
+
def ns_worker_died(worker)
|
209
|
+
ns_remove_busy_worker worker
|
210
|
+
replacement_worker = ns_add_busy_worker
|
211
|
+
ns_ready_worker replacement_worker, false if replacement_worker
|
212
|
+
end
|
213
|
+
|
214
|
+
# creates new worker which has to receive work to do after it's added
|
215
|
+
# @return [nil, Worker] nil of max capacity is reached
|
216
|
+
#
|
217
|
+
# @!visibility private
|
218
|
+
def ns_add_busy_worker
|
219
|
+
return if @pool.size >= @max_length
|
217
220
|
|
218
|
-
|
221
|
+
@pool << (worker = Worker.new(self))
|
222
|
+
@largest_length = @pool.length if @pool.length > @largest_length
|
223
|
+
worker
|
219
224
|
end
|
220
225
|
|
221
|
-
#
|
222
|
-
# have been idle too long. Will check the last time the pool was
|
223
|
-
# pruned and only run if the configured garbage collection
|
224
|
-
# interval has passed.
|
226
|
+
# handle ready worker, giving it new job or assigning back to @ready
|
225
227
|
#
|
226
228
|
# @!visibility private
|
227
|
-
def
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
229
|
+
def ns_ready_worker(worker, success = true)
|
230
|
+
@completed_task_count += 1 if success
|
231
|
+
task_and_args = @queue.shift
|
232
|
+
if task_and_args
|
233
|
+
worker << task_and_args
|
234
|
+
else
|
235
|
+
# stop workers when !running?, do not return them to @ready
|
236
|
+
if running?
|
237
|
+
@ready.push(worker)
|
238
|
+
else
|
239
|
+
worker.stop
|
240
|
+
end
|
235
241
|
end
|
236
242
|
end
|
237
243
|
|
238
|
-
#
|
244
|
+
# returns back worker to @ready which was not idle for enough time
|
239
245
|
#
|
240
246
|
# @!visibility private
|
241
|
-
def
|
242
|
-
|
243
|
-
@
|
247
|
+
def ns_worker_not_old_enough(worker)
|
248
|
+
# let's put workers coming from idle_test back to the start (as the oldest worker)
|
249
|
+
@ready.unshift(worker)
|
250
|
+
true
|
244
251
|
end
|
245
252
|
|
246
|
-
#
|
253
|
+
# removes a worker which is not in not tracked in @ready
|
247
254
|
#
|
248
|
-
#
|
255
|
+
# @!visibility private
|
256
|
+
def ns_remove_busy_worker(worker)
|
257
|
+
@pool.delete(worker)
|
258
|
+
stopped_event.set if @pool.empty? && !running?
|
259
|
+
true
|
260
|
+
end
|
261
|
+
|
262
|
+
# try oldest worker if it is idle for enough time, it's returned back at the start
|
249
263
|
#
|
250
264
|
# @!visibility private
|
251
|
-
def
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
265
|
+
def ns_prune_pool
|
266
|
+
return if @pool.size <= @min_length
|
267
|
+
|
268
|
+
last_used = @ready.shift
|
269
|
+
last_used << :idle_test if last_used
|
270
|
+
|
271
|
+
@next_gc_time = Concurrent.monotonic_time + @gc_interval
|
272
|
+
end
|
273
|
+
|
274
|
+
# @!visibility private
|
275
|
+
class Worker
|
276
|
+
include Concern::Logging
|
277
|
+
|
278
|
+
def initialize(pool)
|
279
|
+
# instance variables accessed only under pool's lock so no need to sync here again
|
280
|
+
@queue = Queue.new
|
281
|
+
@pool = pool
|
282
|
+
@thread = create_worker @queue, pool, pool.idletime
|
283
|
+
end
|
284
|
+
|
285
|
+
def <<(message)
|
286
|
+
@queue << message
|
287
|
+
end
|
288
|
+
|
289
|
+
def stop
|
290
|
+
@queue << :stop
|
291
|
+
end
|
292
|
+
|
293
|
+
def kill
|
294
|
+
@thread.kill
|
295
|
+
end
|
296
|
+
|
297
|
+
private
|
298
|
+
|
299
|
+
def create_worker(queue, pool, idletime)
|
300
|
+
Thread.new(queue, pool, idletime) do |queue, pool, idletime|
|
301
|
+
last_message = Concurrent.monotonic_time
|
302
|
+
catch(:stop) do
|
303
|
+
loop do
|
304
|
+
|
305
|
+
case message = queue.pop
|
306
|
+
when :idle_test
|
307
|
+
if (Concurrent.monotonic_time - last_message) > idletime
|
308
|
+
pool.remove_busy_worker(self)
|
309
|
+
throw :stop
|
310
|
+
else
|
311
|
+
pool.worker_not_old_enough(self)
|
312
|
+
end
|
313
|
+
|
314
|
+
when :stop
|
315
|
+
pool.remove_busy_worker(self)
|
316
|
+
throw :stop
|
317
|
+
|
318
|
+
else
|
319
|
+
task, args = message
|
320
|
+
run_task pool, task, args
|
321
|
+
last_message = Concurrent.monotonic_time
|
322
|
+
|
323
|
+
pool.ready_worker(self)
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def run_task(pool, task, args)
|
332
|
+
task.call(*args)
|
333
|
+
rescue => ex
|
334
|
+
# let it fail
|
335
|
+
log DEBUG, ex
|
336
|
+
rescue Exception => ex
|
337
|
+
log ERROR, ex
|
338
|
+
pool.worker_died(self)
|
339
|
+
throw :stop
|
257
340
|
end
|
258
|
-
return wrkr
|
259
341
|
end
|
342
|
+
|
343
|
+
private_constant :Worker
|
260
344
|
end
|
261
345
|
end
|