concurrent-ruby 0.7.0.rc0-x86-mingw32
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 +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
- data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
- data/ext/concurrent_ruby_ext/extconf.rb +59 -0
- data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
- data/lib/1.9/concurrent_ruby_ext.so +0 -0
- data/lib/2.0/concurrent_ruby_ext.so +0 -0
- data/lib/concurrent.rb +45 -0
- data/lib/concurrent/actress.rb +221 -0
- data/lib/concurrent/actress/ad_hoc.rb +20 -0
- data/lib/concurrent/actress/context.rb +98 -0
- data/lib/concurrent/actress/core.rb +228 -0
- data/lib/concurrent/actress/core_delegations.rb +42 -0
- data/lib/concurrent/actress/envelope.rb +41 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +232 -0
- data/lib/concurrent/async.rb +319 -0
- data/lib/concurrent/atomic.rb +46 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
- data/lib/concurrent/atomic/condition.rb +67 -0
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +98 -0
- data/lib/concurrent/atomic/thread_local_var.rb +117 -0
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
- data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
- data/lib/concurrent/atomic_reference/jruby.rb +8 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
- data/lib/concurrent/atomic_reference/rbx.rb +16 -0
- data/lib/concurrent/atomic_reference/ruby.rb +16 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/channel/buffered_channel.rb +85 -0
- data/lib/concurrent/channel/channel.rb +41 -0
- data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
- data/lib/concurrent/channel/waitable_list.rb +40 -0
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/collection/ring_buffer.rb +59 -0
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +158 -0
- data/lib/concurrent/dataflow.rb +91 -0
- data/lib/concurrent/delay.rb +112 -0
- data/lib/concurrent/dereferenceable.rb +101 -0
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/exchanger.rb +34 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
- data/lib/concurrent/executor/executor.rb +229 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
- data/lib/concurrent/executor/immediate_executor.rb +16 -0
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
- data/lib/concurrent/executor/per_thread_executor.rb +24 -0
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serialized_execution.rb +90 -0
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +143 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +124 -0
- data/lib/concurrent/ivar.rb +111 -0
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +200 -0
- data/lib/concurrent/obligation.rb +171 -0
- data/lib/concurrent/observable.rb +40 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +169 -0
- data/lib/concurrent/scheduled_task.rb +78 -0
- data/lib/concurrent/supervisor.rb +343 -0
- data/lib/concurrent/timer_task.rb +341 -0
- data/lib/concurrent/tvar.rb +252 -0
- data/lib/concurrent/utilities.rb +3 -0
- data/lib/concurrent/utility/processor_count.rb +150 -0
- data/lib/concurrent/utility/timeout.rb +35 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +3 -0
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +142 -0
@@ -0,0 +1,286 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
require_relative 'executor'
|
4
|
+
require 'concurrent/atomic/event'
|
5
|
+
require 'concurrent/executor/ruby_thread_pool_worker'
|
6
|
+
|
7
|
+
module Concurrent
|
8
|
+
|
9
|
+
# @!macro thread_pool_executor
|
10
|
+
class RubyThreadPoolExecutor
|
11
|
+
include RubyExecutor
|
12
|
+
|
13
|
+
# Default maximum number of threads that will be created in the pool.
|
14
|
+
DEFAULT_MAX_POOL_SIZE = 2**15 # 32768
|
15
|
+
|
16
|
+
# Default minimum number of threads that will be retained in the pool.
|
17
|
+
DEFAULT_MIN_POOL_SIZE = 0
|
18
|
+
|
19
|
+
# Default maximum number of tasks that may be added to the task queue.
|
20
|
+
DEFAULT_MAX_QUEUE_SIZE = 0
|
21
|
+
|
22
|
+
# Default maximum number of seconds a thread in the pool may remain idle
|
23
|
+
# before being reclaimed.
|
24
|
+
DEFAULT_THREAD_IDLETIMEOUT = 60
|
25
|
+
|
26
|
+
# The set of possible overflow policies that may be set at thread pool creation.
|
27
|
+
OVERFLOW_POLICIES = [:abort, :discard, :caller_runs]
|
28
|
+
|
29
|
+
# The maximum number of threads that may be created in the pool.
|
30
|
+
attr_reader :max_length
|
31
|
+
|
32
|
+
# The minimum number of threads that may be retained in the pool.
|
33
|
+
attr_reader :min_length
|
34
|
+
|
35
|
+
# The largest number of threads that have been created in the pool since construction.
|
36
|
+
attr_reader :largest_length
|
37
|
+
|
38
|
+
# The number of tasks that have been scheduled for execution on the pool since construction.
|
39
|
+
attr_reader :scheduled_task_count
|
40
|
+
|
41
|
+
# The number of tasks that have been completed by the pool since construction.
|
42
|
+
attr_reader :completed_task_count
|
43
|
+
|
44
|
+
# The number of seconds that a thread may be idle before being reclaimed.
|
45
|
+
attr_reader :idletime
|
46
|
+
|
47
|
+
# The maximum number of tasks that may be waiting in the work queue at any one time.
|
48
|
+
# When the queue size reaches `max_queue` subsequent tasks will be rejected in
|
49
|
+
# accordance with the configured `overflow_policy`.
|
50
|
+
attr_reader :max_queue
|
51
|
+
|
52
|
+
# The policy defining how rejected tasks (tasks received once the queue size reaches
|
53
|
+
# the configured `max_queue`) are handled. Must be one of the values specified in
|
54
|
+
# `OVERFLOW_POLICIES`.
|
55
|
+
attr_reader :overflow_policy
|
56
|
+
|
57
|
+
# Create a new thread pool.
|
58
|
+
#
|
59
|
+
# @param [Hash] opts the options which configure the thread pool
|
60
|
+
#
|
61
|
+
# @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
|
62
|
+
# number of threads to be created
|
63
|
+
# @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) the minimum
|
64
|
+
# number of threads to be retained
|
65
|
+
# @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum
|
66
|
+
# number of seconds a thread may be idle before being reclaimed
|
67
|
+
# @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum
|
68
|
+
# number of tasks allowed in the work queue at any one time; a value of
|
69
|
+
# zero means the queue may grow without bounnd
|
70
|
+
# @option opts [Symbol] :overflow_policy (:abort) the policy for handling new
|
71
|
+
# tasks that are received when the queue size has reached `max_queue`
|
72
|
+
#
|
73
|
+
# @raise [ArgumentError] if `:max_threads` is less than one
|
74
|
+
# @raise [ArgumentError] if `:min_threads` is less than zero
|
75
|
+
# @raise [ArgumentError] if `:overflow_policy` is not one of the values specified
|
76
|
+
# in `OVERFLOW_POLICIES`
|
77
|
+
#
|
78
|
+
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
|
79
|
+
def initialize(opts = {})
|
80
|
+
@min_length = opts.fetch(:min_threads, DEFAULT_MIN_POOL_SIZE).to_i
|
81
|
+
@max_length = opts.fetch(:max_threads, DEFAULT_MAX_POOL_SIZE).to_i
|
82
|
+
@idletime = opts.fetch(:idletime, DEFAULT_THREAD_IDLETIMEOUT).to_i
|
83
|
+
@max_queue = opts.fetch(:max_queue, DEFAULT_MAX_QUEUE_SIZE).to_i
|
84
|
+
@overflow_policy = opts.fetch(:overflow_policy, :abort)
|
85
|
+
|
86
|
+
raise ArgumentError.new('max_threads must be greater than zero') if @max_length <= 0
|
87
|
+
raise ArgumentError.new('min_threads cannot be less than zero') if @min_length < 0
|
88
|
+
raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(@overflow_policy)
|
89
|
+
|
90
|
+
init_executor
|
91
|
+
|
92
|
+
@pool = []
|
93
|
+
@queue = Queue.new
|
94
|
+
@scheduled_task_count = 0
|
95
|
+
@completed_task_count = 0
|
96
|
+
@largest_length = 0
|
97
|
+
|
98
|
+
@gc_interval = opts.fetch(:gc_interval, 1).to_i # undocumented
|
99
|
+
@last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
|
100
|
+
end
|
101
|
+
|
102
|
+
def can_overflow?
|
103
|
+
@max_queue != 0
|
104
|
+
end
|
105
|
+
|
106
|
+
# The number of threads currently in the pool.
|
107
|
+
#
|
108
|
+
# @return [Integer] the length
|
109
|
+
def length
|
110
|
+
mutex.synchronize{ running? ? @pool.length : 0 }
|
111
|
+
end
|
112
|
+
alias_method :current_length, :length
|
113
|
+
|
114
|
+
# The number of tasks in the queue awaiting execution.
|
115
|
+
#
|
116
|
+
# @return [Integer] the queue_length
|
117
|
+
def queue_length
|
118
|
+
mutex.synchronize{ running? ? @queue.length : 0 }
|
119
|
+
end
|
120
|
+
|
121
|
+
# Number of tasks that may be enqueued before reaching `max_queue` and rejecting
|
122
|
+
# new tasks. A value of -1 indicates that the queue may grow without bound.
|
123
|
+
#
|
124
|
+
# @return [Integer] the remaining_capacity
|
125
|
+
def remaining_capacity
|
126
|
+
mutex.synchronize { @max_queue == 0 ? -1 : @max_queue - @queue.length }
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns an array with the status of each thread in the pool
|
130
|
+
#
|
131
|
+
# This method is deprecated and will be removed soon.
|
132
|
+
def status
|
133
|
+
warn '[DEPRECATED] `status` is deprecated and will be removed soon.'
|
134
|
+
mutex.synchronize { @pool.collect { |worker| worker.status } }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Run on task completion.
|
138
|
+
#
|
139
|
+
# @!visibility private
|
140
|
+
def on_end_task
|
141
|
+
mutex.synchronize do
|
142
|
+
@completed_task_count += 1 #if success
|
143
|
+
break unless running?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Run when a thread worker exits.
|
148
|
+
#
|
149
|
+
# @!visibility private
|
150
|
+
def on_worker_exit(worker)
|
151
|
+
mutex.synchronize do
|
152
|
+
@pool.delete(worker)
|
153
|
+
if @pool.empty? && ! running?
|
154
|
+
stop_event.set
|
155
|
+
stopped_event.set
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
protected
|
161
|
+
|
162
|
+
# @!visibility private
|
163
|
+
def execute(*args, &task)
|
164
|
+
prune_pool
|
165
|
+
if ensure_capacity?
|
166
|
+
@scheduled_task_count += 1
|
167
|
+
@queue << [args, task]
|
168
|
+
else
|
169
|
+
handle_overflow(*args, &task) if @max_queue != 0 && @queue.length >= @max_queue
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# @!visibility private
|
174
|
+
def shutdown_execution
|
175
|
+
if @pool.empty?
|
176
|
+
stopped_event.set
|
177
|
+
else
|
178
|
+
@pool.length.times{ @queue << :stop }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# @!visibility private
|
183
|
+
def kill_execution
|
184
|
+
@queue.clear
|
185
|
+
drain_pool
|
186
|
+
end
|
187
|
+
|
188
|
+
# Check the thread pool configuration and determine if the pool
|
189
|
+
# has enought capacity to handle the request. Will grow the size
|
190
|
+
# of the pool if necessary.
|
191
|
+
#
|
192
|
+
# @return [Boolean] true if the pool has enough capacity else false
|
193
|
+
#
|
194
|
+
# @!visibility private
|
195
|
+
def ensure_capacity?
|
196
|
+
additional = 0
|
197
|
+
capacity = true
|
198
|
+
|
199
|
+
if @pool.size < @min_length
|
200
|
+
additional = @min_length - @pool.size
|
201
|
+
elsif @queue.empty? && @queue.num_waiting >= 1
|
202
|
+
additional = 0
|
203
|
+
elsif @pool.size == 0 && @min_length == 0
|
204
|
+
additional = 1
|
205
|
+
elsif @pool.size < @max_length || @max_length == 0
|
206
|
+
additional = 1
|
207
|
+
elsif @max_queue == 0 || @queue.size < @max_queue
|
208
|
+
additional = 0
|
209
|
+
else
|
210
|
+
capacity = false
|
211
|
+
end
|
212
|
+
|
213
|
+
additional.times do
|
214
|
+
@pool << create_worker_thread
|
215
|
+
end
|
216
|
+
|
217
|
+
if additional > 0
|
218
|
+
@largest_length = [@largest_length, @pool.length].max
|
219
|
+
end
|
220
|
+
|
221
|
+
capacity
|
222
|
+
end
|
223
|
+
|
224
|
+
# Handler which executes the `overflow_policy` once the queue size
|
225
|
+
# reaches `max_queue`.
|
226
|
+
#
|
227
|
+
# @param [Array] args the arguments to the task which is being handled.
|
228
|
+
#
|
229
|
+
# @!visibility private
|
230
|
+
def handle_overflow(*args)
|
231
|
+
case @overflow_policy
|
232
|
+
when :abort
|
233
|
+
raise RejectedExecutionError
|
234
|
+
when :discard
|
235
|
+
false
|
236
|
+
when :caller_runs
|
237
|
+
begin
|
238
|
+
yield(*args)
|
239
|
+
rescue => ex
|
240
|
+
# let it fail
|
241
|
+
log DEBUG, ex
|
242
|
+
end
|
243
|
+
true
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Scan all threads in the pool and reclaim any that are dead or
|
248
|
+
# have been idle too long. Will check the last time the pool was
|
249
|
+
# pruned and only run if the configured garbage collection
|
250
|
+
# interval has passed.
|
251
|
+
#
|
252
|
+
# @!visibility private
|
253
|
+
def prune_pool
|
254
|
+
if Time.now.to_f - @gc_interval >= @last_gc_time
|
255
|
+
@pool.delete_if do |worker|
|
256
|
+
worker.dead? ||
|
257
|
+
(@idletime == 0 ? false : Time.now.to_f - @idletime > worker.last_activity)
|
258
|
+
end
|
259
|
+
@last_gc_time = Time.now.to_f
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
# Reclaim all threads in the pool.
|
264
|
+
#
|
265
|
+
# @!visibility private
|
266
|
+
def drain_pool
|
267
|
+
@pool.each {|worker| worker.kill }
|
268
|
+
@pool.clear
|
269
|
+
end
|
270
|
+
|
271
|
+
# Create a single worker thread to be added to the pool.
|
272
|
+
#
|
273
|
+
# @return [Thread] the new thread.
|
274
|
+
#
|
275
|
+
# @!visibility private
|
276
|
+
def create_worker_thread
|
277
|
+
wrkr = RubyThreadPoolWorker.new(@queue, self)
|
278
|
+
Thread.new(wrkr, self) do |worker, parent|
|
279
|
+
Thread.current.abort_on_exception = false
|
280
|
+
worker.run
|
281
|
+
parent.on_worker_exit(worker)
|
282
|
+
end
|
283
|
+
return wrkr
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'concurrent/logging'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# @!visibility private
|
7
|
+
class RubyThreadPoolWorker
|
8
|
+
include Logging
|
9
|
+
|
10
|
+
# @!visibility private
|
11
|
+
def initialize(queue, parent)
|
12
|
+
@queue = queue
|
13
|
+
@parent = parent
|
14
|
+
@mutex = Mutex.new
|
15
|
+
@last_activity = Time.now.to_f
|
16
|
+
end
|
17
|
+
|
18
|
+
# @!visibility private
|
19
|
+
def dead?
|
20
|
+
return @mutex.synchronize do
|
21
|
+
@thread.nil? ? false : ! @thread.alive?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @!visibility private
|
26
|
+
def last_activity
|
27
|
+
@mutex.synchronize { @last_activity }
|
28
|
+
end
|
29
|
+
|
30
|
+
def status
|
31
|
+
@mutex.synchronize do
|
32
|
+
return 'not running' if @thread.nil?
|
33
|
+
@thread.status
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @!visibility private
|
38
|
+
def kill
|
39
|
+
@mutex.synchronize do
|
40
|
+
Thread.kill(@thread) unless @thread.nil?
|
41
|
+
@thread = nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# @!visibility private
|
46
|
+
def run(thread = Thread.current)
|
47
|
+
@mutex.synchronize do
|
48
|
+
raise StandardError.new('already running') unless @thread.nil?
|
49
|
+
@thread = thread
|
50
|
+
end
|
51
|
+
|
52
|
+
loop do
|
53
|
+
task = @queue.pop
|
54
|
+
if task == :stop
|
55
|
+
@thread = nil
|
56
|
+
@parent.on_worker_exit(self)
|
57
|
+
break
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
task.last.call(*task.first)
|
62
|
+
rescue => ex
|
63
|
+
# let it fail
|
64
|
+
log DEBUG, ex
|
65
|
+
ensure
|
66
|
+
@last_activity = Time.now.to_f
|
67
|
+
@parent.on_end_task
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# A simple utility class that executes a callable and returns and array of three elements:
|
6
|
+
# success - indicating if the callable has been executed without errors
|
7
|
+
# value - filled by the callable result if it has been executed without errors, nil otherwise
|
8
|
+
# reason - the error risen by the callable if it has been executed with errors, nil otherwise
|
9
|
+
class SafeTaskExecutor
|
10
|
+
|
11
|
+
def initialize(task, opts = {})
|
12
|
+
@task = task
|
13
|
+
@mutex = Mutex.new
|
14
|
+
@exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Array]
|
18
|
+
def execute(*args)
|
19
|
+
@mutex.synchronize do
|
20
|
+
success = false
|
21
|
+
value = reason = nil
|
22
|
+
|
23
|
+
begin
|
24
|
+
value = @task.call(*args)
|
25
|
+
success = true
|
26
|
+
rescue @exception_class => ex
|
27
|
+
reason = ex
|
28
|
+
success = false
|
29
|
+
end
|
30
|
+
|
31
|
+
[success, value, reason]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'concurrent/logging'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# Ensures passed jobs in a serialized order never running at the same time.
|
6
|
+
class SerializedExecution
|
7
|
+
include Logging
|
8
|
+
|
9
|
+
Job = Struct.new(:executor, :args, :block) do
|
10
|
+
def call
|
11
|
+
block.call *args
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@being_executed = false
|
17
|
+
@stash = []
|
18
|
+
@mutex = Mutex.new
|
19
|
+
end
|
20
|
+
|
21
|
+
# Submit a task to the executor for asynchronous processing.
|
22
|
+
#
|
23
|
+
# @param [Executor] executor to be used for this job
|
24
|
+
#
|
25
|
+
# @param [Array] args zero or more arguments to be passed to the task
|
26
|
+
#
|
27
|
+
# @yield the asynchronous task to perform
|
28
|
+
#
|
29
|
+
# @return [Boolean] `true` if the task is queued, `false` if the executor
|
30
|
+
# is not running
|
31
|
+
#
|
32
|
+
# @raise [ArgumentError] if no task is given
|
33
|
+
def post(executor, *args, &task)
|
34
|
+
return nil if task.nil?
|
35
|
+
|
36
|
+
job = Job.new executor, args, task
|
37
|
+
|
38
|
+
begin
|
39
|
+
@mutex.lock
|
40
|
+
post = if @being_executed
|
41
|
+
@stash << job
|
42
|
+
false
|
43
|
+
else
|
44
|
+
@being_executed = true
|
45
|
+
end
|
46
|
+
ensure
|
47
|
+
@mutex.unlock
|
48
|
+
end
|
49
|
+
|
50
|
+
call_job job if post
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def call_job(job)
|
57
|
+
did_it_run = begin
|
58
|
+
job.executor.post { work(job) }
|
59
|
+
true
|
60
|
+
rescue RejectedExecutionError => ex
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# TODO not the best idea to run it myself
|
65
|
+
unless did_it_run
|
66
|
+
begin
|
67
|
+
work job
|
68
|
+
rescue => ex
|
69
|
+
# let it fail
|
70
|
+
log DEBUG, ex
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# ensures next job is executed if any is stashed
|
76
|
+
def work(job)
|
77
|
+
job.call
|
78
|
+
ensure
|
79
|
+
begin
|
80
|
+
@mutex.lock
|
81
|
+
job = @stash.shift || (@being_executed = false)
|
82
|
+
ensure
|
83
|
+
@mutex.unlock
|
84
|
+
end
|
85
|
+
|
86
|
+
call_job job if job
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|