concurrent-ruby 0.8.0.pre2-java → 0.9.0-java
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 +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,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'concurrent/synchronization'
|
2
2
|
|
3
3
|
module Concurrent
|
4
4
|
|
@@ -6,17 +6,18 @@ module Concurrent
|
|
6
6
|
# success - indicating if the callable has been executed without errors
|
7
7
|
# value - filled by the callable result if it has been executed without errors, nil otherwise
|
8
8
|
# reason - the error risen by the callable if it has been executed with errors, nil otherwise
|
9
|
-
class SafeTaskExecutor
|
9
|
+
class SafeTaskExecutor < Synchronization::Object
|
10
10
|
|
11
11
|
def initialize(task, opts = {})
|
12
|
+
super()
|
12
13
|
@task = task
|
13
|
-
@mutex = Mutex.new
|
14
14
|
@exception_class = opts.fetch(:rescue_exception, false) ? Exception : StandardError
|
15
|
+
ensure_ivar_visibility!
|
15
16
|
end
|
16
17
|
|
17
18
|
# @return [Array]
|
18
19
|
def execute(*args)
|
19
|
-
|
20
|
+
synchronize do
|
20
21
|
success = false
|
21
22
|
value = reason = nil
|
22
23
|
|
@@ -1,14 +1,18 @@
|
|
1
1
|
require 'delegate'
|
2
|
-
require 'concurrent/executor/
|
3
|
-
require 'concurrent/logging'
|
4
|
-
require 'concurrent/
|
2
|
+
require 'concurrent/executor/executor_service'
|
3
|
+
require 'concurrent/concern/logging'
|
4
|
+
require 'concurrent/synchronization'
|
5
5
|
|
6
6
|
module Concurrent
|
7
7
|
|
8
8
|
# Ensures passed jobs in a serialized order never running at the same time.
|
9
|
-
class SerializedExecution
|
10
|
-
include Logging
|
11
|
-
|
9
|
+
class SerializedExecution < Synchronization::Object
|
10
|
+
include Concern::Logging
|
11
|
+
|
12
|
+
def initialize()
|
13
|
+
super()
|
14
|
+
synchronize { ns_initialize }
|
15
|
+
end
|
12
16
|
|
13
17
|
Job = Struct.new(:executor, :args, :block) do
|
14
18
|
def call
|
@@ -16,13 +20,6 @@ module Concurrent
|
|
16
20
|
end
|
17
21
|
end
|
18
22
|
|
19
|
-
def initialize
|
20
|
-
synchronize do
|
21
|
-
@being_executed = false
|
22
|
-
@stash = []
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
23
|
# Submit a task to the executor for asynchronous processing.
|
27
24
|
#
|
28
25
|
# @param [Executor] executor to be used for this job
|
@@ -69,7 +66,12 @@ module Concurrent
|
|
69
66
|
true
|
70
67
|
end
|
71
68
|
|
72
|
-
|
69
|
+
protected
|
70
|
+
|
71
|
+
def ns_initialize
|
72
|
+
@being_executed = false
|
73
|
+
@stash = []
|
74
|
+
end
|
73
75
|
|
74
76
|
def call_job(job)
|
75
77
|
did_it_run = begin
|
@@ -98,17 +100,19 @@ module Concurrent
|
|
98
100
|
job = @stash.shift || (@being_executed = false)
|
99
101
|
end
|
100
102
|
|
103
|
+
# TODO maybe be able to tell caching pool to just enqueue this job, because the current one end at the end
|
104
|
+
# of this block
|
101
105
|
call_job job if job
|
102
106
|
end
|
103
107
|
end
|
104
108
|
|
105
|
-
# A wrapper/delegator for any `
|
109
|
+
# A wrapper/delegator for any `ExecutorService` that
|
106
110
|
# guarantees serialized execution of tasks.
|
107
111
|
#
|
108
112
|
# @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html)
|
109
113
|
# @see Concurrent::SerializedExecution
|
110
114
|
class SerializedExecutionDelegator < SimpleDelegator
|
111
|
-
include
|
115
|
+
include SerialExecutorService
|
112
116
|
|
113
117
|
def initialize(executor)
|
114
118
|
@executor = executor
|
@@ -116,9 +120,9 @@ module Concurrent
|
|
116
120
|
super(executor)
|
117
121
|
end
|
118
122
|
|
119
|
-
# @!macro
|
123
|
+
# @!macro executor_service_method_post
|
120
124
|
def post(*args, &task)
|
121
|
-
raise ArgumentError.new('no block given') unless block_given?
|
125
|
+
Kernel.raise ArgumentError.new('no block given') unless block_given?
|
122
126
|
return false unless running?
|
123
127
|
@serializer.post(@executor, *args, &task)
|
124
128
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'concurrent/atomics'
|
2
|
-
require 'concurrent/executor/
|
2
|
+
require 'concurrent/executor/executor_service'
|
3
3
|
|
4
4
|
module Concurrent
|
5
5
|
|
@@ -15,17 +15,9 @@ module Concurrent
|
|
15
15
|
# lead to suboptimal performance.
|
16
16
|
#
|
17
17
|
# @note Intended for use primarily in testing and debugging.
|
18
|
-
class
|
19
|
-
include Executor
|
18
|
+
class SimpleExecutorService < RubyExecutorService
|
20
19
|
|
21
|
-
#
|
22
|
-
def initialize
|
23
|
-
@running = Concurrent::AtomicBoolean.new(true)
|
24
|
-
@stopped = Concurrent::Event.new
|
25
|
-
@count = Concurrent::AtomicFixnum.new(0)
|
26
|
-
end
|
27
|
-
|
28
|
-
# @!macro executor_method_post
|
20
|
+
# @!macro executor_service_method_post
|
29
21
|
def self.post(*args)
|
30
22
|
raise ArgumentError.new('no block given') unless block_given?
|
31
23
|
Thread.new(*args) do
|
@@ -35,13 +27,13 @@ module Concurrent
|
|
35
27
|
true
|
36
28
|
end
|
37
29
|
|
38
|
-
# @!macro
|
30
|
+
# @!macro executor_service_method_left_shift
|
39
31
|
def self.<<(task)
|
40
32
|
post(&task)
|
41
33
|
self
|
42
34
|
end
|
43
35
|
|
44
|
-
# @!macro
|
36
|
+
# @!macro executor_service_method_post
|
45
37
|
def post(*args, &task)
|
46
38
|
raise ArgumentError.new('no block given') unless block_given?
|
47
39
|
return false unless running?
|
@@ -57,44 +49,61 @@ module Concurrent
|
|
57
49
|
end
|
58
50
|
end
|
59
51
|
|
60
|
-
# @!macro
|
52
|
+
# @!macro executor_service_method_left_shift
|
61
53
|
def <<(task)
|
62
54
|
post(&task)
|
63
55
|
self
|
64
56
|
end
|
65
57
|
|
66
|
-
# @!macro
|
58
|
+
# @!macro executor_service_method_running_question
|
67
59
|
def running?
|
68
60
|
@running.true?
|
69
61
|
end
|
70
62
|
|
71
|
-
# @!macro
|
63
|
+
# @!macro executor_service_method_shuttingdown_question
|
72
64
|
def shuttingdown?
|
73
65
|
@running.false? && ! @stopped.set?
|
74
66
|
end
|
75
67
|
|
76
|
-
# @!macro
|
68
|
+
# @!macro executor_service_method_shutdown_question
|
77
69
|
def shutdown?
|
78
70
|
@stopped.set?
|
79
71
|
end
|
80
72
|
|
81
|
-
# @!macro
|
73
|
+
# @!macro executor_service_method_shutdown
|
82
74
|
def shutdown
|
83
75
|
@running.make_false
|
84
76
|
@stopped.set if @count.value == 0
|
85
77
|
true
|
86
78
|
end
|
87
79
|
|
88
|
-
# @!macro
|
80
|
+
# @!macro executor_service_method_kill
|
89
81
|
def kill
|
90
82
|
@running.make_false
|
91
83
|
@stopped.set
|
92
84
|
true
|
93
85
|
end
|
94
86
|
|
95
|
-
# @!macro
|
87
|
+
# @!macro executor_service_method_wait_for_termination
|
96
88
|
def wait_for_termination(timeout = nil)
|
97
89
|
@stopped.wait(timeout)
|
98
90
|
end
|
91
|
+
|
92
|
+
protected
|
93
|
+
|
94
|
+
def ns_initialize
|
95
|
+
@running = Concurrent::AtomicBoolean.new(true)
|
96
|
+
@stopped = Concurrent::Event.new
|
97
|
+
@count = Concurrent::AtomicFixnum.new(0)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# @deprecated
|
102
|
+
class PerThreadExecutor < SimpleExecutorService
|
103
|
+
|
104
|
+
def initialize
|
105
|
+
deprecated 'use SimpleExecutorService instead'
|
106
|
+
super
|
107
|
+
end
|
99
108
|
end
|
100
109
|
end
|
@@ -2,34 +2,45 @@ require 'concurrent/executor/ruby_single_thread_executor'
|
|
2
2
|
|
3
3
|
module Concurrent
|
4
4
|
|
5
|
-
if
|
6
|
-
|
5
|
+
if Concurrent.on_jruby?
|
7
6
|
require 'concurrent/executor/java_single_thread_executor'
|
7
|
+
end
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
SingleThreadExecutorImplementation = case
|
10
|
+
when Concurrent.on_jruby?
|
11
|
+
JavaSingleThreadExecutor
|
12
|
+
else
|
13
|
+
RubySingleThreadExecutor
|
14
|
+
end
|
15
|
+
private_constant :SingleThreadExecutorImplementation
|
16
|
+
|
17
|
+
# @!macro [attach] single_thread_executor
|
18
|
+
#
|
19
|
+
# A thread pool with a set number of threads. The number of threads in the pool
|
20
|
+
# is set on construction and remains constant. When all threads are busy new
|
21
|
+
# tasks `#post` to the thread pool are enqueued until a thread becomes available.
|
22
|
+
# Should a thread crash for any reason the thread will immediately be removed
|
23
|
+
# from the pool and replaced.
|
24
|
+
#
|
25
|
+
# The API and behavior of this class are based on Java's `SingleThreadExecutor`
|
26
|
+
#
|
27
|
+
# @!macro thread_pool_options
|
28
|
+
# @!macro abstract_executor_service_public_api
|
29
|
+
class SingleThreadExecutor < SingleThreadExecutorImplementation
|
30
|
+
|
31
|
+
# @!macro [new] single_thread_executor_method_initialize
|
18
32
|
#
|
19
|
-
#
|
20
|
-
# On all other platforms it will inherit from `RubySingleThreadExecutor`.
|
33
|
+
# Create a new thread pool.
|
21
34
|
#
|
22
|
-
# @
|
23
|
-
#
|
35
|
+
# @option opts [Symbol] :fallback_policy (:discard) the policy for
|
36
|
+
# handling new tasks that are received when the queue size has
|
37
|
+
# reached `max_queue` or after the executor has shut down
|
24
38
|
#
|
25
39
|
# @see http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
|
26
40
|
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Executors.html
|
27
41
|
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# @!macro single_thread_executor
|
32
|
-
class SingleThreadExecutor < RubySingleThreadExecutor
|
33
|
-
end
|
42
|
+
|
43
|
+
# @!method initialize(opts = {})
|
44
|
+
# @!macro single_thread_executor_method_initialize
|
34
45
|
end
|
35
46
|
end
|
@@ -1,68 +1,81 @@
|
|
1
|
+
require 'concurrent/utility/engine'
|
1
2
|
require 'concurrent/executor/ruby_thread_pool_executor'
|
2
3
|
|
3
4
|
module Concurrent
|
4
5
|
|
5
|
-
if
|
6
|
+
if Concurrent.on_jruby?
|
6
7
|
require 'concurrent/executor/java_thread_pool_executor'
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
# @note When running on the JVM (JRuby) this class will inherit from `JavaThreadPoolExecutor`.
|
53
|
-
# On all other platforms it will inherit from `RubyThreadPoolExecutor`.
|
54
|
-
#
|
55
|
-
# @see Concurrent::RubyThreadPoolExecutor
|
56
|
-
# @see Concurrent::JavaThreadPoolExecutor
|
8
|
+
end
|
9
|
+
|
10
|
+
ThreadPoolExecutorImplementation = case
|
11
|
+
when Concurrent.on_jruby?
|
12
|
+
JavaThreadPoolExecutor
|
13
|
+
else
|
14
|
+
RubyThreadPoolExecutor
|
15
|
+
end
|
16
|
+
private_constant :ThreadPoolExecutorImplementation
|
17
|
+
|
18
|
+
# @!macro [attach] thread_pool_executor
|
19
|
+
#
|
20
|
+
# An abstraction composed of one or more threads and a task queue. Tasks
|
21
|
+
# (blocks or `proc` objects) are submit to the pool and added to the queue.
|
22
|
+
# The threads in the pool remove the tasks and execute them in the order
|
23
|
+
# they were received. When there are more tasks queued than there are
|
24
|
+
# threads to execute them the pool will create new threads, up to the
|
25
|
+
# configured maximum. Similarly, threads that are idle for too long will
|
26
|
+
# be garbage collected, down to the configured minimum options. Should a
|
27
|
+
# thread crash it, too, will be garbage collected.
|
28
|
+
#
|
29
|
+
# `ThreadPoolExecutor` is based on the Java class of the same name. From
|
30
|
+
# the official Java documentationa;
|
31
|
+
#
|
32
|
+
# > Thread pools address two different problems: they usually provide
|
33
|
+
# > improved performance when executing large numbers of asynchronous tasks,
|
34
|
+
# > due to reduced per-task invocation overhead, and they provide a means
|
35
|
+
# > of bounding and managing the resources, including threads, consumed
|
36
|
+
# > when executing a collection of tasks. Each ThreadPoolExecutor also
|
37
|
+
# > maintains some basic statistics, such as the number of completed tasks.
|
38
|
+
# >
|
39
|
+
# > To be useful across a wide range of contexts, this class provides many
|
40
|
+
# > adjustable parameters and extensibility hooks. However, programmers are
|
41
|
+
# > urged to use the more convenient Executors factory methods
|
42
|
+
# > [CachedThreadPool] (unbounded thread pool, with automatic thread reclamation),
|
43
|
+
# > [FixedThreadPool] (fixed size thread pool) and [SingleThreadExecutor] (single
|
44
|
+
# > background thread), that preconfigure settings for the most common usage
|
45
|
+
# > scenarios.
|
46
|
+
#
|
47
|
+
# @!macro thread_pool_options
|
48
|
+
#
|
49
|
+
# @!macro thread_pool_executor_public_api
|
50
|
+
class ThreadPoolExecutor < ThreadPoolExecutorImplementation
|
51
|
+
|
52
|
+
# @!macro [new] thread_pool_executor_method_initialize
|
57
53
|
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
# @
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
#
|
65
|
-
|
66
|
-
|
54
|
+
# Create a new thread pool.
|
55
|
+
#
|
56
|
+
# @param [Hash] opts the options which configure the thread pool.
|
57
|
+
#
|
58
|
+
# @option opts [Integer] :max_threads (DEFAULT_MAX_POOL_SIZE) the maximum
|
59
|
+
# number of threads to be created
|
60
|
+
# @option opts [Integer] :min_threads (DEFAULT_MIN_POOL_SIZE) the minimum
|
61
|
+
# number of threads to be retained
|
62
|
+
# @option opts [Integer] :idletime (DEFAULT_THREAD_IDLETIMEOUT) the maximum
|
63
|
+
# number of seconds a thread may be idle before being reclaimed
|
64
|
+
# @option opts [Integer] :max_queue (DEFAULT_MAX_QUEUE_SIZE) the maximum
|
65
|
+
# number of tasks allowed in the work queue at any one time; a value of
|
66
|
+
# zero means the queue may grow without bound
|
67
|
+
# @option opts [Symbol] :fallback_policy (:abort) the policy for handling new
|
68
|
+
# tasks that are received when the queue size has reached
|
69
|
+
# `max_queue` or the executor has shut down
|
70
|
+
#
|
71
|
+
# @raise [ArgumentError] if `:max_threads` is less than one
|
72
|
+
# @raise [ArgumentError] if `:min_threads` is less than zero
|
73
|
+
# @raise [ArgumentError] if `:fallback_policy` is not one of the values specified
|
74
|
+
# in `FALLBACK_POLICIES`
|
75
|
+
#
|
76
|
+
# @see http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ThreadPoolExecutor.html
|
77
|
+
|
78
|
+
# @!method initialize(opts = {})
|
79
|
+
# @!macro thread_pool_executor_method_initialize
|
67
80
|
end
|
68
81
|
end
|
@@ -1,114 +1,126 @@
|
|
1
|
-
require '
|
2
|
-
require_relative 'executor'
|
3
|
-
require 'concurrent/options_parser'
|
1
|
+
require 'concurrent/scheduled_task'
|
4
2
|
require 'concurrent/atomic/event'
|
5
3
|
require 'concurrent/collection/priority_queue'
|
4
|
+
require 'concurrent/concern/deprecation'
|
5
|
+
require 'concurrent/executor/executor_service'
|
6
6
|
require 'concurrent/executor/single_thread_executor'
|
7
7
|
|
8
8
|
module Concurrent
|
9
9
|
|
10
|
-
# Executes a collection of tasks
|
10
|
+
# Executes a collection of tasks, each after a given delay. A master task
|
11
11
|
# monitors the set and schedules each task for execution at the appropriate
|
12
|
-
# time. Tasks are run on the global
|
13
|
-
|
14
|
-
|
12
|
+
# time. Tasks are run on the global thread pool or on the supplied executor.
|
13
|
+
# Each task is represented as a `ScheduledTask`.
|
14
|
+
#
|
15
|
+
# @see Concurrent::ScheduledTask
|
16
|
+
#
|
17
|
+
# @!macro monotonic_clock_warning
|
18
|
+
class TimerSet < RubyExecutorService
|
19
|
+
extend Concern::Deprecation
|
15
20
|
|
16
21
|
# Create a new set of timed tasks.
|
17
22
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
23
|
+
# @!macro [attach] executor_options
|
24
|
+
#
|
25
|
+
# @param [Hash] opts the options used to specify the executor on which to perform actions
|
26
|
+
# @option opts [Executor] :executor when set use the given `Executor` instance.
|
27
|
+
# Three special values are also supported: `:task` returns the global task pool,
|
28
|
+
# `:operation` returns the global operation pool, and `:immediate` returns a new
|
29
|
+
# `ImmediateExecutor` object.
|
24
30
|
def initialize(opts = {})
|
25
|
-
|
26
|
-
@task_executor = OptionsParser::get_executor_from(opts) || Concurrent.configuration.global_task_pool
|
27
|
-
@timer_executor = SingleThreadExecutor.new
|
28
|
-
@condition = Condition.new
|
29
|
-
init_executor
|
31
|
+
super(opts)
|
30
32
|
end
|
31
33
|
|
32
|
-
# Post a task to be execute
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
34
|
+
# Post a task to be execute run after a given delay (in seconds). If the
|
35
|
+
# delay is less than 1/100th of a second the task will be immediately post
|
36
|
+
# to the executor.
|
37
|
+
#
|
38
|
+
# @param [Float] delay the number of seconds to wait for before executing the task.
|
39
|
+
# @param [Array<Object>] args the arguments passed to the task on execution.
|
36
40
|
#
|
37
|
-
# @
|
41
|
+
# @yield the task to be performed.
|
38
42
|
#
|
39
|
-
# @
|
43
|
+
# @return [Concurrent::ScheduledTask, false] IVar representing the task if the post
|
44
|
+
# is successful; false after shutdown.
|
40
45
|
#
|
41
|
-
# @
|
46
|
+
# @raise [ArgumentError] if the intended execution time is not in the future.
|
47
|
+
# @raise [ArgumentError] if no block is given.
|
42
48
|
#
|
43
|
-
#
|
44
|
-
|
45
|
-
def post(intended_time, *args, &task)
|
46
|
-
time = TimerSet.calculate_schedule_time(intended_time).to_f
|
49
|
+
# @!macro deprecated_scheduling_by_clock_time
|
50
|
+
def post(delay, *args, &task)
|
47
51
|
raise ArgumentError.new('no block given') unless block_given?
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
@timer_executor.post(&method(:process_tasks))
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
@condition.signal
|
61
|
-
true
|
52
|
+
return false unless running?
|
53
|
+
opts = {
|
54
|
+
executor: @task_executor,
|
55
|
+
args: args,
|
56
|
+
timer_set: self
|
57
|
+
}
|
58
|
+
task = ScheduledTask.execute(delay, opts, &task) # may raise exception
|
59
|
+
task.unscheduled? ? false : task
|
62
60
|
end
|
63
61
|
|
64
|
-
#
|
65
|
-
#
|
62
|
+
# Begin an immediate shutdown. In-progress tasks will be allowed to
|
63
|
+
# complete but enqueued tasks will be dismissed and no new tasks
|
64
|
+
# will be accepted. Has no additional effect if the thread pool is
|
65
|
+
# not running.
|
66
66
|
def kill
|
67
|
-
mutex.synchronize { @queue.clear }
|
68
67
|
shutdown
|
69
68
|
end
|
70
69
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
#
|
70
|
+
private :<<
|
71
|
+
|
72
|
+
protected
|
73
|
+
|
74
|
+
# Initialize the object.
|
76
75
|
#
|
77
|
-
# @param [
|
78
|
-
#
|
79
|
-
|
76
|
+
# @param [Hash] opts the options to create the object with.
|
77
|
+
# @!visibility private
|
78
|
+
def ns_initialize(opts)
|
79
|
+
@queue = Collection::PriorityQueue.new(order: :min)
|
80
|
+
@task_executor = Executor.executor_from_options(opts) || Concurrent.global_io_executor
|
81
|
+
@timer_executor = SingleThreadExecutor.new
|
82
|
+
@condition = Event.new
|
83
|
+
self.auto_terminate = opts.fetch(:auto_terminate, true)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Post the task to the internal queue.
|
80
87
|
#
|
81
|
-
# @
|
88
|
+
# @note This is intended as a callback method from ScheduledTask
|
89
|
+
# only. It is not intended to be used directly. Post a task
|
90
|
+
# by using the `SchedulesTask#execute` method.
|
82
91
|
#
|
83
|
-
#
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
92
|
+
# @!visibility private
|
93
|
+
def post_task(task)
|
94
|
+
synchronize{ ns_post_task(task) }
|
95
|
+
end
|
96
|
+
|
97
|
+
# @!visibility private
|
98
|
+
def ns_post_task(task)
|
99
|
+
return false unless ns_running?
|
100
|
+
if (task.initial_delay) <= 0.01
|
101
|
+
task.executor.post{ task.process_task }
|
88
102
|
else
|
89
|
-
|
90
|
-
|
103
|
+
@queue.push(task)
|
104
|
+
# only post the process method when the queue is empty
|
105
|
+
@timer_executor.post(&method(:process_tasks)) if @queue.length == 1
|
106
|
+
@condition.set
|
91
107
|
end
|
108
|
+
true
|
92
109
|
end
|
93
110
|
|
94
|
-
|
95
|
-
|
96
|
-
#
|
97
|
-
# It
|
98
|
-
#
|
99
|
-
# times.
|
111
|
+
# Remove the given task from the queue.
|
112
|
+
#
|
113
|
+
# @note This is intended as a callback method from `ScheduledTask`
|
114
|
+
# only. It is not intended to be used directly. Cancel a task
|
115
|
+
# by using the `ScheduledTask#cancel` method.
|
100
116
|
#
|
101
117
|
# @!visibility private
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
def <=>(other)
|
106
|
-
self.time <=> other.time
|
107
|
-
end
|
118
|
+
def remove_task(task)
|
119
|
+
synchronize{ @queue.delete(task) }
|
108
120
|
end
|
109
121
|
|
110
|
-
|
111
|
-
|
122
|
+
# `ExecutorServic` callback called during shutdown.
|
123
|
+
#
|
112
124
|
# @!visibility private
|
113
125
|
def shutdown_execution
|
114
126
|
@queue.clear
|
@@ -124,11 +136,13 @@ module Concurrent
|
|
124
136
|
# @!visibility private
|
125
137
|
def process_tasks
|
126
138
|
loop do
|
127
|
-
task =
|
139
|
+
task = synchronize { @condition.reset; @queue.peek }
|
128
140
|
break unless task
|
129
|
-
interval = task.time - Time.now.to_f
|
130
141
|
|
131
|
-
|
142
|
+
now = Concurrent.monotonic_time
|
143
|
+
diff = task.schedule_time - now
|
144
|
+
|
145
|
+
if diff <= 0
|
132
146
|
# We need to remove the task from the queue before passing
|
133
147
|
# it to the executor, to avoid race conditions where we pass
|
134
148
|
# the peek'ed task to the executor and then pop a different
|
@@ -141,12 +155,10 @@ module Concurrent
|
|
141
155
|
# the only reader, so whatever timer is at the head of the
|
142
156
|
# queue now must have the same pop time, or a closer one, as
|
143
157
|
# when we peeked).
|
144
|
-
task =
|
145
|
-
|
158
|
+
task = synchronize { @queue.pop }
|
159
|
+
task.executor.post{ task.process_task }
|
146
160
|
else
|
147
|
-
|
148
|
-
@condition.wait(mutex, [interval, 60].min)
|
149
|
-
end
|
161
|
+
@condition.wait([diff, 60].min)
|
150
162
|
end
|
151
163
|
end
|
152
164
|
end
|