concurrent-ruby 0.7.0.rc0-x64-mingw32 → 0.7.0.rc1-x64-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 +8 -8
- data/README.md +13 -8
- data/lib/2.0/concurrent_ruby_ext.so +0 -0
- data/lib/concurrent.rb +0 -1
- data/lib/concurrent/actress.rb +10 -6
- data/lib/concurrent/actress/core.rb +1 -1
- data/lib/concurrent/async.rb +39 -74
- data/lib/concurrent/atomic.rb +21 -1
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +1 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +22 -0
- data/lib/concurrent/atomic_reference/jruby.rb +2 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +36 -6
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +8 -7
- data/lib/concurrent/atomic_reference/rbx.rb +7 -4
- data/lib/concurrent/atomic_reference/ruby.rb +2 -0
- data/lib/concurrent/executor/executor.rb +118 -70
- data/lib/concurrent/executor/immediate_executor.rb +50 -1
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +14 -6
- data/lib/concurrent/executor/java_single_thread_executor.rb +1 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +3 -10
- data/lib/concurrent/executor/per_thread_executor.rb +80 -4
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +1 -1
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +4 -4
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +1 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +2 -0
- data/lib/concurrent/executor/serialized_execution.rb +23 -0
- data/lib/concurrent/version.rb +1 -1
- metadata +2 -4
- data/lib/concurrent/atomic_reference/delegated_update.rb +0 -28
- data/lib/concurrent/supervisor.rb +0 -343
@@ -17,14 +17,22 @@ if RUBY_PLATFORM == 'java'
|
|
17
17
|
#
|
18
18
|
# @see http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html#newFixedThreadPool-int-
|
19
19
|
def initialize(num_threads, opts = {})
|
20
|
-
@overflow_policy = opts.fetch(:overflow_policy, :abort)
|
21
|
-
@max_queue = 0
|
22
20
|
|
23
|
-
|
24
|
-
|
21
|
+
opts = {
|
22
|
+
min_threads: num_threads,
|
23
|
+
max_threads: num_threads
|
24
|
+
}.merge(opts)
|
25
|
+
super(opts)
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
|
28
|
+
#@overflow_policy = opts.fetch(:overflow_policy, :abort)
|
29
|
+
#@max_queue = 0
|
30
|
+
#
|
31
|
+
#raise ArgumentError.new('number of threads must be greater than zero') if num_threads < 1
|
32
|
+
#raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
|
33
|
+
#
|
34
|
+
#@executor = java.util.concurrent.Executors.newFixedThreadPool(num_threads)
|
35
|
+
#@executor.setRejectedExecutionHandler(OVERFLOW_POLICIES[@overflow_policy].new)
|
28
36
|
|
29
37
|
set_shutdown_hook
|
30
38
|
end
|
@@ -71,6 +71,7 @@ if RUBY_PLATFORM == 'java'
|
|
71
71
|
|
72
72
|
raise ArgumentError.new('max_threads must be greater than zero') if max_length <= 0
|
73
73
|
raise ArgumentError.new('min_threads cannot be less than zero') if min_length < 0
|
74
|
+
raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
|
74
75
|
raise ArgumentError.new("#{@overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.keys.include?(@overflow_policy)
|
75
76
|
|
76
77
|
if min_length == 0 && @max_queue == 0
|
@@ -89,6 +90,7 @@ if RUBY_PLATFORM == 'java'
|
|
89
90
|
set_shutdown_hook
|
90
91
|
end
|
91
92
|
|
93
|
+
# @!macro executor_module_method_can_overflow_question
|
92
94
|
def can_overflow?
|
93
95
|
@max_queue != 0
|
94
96
|
end
|
@@ -171,16 +173,7 @@ if RUBY_PLATFORM == 'java'
|
|
171
173
|
#
|
172
174
|
# @return [Boolean] `true` when running, `false` when shutting down or shutdown
|
173
175
|
def running?
|
174
|
-
super &&
|
175
|
-
end
|
176
|
-
|
177
|
-
# Begin an orderly shutdown. Tasks already in the queue will be executed,
|
178
|
-
# but no new tasks will be accepted. Has no additional effect if the
|
179
|
-
# thread pool is not running.
|
180
|
-
def shutdown
|
181
|
-
super
|
182
|
-
@executor.getQueue.clear
|
183
|
-
nil
|
176
|
+
super && !@executor.isTerminating
|
184
177
|
end
|
185
178
|
end
|
186
179
|
end
|
@@ -1,24 +1,100 @@
|
|
1
|
+
require 'concurrent/atomics'
|
2
|
+
require 'concurrent/executor/executor'
|
3
|
+
|
1
4
|
module Concurrent
|
2
5
|
|
6
|
+
# An executor service in which every operation spawns a new,
|
7
|
+
# independently operating thread.
|
8
|
+
#
|
9
|
+
# This is perhaps the most inefficient executor service in this
|
10
|
+
# library. It exists mainly for testing an debugging. Thread creation
|
11
|
+
# and management is expensive in Ruby and this executor performs no
|
12
|
+
# resource pooling. This can be very beneficial during testing and
|
13
|
+
# debugging because it decouples the using code from the underlying
|
14
|
+
# executor implementation. In production this executor will likely
|
15
|
+
# lead to suboptimal performance.
|
16
|
+
#
|
17
|
+
# @note Intended for use primarily in testing and debugging.
|
3
18
|
class PerThreadExecutor
|
4
19
|
include Executor
|
5
20
|
|
21
|
+
# Creates a new executor
|
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
|
6
29
|
def self.post(*args)
|
7
30
|
raise ArgumentError.new('no block given') unless block_given?
|
8
31
|
Thread.new(*args) do
|
9
32
|
Thread.current.abort_on_exception = false
|
10
33
|
yield(*args)
|
11
34
|
end
|
12
|
-
|
35
|
+
true
|
13
36
|
end
|
14
37
|
|
38
|
+
# @!macro executor_method_left_shift
|
39
|
+
def self.<<(task)
|
40
|
+
post(&task)
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# @!macro executor_method_post
|
15
45
|
def post(*args, &task)
|
16
|
-
|
46
|
+
raise ArgumentError.new('no block given') unless block_given?
|
47
|
+
return false unless running?
|
48
|
+
@count.increment
|
49
|
+
Thread.new(*args) do
|
50
|
+
Thread.current.abort_on_exception = false
|
51
|
+
begin
|
52
|
+
yield(*args)
|
53
|
+
ensure
|
54
|
+
@count.decrement
|
55
|
+
@stopped.set if @running.false? && @count.value == 0
|
56
|
+
end
|
57
|
+
end
|
17
58
|
end
|
18
59
|
|
60
|
+
# @!macro executor_method_left_shift
|
19
61
|
def <<(task)
|
20
|
-
|
21
|
-
|
62
|
+
post(&task)
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!macro executor_method_running_question
|
67
|
+
def running?
|
68
|
+
@running.true?
|
69
|
+
end
|
70
|
+
|
71
|
+
# @!macro executor_method_shuttingdown_question
|
72
|
+
def shuttingdown?
|
73
|
+
@running.false? && ! @stopped.set?
|
74
|
+
end
|
75
|
+
|
76
|
+
# @!macro executor_method_shutdown_question
|
77
|
+
def shutdown?
|
78
|
+
@stopped.set?
|
79
|
+
end
|
80
|
+
|
81
|
+
# @!macro executor_method_shutdown
|
82
|
+
def shutdown
|
83
|
+
@running.make_false
|
84
|
+
@stopped.set if @count.value == 0
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
# @!macro executor_method_kill
|
89
|
+
def kill
|
90
|
+
@running.make_false
|
91
|
+
@stopped.set
|
92
|
+
true
|
93
|
+
end
|
94
|
+
|
95
|
+
# @!macro executor_method_wait_for_termination
|
96
|
+
def wait_for_termination(timeout = nil)
|
97
|
+
@stopped.wait(timeout)
|
22
98
|
end
|
23
99
|
end
|
24
100
|
end
|
@@ -19,13 +19,13 @@ module Concurrent
|
|
19
19
|
raise ArgumentError.new('number of threads must be greater than zero') if num_threads < 1
|
20
20
|
raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(overflow_policy)
|
21
21
|
|
22
|
-
opts =
|
22
|
+
opts = {
|
23
23
|
min_threads: num_threads,
|
24
24
|
max_threads: num_threads,
|
25
|
-
|
25
|
+
overflow_policy: overflow_policy,
|
26
26
|
max_queue: DEFAULT_MAX_QUEUE_SIZE,
|
27
|
-
idletime:
|
28
|
-
)
|
27
|
+
idletime: DEFAULT_THREAD_IDLETIMEOUT,
|
28
|
+
}.merge(opts)
|
29
29
|
super(opts)
|
30
30
|
end
|
31
31
|
end
|
@@ -86,6 +86,7 @@ module Concurrent
|
|
86
86
|
raise ArgumentError.new('max_threads must be greater than zero') if @max_length <= 0
|
87
87
|
raise ArgumentError.new('min_threads cannot be less than zero') if @min_length < 0
|
88
88
|
raise ArgumentError.new("#{overflow_policy} is not a valid overflow policy") unless OVERFLOW_POLICIES.include?(@overflow_policy)
|
89
|
+
raise ArgumentError.new('min_threads cannot be more than max_threads') if min_length > max_length
|
89
90
|
|
90
91
|
init_executor
|
91
92
|
|
@@ -99,6 +100,7 @@ module Concurrent
|
|
99
100
|
@last_gc_time = Time.now.to_f - [1.0, (@gc_interval * 2.0)].max
|
100
101
|
end
|
101
102
|
|
103
|
+
# @!macro executor_module_method_can_overflow_question
|
102
104
|
def can_overflow?
|
103
105
|
@max_queue != 0
|
104
106
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'concurrent/executor/executor'
|
1
3
|
require 'concurrent/logging'
|
2
4
|
|
3
5
|
module Concurrent
|
@@ -85,6 +87,27 @@ module Concurrent
|
|
85
87
|
|
86
88
|
call_job job if job
|
87
89
|
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# A wrapper/delegator for any `Executor` or `ExecutorService` that
|
93
|
+
# guarantees serialized execution of tasks.
|
94
|
+
#
|
95
|
+
# @see [SimpleDelegator](http://www.ruby-doc.org/stdlib-2.1.2/libdoc/delegate/rdoc/SimpleDelegator.html)
|
96
|
+
# @see Concurrent::SerializedExecution
|
97
|
+
class SerializedExecutionDelegator < SimpleDelegator
|
98
|
+
include SerialExecutor
|
88
99
|
|
100
|
+
def initialize(executor)
|
101
|
+
@executor = executor
|
102
|
+
@serializer = SerializedExecution.new
|
103
|
+
super(executor)
|
104
|
+
end
|
105
|
+
|
106
|
+
# @!macro executor_method_post
|
107
|
+
def post(*args, &task)
|
108
|
+
raise ArgumentError.new('no block given') unless block_given?
|
109
|
+
return false unless running?
|
110
|
+
@serializer.post(@executor, *args, &task)
|
111
|
+
end
|
89
112
|
end
|
90
113
|
end
|
data/lib/concurrent/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: concurrent-ruby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.0.
|
4
|
+
version: 0.7.0.rc1
|
5
5
|
platform: x64-mingw32
|
6
6
|
authors:
|
7
7
|
- Jerry D'Antonio
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: ! " Modern concurrency tools including agents, futures, promises,
|
14
14
|
thread pools, actors, supervisors, and more.\n Inspired by Erlang, Clojure, Go,
|
@@ -50,7 +50,6 @@ files:
|
|
50
50
|
- lib/concurrent/atomic/event.rb
|
51
51
|
- lib/concurrent/atomic/thread_local_var.rb
|
52
52
|
- lib/concurrent/atomic_reference/concurrent_update_error.rb
|
53
|
-
- lib/concurrent/atomic_reference/delegated_update.rb
|
54
53
|
- lib/concurrent/atomic_reference/direct_update.rb
|
55
54
|
- lib/concurrent/atomic_reference/jruby.rb
|
56
55
|
- lib/concurrent/atomic_reference/mutex_atomic.rb
|
@@ -102,7 +101,6 @@ files:
|
|
102
101
|
- lib/concurrent/options_parser.rb
|
103
102
|
- lib/concurrent/promise.rb
|
104
103
|
- lib/concurrent/scheduled_task.rb
|
105
|
-
- lib/concurrent/supervisor.rb
|
106
104
|
- lib/concurrent/timer_task.rb
|
107
105
|
- lib/concurrent/tvar.rb
|
108
106
|
- lib/concurrent/utilities.rb
|
@@ -1,28 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic_reference/concurrent_update_error'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
|
5
|
-
# Define update methods that delegate to @ref field
|
6
|
-
class Atomic
|
7
|
-
# Pass the current value to the given block, replacing it
|
8
|
-
# with the block's result. May retry if the value changes
|
9
|
-
# during the block's execution.
|
10
|
-
def update
|
11
|
-
true until @ref.compare_and_set(old_value = @ref.get, new_value = yield(old_value))
|
12
|
-
new_value
|
13
|
-
end
|
14
|
-
|
15
|
-
def try_update
|
16
|
-
old_value = @ref.get
|
17
|
-
new_value = yield old_value
|
18
|
-
unless @ref.compare_and_set(old_value, new_value)
|
19
|
-
if $VERBOSE
|
20
|
-
raise ConcurrentUpdateError, "Update failed"
|
21
|
-
else
|
22
|
-
raise ConcurrentUpdateError, "Update failed", ConcurrentUpdateError::CONC_UP_ERR_BACKTRACE
|
23
|
-
end
|
24
|
-
end
|
25
|
-
new_value
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,343 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
require 'concurrent/errors'
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
|
7
|
-
class Supervisor
|
8
|
-
|
9
|
-
DEFAULT_MONITOR_INTERVAL = 1
|
10
|
-
RESTART_STRATEGIES = [:one_for_one, :one_for_all, :rest_for_one]
|
11
|
-
DEFAULT_MAX_RESTART = 5
|
12
|
-
DEFAULT_MAX_TIME = 60
|
13
|
-
WORKER_API = {run: 0, stop: 0, running?: 0}
|
14
|
-
|
15
|
-
CHILD_TYPES = [:worker, :supervisor]
|
16
|
-
CHILD_RESTART_OPTIONS = [:permanent, :transient, :temporary]
|
17
|
-
|
18
|
-
WorkerContext = Struct.new(:worker, :type, :restart) do
|
19
|
-
attr_accessor :thread
|
20
|
-
attr_accessor :terminated
|
21
|
-
|
22
|
-
def alive?() return thread && thread.alive?; end
|
23
|
-
|
24
|
-
def needs_restart?
|
25
|
-
return false if thread && thread.alive?
|
26
|
-
return false if terminated
|
27
|
-
case self.restart
|
28
|
-
when :permanent
|
29
|
-
return true
|
30
|
-
when :transient
|
31
|
-
return thread.nil? || thread.status.nil?
|
32
|
-
else #when :temporary
|
33
|
-
return false
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
WorkerCounts = Struct.new(:specs, :supervisors, :workers) do
|
39
|
-
attr_accessor :status
|
40
|
-
def add(context)
|
41
|
-
self.specs += 1
|
42
|
-
self.supervisors += 1 if context.type == :supervisor
|
43
|
-
self.workers += 1 if context.type == :worker
|
44
|
-
end
|
45
|
-
def active() sleeping + running + aborting end
|
46
|
-
def sleeping() @status.reduce(0){|x, s| x += (s == 'sleep' ? 1 : 0) } end
|
47
|
-
def running() @status.reduce(0){|x, s| x += (s == 'run' ? 1 : 0) } end
|
48
|
-
def aborting() @status.reduce(0){|x, s| x += (s == 'aborting' ? 1 : 0) } end
|
49
|
-
def stopped() @status.reduce(0){|x, s| x += (s == false ? 1 : 0) } end
|
50
|
-
def abend() @status.reduce(0){|x, s| x += (s.nil? ? 1 : 0) } end
|
51
|
-
end
|
52
|
-
|
53
|
-
attr_reader :monitor_interval
|
54
|
-
attr_reader :restart_strategy
|
55
|
-
attr_reader :max_restart
|
56
|
-
attr_reader :max_time
|
57
|
-
|
58
|
-
alias_method :strategy, :restart_strategy
|
59
|
-
alias_method :max_r, :max_restart
|
60
|
-
alias_method :max_t, :max_time
|
61
|
-
|
62
|
-
def initialize(opts = {})
|
63
|
-
warn '[EXPERIMENTAL] Supervisor is being completely rewritten and will change soon.'
|
64
|
-
@restart_strategy = opts[:restart_strategy] || opts[:strategy] || :one_for_one
|
65
|
-
@monitor_interval = (opts[:monitor_interval] || DEFAULT_MONITOR_INTERVAL).to_f
|
66
|
-
@max_restart = (opts[:max_restart] || opts[:max_r] || DEFAULT_MAX_RESTART).to_i
|
67
|
-
@max_time = (opts[:max_time] || opts[:max_t] || DEFAULT_MAX_TIME).to_i
|
68
|
-
|
69
|
-
raise ArgumentError.new(":#{@restart_strategy} is not a valid restart strategy") unless RESTART_STRATEGIES.include?(@restart_strategy)
|
70
|
-
raise ArgumentError.new(':monitor_interval must be greater than zero') unless @monitor_interval > 0.0
|
71
|
-
raise ArgumentError.new(':max_restart must be greater than zero') unless @max_restart > 0
|
72
|
-
raise ArgumentError.new(':max_time must be greater than zero') unless @max_time > 0
|
73
|
-
|
74
|
-
@running = false
|
75
|
-
@mutex = Mutex.new
|
76
|
-
@workers = []
|
77
|
-
@monitor = nil
|
78
|
-
|
79
|
-
@count = WorkerCounts.new(0, 0, 0)
|
80
|
-
@restart_times = []
|
81
|
-
|
82
|
-
add_worker(opts[:worker]) unless opts[:worker].nil?
|
83
|
-
add_workers(opts[:workers]) unless opts[:workers].nil?
|
84
|
-
end
|
85
|
-
|
86
|
-
def run!
|
87
|
-
@mutex.synchronize do
|
88
|
-
raise StandardError.new('already running') if @running
|
89
|
-
@running = true
|
90
|
-
@monitor = Thread.new do
|
91
|
-
Thread.current.abort_on_exception = false
|
92
|
-
monitor
|
93
|
-
end
|
94
|
-
end
|
95
|
-
Thread.pass
|
96
|
-
end
|
97
|
-
|
98
|
-
def run
|
99
|
-
@mutex.synchronize do
|
100
|
-
raise StandardError.new('already running') if @running
|
101
|
-
@running = true
|
102
|
-
end
|
103
|
-
monitor
|
104
|
-
true
|
105
|
-
end
|
106
|
-
|
107
|
-
def stop
|
108
|
-
@mutex.synchronize do
|
109
|
-
return true unless @running
|
110
|
-
|
111
|
-
@running = false
|
112
|
-
unless @monitor.nil?
|
113
|
-
@monitor.run if @monitor.status == 'sleep'
|
114
|
-
if @monitor.join(0.1).nil?
|
115
|
-
@monitor.kill
|
116
|
-
end
|
117
|
-
@monitor = nil
|
118
|
-
end
|
119
|
-
@restart_times.clear
|
120
|
-
|
121
|
-
@workers.length.times do |i|
|
122
|
-
context = @workers[-1-i]
|
123
|
-
terminate_worker(context)
|
124
|
-
end
|
125
|
-
prune_workers
|
126
|
-
end
|
127
|
-
|
128
|
-
true
|
129
|
-
end
|
130
|
-
|
131
|
-
def running?
|
132
|
-
@mutex.synchronize { @running }
|
133
|
-
end
|
134
|
-
|
135
|
-
def length
|
136
|
-
@mutex.synchronize { @workers.length }
|
137
|
-
end
|
138
|
-
alias_method :size, :length
|
139
|
-
|
140
|
-
def current_restart_count
|
141
|
-
@restart_times.length
|
142
|
-
end
|
143
|
-
|
144
|
-
def count
|
145
|
-
@mutex.synchronize do
|
146
|
-
@count.status = @workers.collect{|w| w.thread ? w.thread.status : false }
|
147
|
-
@count.dup.freeze
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
def add_worker(worker, opts = {})
|
152
|
-
return nil if worker.nil? || ! behaves_as_worker?(worker)
|
153
|
-
@mutex.synchronize {
|
154
|
-
restart = opts[:restart] || :permanent
|
155
|
-
type = opts[:type] || (worker.is_a?(Supervisor) ? :supervisor : nil) || :worker
|
156
|
-
raise ArgumentError.new(":#{restart} is not a valid restart option") unless CHILD_RESTART_OPTIONS.include?(restart)
|
157
|
-
raise ArgumentError.new(":#{type} is not a valid child type") unless CHILD_TYPES.include?(type)
|
158
|
-
context = WorkerContext.new(worker, type, restart)
|
159
|
-
@workers << context
|
160
|
-
@count.add(context)
|
161
|
-
worker.run if @running
|
162
|
-
context.object_id
|
163
|
-
}
|
164
|
-
end
|
165
|
-
alias_method :add_child, :add_worker
|
166
|
-
|
167
|
-
def add_workers(workers, opts = {})
|
168
|
-
workers.collect do |worker|
|
169
|
-
add_worker(worker, opts)
|
170
|
-
end
|
171
|
-
end
|
172
|
-
alias_method :add_children, :add_workers
|
173
|
-
|
174
|
-
def remove_worker(worker_id)
|
175
|
-
@mutex.synchronize do
|
176
|
-
index, context = find_worker(worker_id)
|
177
|
-
break(nil) if context.nil?
|
178
|
-
break(false) if context.alive?
|
179
|
-
@workers.delete_at(index)
|
180
|
-
context.worker
|
181
|
-
end
|
182
|
-
end
|
183
|
-
alias_method :remove_child, :remove_worker
|
184
|
-
|
185
|
-
def stop_worker(worker_id)
|
186
|
-
@mutex.synchronize do
|
187
|
-
return true unless @running
|
188
|
-
|
189
|
-
index, context = find_worker(worker_id)
|
190
|
-
break(nil) if index.nil?
|
191
|
-
context.terminated = true
|
192
|
-
terminate_worker(context)
|
193
|
-
@workers.delete_at(index) if @workers[index].restart == :temporary
|
194
|
-
true
|
195
|
-
end
|
196
|
-
end
|
197
|
-
alias_method :stop_child, :stop_worker
|
198
|
-
|
199
|
-
def start_worker(worker_id)
|
200
|
-
@mutex.synchronize do
|
201
|
-
return false unless @running
|
202
|
-
|
203
|
-
index, context = find_worker(worker_id)
|
204
|
-
break(nil) if context.nil?
|
205
|
-
context.terminated = false
|
206
|
-
run_worker(context) unless context.alive?
|
207
|
-
true
|
208
|
-
end
|
209
|
-
end
|
210
|
-
alias_method :start_child, :start_worker
|
211
|
-
|
212
|
-
def restart_worker(worker_id)
|
213
|
-
@mutex.synchronize do
|
214
|
-
return false unless @running
|
215
|
-
|
216
|
-
index, context = find_worker(worker_id)
|
217
|
-
break(nil) if context.nil?
|
218
|
-
break(false) if context.restart == :temporary
|
219
|
-
context.terminated = false
|
220
|
-
terminate_worker(context)
|
221
|
-
run_worker(context)
|
222
|
-
true
|
223
|
-
end
|
224
|
-
end
|
225
|
-
alias_method :restart_child, :restart_worker
|
226
|
-
|
227
|
-
private
|
228
|
-
|
229
|
-
def behaves_as_worker?(obj)
|
230
|
-
WORKER_API.each do |method, arity|
|
231
|
-
break(false) unless obj.respond_to?(method) && obj.method(method).arity == arity
|
232
|
-
true
|
233
|
-
end
|
234
|
-
end
|
235
|
-
|
236
|
-
def monitor
|
237
|
-
@workers.each{|context| run_worker(context)}
|
238
|
-
loop do
|
239
|
-
sleep(@monitor_interval)
|
240
|
-
break unless running?
|
241
|
-
@mutex.synchronize do
|
242
|
-
prune_workers
|
243
|
-
self.send(@restart_strategy)
|
244
|
-
end
|
245
|
-
break unless running?
|
246
|
-
end
|
247
|
-
rescue MaxRestartFrequencyError => ex
|
248
|
-
stop
|
249
|
-
end
|
250
|
-
|
251
|
-
def run_worker(context)
|
252
|
-
context.thread = Thread.new do
|
253
|
-
Thread.current.abort_on_exception = false
|
254
|
-
context.worker.run
|
255
|
-
end
|
256
|
-
context
|
257
|
-
end
|
258
|
-
|
259
|
-
def terminate_worker(context)
|
260
|
-
if context.alive?
|
261
|
-
context.worker.stop
|
262
|
-
Thread.pass
|
263
|
-
end
|
264
|
-
rescue Exception => ex
|
265
|
-
begin
|
266
|
-
Thread.kill(context.thread)
|
267
|
-
rescue
|
268
|
-
# suppress
|
269
|
-
end
|
270
|
-
ensure
|
271
|
-
context.thread = nil
|
272
|
-
end
|
273
|
-
|
274
|
-
def prune_workers
|
275
|
-
@workers.delete_if{|w| w.restart == :temporary && ! w.alive? }
|
276
|
-
end
|
277
|
-
|
278
|
-
def find_worker(worker_id)
|
279
|
-
index = @workers.find_index{|worker| worker.object_id == worker_id}
|
280
|
-
if index.nil?
|
281
|
-
[nil, nil]
|
282
|
-
else
|
283
|
-
[index, @workers[index]]
|
284
|
-
end
|
285
|
-
end
|
286
|
-
|
287
|
-
def exceeded_max_restart_frequency?
|
288
|
-
@restart_times.unshift(Time.now.to_i)
|
289
|
-
diff = (@restart_times.first - @restart_times.last).abs
|
290
|
-
if @restart_times.length >= @max_restart && diff <= @max_time
|
291
|
-
return true
|
292
|
-
elsif diff >= @max_time
|
293
|
-
@restart_times.pop
|
294
|
-
end
|
295
|
-
false
|
296
|
-
end
|
297
|
-
|
298
|
-
#----------------------------------------------------------------
|
299
|
-
# restart strategies
|
300
|
-
|
301
|
-
def one_for_one
|
302
|
-
@workers.each do |context|
|
303
|
-
if context.needs_restart?
|
304
|
-
raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
|
305
|
-
run_worker(context)
|
306
|
-
end
|
307
|
-
end
|
308
|
-
end
|
309
|
-
|
310
|
-
def one_for_all
|
311
|
-
restart = false
|
312
|
-
|
313
|
-
restart = @workers.each do |context|
|
314
|
-
if context.needs_restart?
|
315
|
-
raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
|
316
|
-
break(true)
|
317
|
-
end
|
318
|
-
end
|
319
|
-
|
320
|
-
if restart
|
321
|
-
@workers.each do |context|
|
322
|
-
terminate_worker(context)
|
323
|
-
end
|
324
|
-
@workers.each{|context| run_worker(context)}
|
325
|
-
end
|
326
|
-
end
|
327
|
-
|
328
|
-
def rest_for_one
|
329
|
-
restart = false
|
330
|
-
|
331
|
-
@workers.each do |context|
|
332
|
-
if restart
|
333
|
-
terminate_worker(context)
|
334
|
-
elsif context.needs_restart?
|
335
|
-
raise MaxRestartFrequencyError if exceeded_max_restart_frequency?
|
336
|
-
restart = true
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
one_for_one if restart
|
341
|
-
end
|
342
|
-
end
|
343
|
-
end
|