angael 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/angael/manager.rb +44 -30
- data/lib/angael/process_helper.rb +37 -0
- data/lib/angael/version.rb +1 -1
- data/lib/angael/worker.rb +41 -50
- data/spec/lib/angael/manager_spec.rb +41 -30
- data/spec/lib/angael/process_helper_spec.rb +72 -0
- data/spec/lib/angael/worker_spec.rb +60 -16
- metadata +4 -2
data/lib/angael/manager.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
|
+
require 'angael/process_helper'
|
1
2
|
module Angael
|
2
3
|
# A Manager has a number of of worker objects. Starting the Manager simply
|
3
4
|
# calls #start! on each worker, then it goes into an infinite loop, waiting
|
4
5
|
# for SIGINT or SIGTERM. When either of those is received, the manager will
|
5
|
-
# call #
|
6
|
+
# call #stop_with_wait on each Worker.
|
6
7
|
class Manager
|
8
|
+
include ProcessHelper
|
7
9
|
attr_reader :workers
|
8
10
|
|
9
11
|
# Creates a new manager.
|
10
12
|
#
|
11
13
|
# @worker_class [Class] The class to use for the workers. Must respond
|
12
|
-
# to #new, #start!, and #
|
14
|
+
# to #new, #start!, and #stop_with_wait
|
13
15
|
# @worker_count [Integer] The number of workers to manager. Default is 1.
|
14
16
|
# @worker_args [Array] An array of arguments that will be passed to
|
15
17
|
# worker_class.new. The arguments will be splatted
|
@@ -33,6 +35,8 @@ module Angael
|
|
33
35
|
@workers = []
|
34
36
|
worker_count.times { workers << worker_class.new(*worker_args) }
|
35
37
|
@restart_after = opts[:restart_after]
|
38
|
+
# TODO: Add a spec for this
|
39
|
+
raise ArgumentError, ':restart_after must be either an Integer greater than zero or nil' if @restart_after && (@restart_after.to_i != @restart_after || @restart_after == 0)
|
36
40
|
@logger = opts[:logger]
|
37
41
|
if @logger
|
38
42
|
@log_level = opts[:log_level] || begin
|
@@ -44,13 +48,13 @@ module Angael
|
|
44
48
|
|
45
49
|
|
46
50
|
# Starts workers by calling Worker#start! Loops forever waiting for SIGINT
|
47
|
-
# or SIGTERM, at which time it calls Worker#
|
51
|
+
# or SIGTERM, at which time it calls Worker#stop_with_wait on each worker.
|
48
52
|
def start!
|
49
53
|
workers.each { |w| w.start! }
|
50
54
|
|
51
55
|
trap("CHLD") do
|
52
56
|
workers.each do |w|
|
53
|
-
result =
|
57
|
+
result = exit_status(w.pid)
|
54
58
|
#print w.pid.to_s
|
55
59
|
#print "\t"
|
56
60
|
#p result
|
@@ -62,23 +66,40 @@ module Angael
|
|
62
66
|
end
|
63
67
|
end
|
64
68
|
trap("INT") do
|
65
|
-
|
69
|
+
log("SIGINT Received")
|
70
|
+
@interrupted = true
|
66
71
|
end
|
67
72
|
trap("TERM") do
|
68
|
-
|
73
|
+
log("SIGTERM Received")
|
74
|
+
@interrupted = true
|
69
75
|
end
|
70
76
|
|
71
77
|
if @restart_after
|
78
|
+
restart_after_counter = @restart_after
|
72
79
|
loop do
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
80
|
+
stop! if @interrupted
|
81
|
+
|
82
|
+
# This ensures we are checking @interrupted every 1 second, but we
|
83
|
+
# can set @restart_after to something greater than zero.
|
84
|
+
if restart_after_counter > 0
|
85
|
+
sleep 1
|
86
|
+
restart_after_counter -= 1
|
87
|
+
else
|
88
|
+
# Periodically restart workers, 1 at a time.
|
89
|
+
log("Sleeping for #@restart_after seconds")
|
90
|
+
w = next_worker_to_restart
|
91
|
+
log("Time to restart a worker: Calling #stop_with_wait for worker #{w.inspect}")
|
92
|
+
w.stop_with_wait
|
93
|
+
log("Worker has been stopped: #{w.inspect}")
|
94
|
+
w.start!
|
95
|
+
log("Worker has been restarted: #{w.inspect}")
|
96
|
+
w = nil
|
97
|
+
restart_after_counter = @restart_after
|
98
|
+
end
|
78
99
|
end
|
79
100
|
else
|
80
101
|
loop do
|
81
|
-
|
102
|
+
stop! if @interrupted
|
82
103
|
sleep 1
|
83
104
|
end
|
84
105
|
end
|
@@ -91,11 +112,18 @@ module Angael
|
|
91
112
|
#########
|
92
113
|
|
93
114
|
def stop!
|
94
|
-
log("
|
115
|
+
log("Attempting to gracefully stopping worker manager")
|
116
|
+
# Tell each worker to stop, without waiting to see if it worked.
|
117
|
+
workers.each { |w|
|
118
|
+
log("Calling #stop_without_wait for worker #{w.inspect}")
|
119
|
+
w.stop_without_wait
|
120
|
+
log("Finished call to #stop_without_wait for worker #{w.inspect}")
|
121
|
+
}
|
122
|
+
# Wait for each worker to stop, one at a time.
|
95
123
|
workers.each { |w|
|
96
|
-
log("Calling #
|
97
|
-
w.
|
98
|
-
log("Finished call to #
|
124
|
+
log("Calling #stop_with_wait for worker #{w.inspect}")
|
125
|
+
w.stop_with_wait
|
126
|
+
log("Finished call to #stop_with_wait for worker #{w.inspect}")
|
99
127
|
}
|
100
128
|
exit 0
|
101
129
|
end
|
@@ -104,20 +132,6 @@ module Angael
|
|
104
132
|
@logger.add(@log_level, "#{Time.now.utc} - #{self.class} (pid #{$$}): #{msg}") if @logger
|
105
133
|
end
|
106
134
|
|
107
|
-
# Returns immediately. If the process is still running, it returns nil.
|
108
|
-
# If the process is a zombie, it returns an array with the pid as the
|
109
|
-
# first element and a Process::Status object as the 2nd element, i.e.
|
110
|
-
# it returns the same thing as Process.wait2. If the process does not
|
111
|
-
# exist (i.e. it is completely gone) then it returns an array with the
|
112
|
-
# pid as the first element and nil as the 2nd element (because there
|
113
|
-
# is no Process::Status object to return).
|
114
|
-
def wait(pid)
|
115
|
-
begin
|
116
|
-
Process.wait2(pid, Process::WNOHANG)
|
117
|
-
rescue Errno::ECHILD
|
118
|
-
[pid, nil] # It did exit, but we don't know the exit status.
|
119
|
-
end
|
120
|
-
end
|
121
135
|
|
122
136
|
def next_worker_to_restart
|
123
137
|
@worker_count ||= workers.size
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Angael
|
2
|
+
module ProcessHelper
|
3
|
+
# Returns immediately. If the process is still running, it returns nil.
|
4
|
+
# If the process is a zombie, it returns an array with the pid as the
|
5
|
+
# first element and a Process::Status object as the 2nd element, i.e.
|
6
|
+
# it returns the same thing as Process.wait2. If the process does not
|
7
|
+
# exist (i.e. it is completely gone) or it is not accessible to the
|
8
|
+
# current process (i.e. an Errno::EPERM error is raised), then this method
|
9
|
+
# returns an array with the pid as the first element and nil as the 2nd
|
10
|
+
# element (because there is no Process::Status object to return).
|
11
|
+
# If the argument is nil, then it returns [nil, nil] because there can't be
|
12
|
+
# a process running with the pid nil.
|
13
|
+
def exit_status(pid)
|
14
|
+
return [nil, nil] if pid.nil?
|
15
|
+
|
16
|
+
# Sometimes wait2 returns nil even when the process has exited. This
|
17
|
+
# raises an Errno::ESRCH error in that case.
|
18
|
+
raise "Unexpected return value from Process.kill(0, pid)" unless Process.kill(0, pid) == 1
|
19
|
+
|
20
|
+
Process.wait2(pid, Process::WNOHANG)
|
21
|
+
rescue Errno::ECHILD, Errno::ESRCH, Errno::EPERM
|
22
|
+
# There is no longer any record of this PID.
|
23
|
+
# It did exit, but we don't know the exit status.
|
24
|
+
[pid, nil]
|
25
|
+
end
|
26
|
+
|
27
|
+
|
28
|
+
# Will return nil instead of raising Errno::ESRCH when the process does
|
29
|
+
# not exists.
|
30
|
+
# TODO: Add explicit tests for this.
|
31
|
+
def send_signal(signal, pid)
|
32
|
+
Process.kill(signal, pid)
|
33
|
+
rescue Errno::ESRCH
|
34
|
+
nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/angael/version.rb
CHANGED
data/lib/angael/worker.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'angael/process_helper'
|
2
2
|
module Angael
|
3
3
|
# Usage
|
4
4
|
# include Angael::Worker
|
@@ -17,6 +17,7 @@ module Angael
|
|
17
17
|
# it is sent SIGINT. If you don't define this method, it waits
|
18
18
|
# 60 seconds.
|
19
19
|
module Worker
|
20
|
+
include ProcessHelper
|
20
21
|
class ChildProcessNotStoppedError < StandardError; end
|
21
22
|
|
22
23
|
attr_reader :pid
|
@@ -64,9 +65,13 @@ module Angael
|
|
64
65
|
start!
|
65
66
|
end
|
66
67
|
|
67
|
-
|
68
|
+
# Returns true if SIGINT was sent to the child process, even if the child
|
69
|
+
# process does not exists.
|
70
|
+
# Returns false if started? is true.
|
71
|
+
# Sets stopping? to false.
|
72
|
+
def stop_without_wait
|
68
73
|
unless started?
|
69
|
-
__log("
|
74
|
+
__log("Tried to stop worker with PID #{pid} but it is not started")
|
70
75
|
return false
|
71
76
|
end
|
72
77
|
|
@@ -74,28 +79,38 @@ module Angael
|
|
74
79
|
# stopped the child process.
|
75
80
|
@stopping = true
|
76
81
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
82
|
+
__log("Sending SIGINT to child process with pid #{pid}.")
|
83
|
+
send_signal('INT', pid)
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
# Keeps sending SIGINT until the child process exits. If #timeout seconds
|
88
|
+
# pass, then it sends 1 SIGKILL. If that also fails, it raises ChildProcessNotStoppedError.
|
89
|
+
def stop_with_wait
|
90
|
+
return false unless stop_without_wait
|
91
|
+
|
92
|
+
__log("Waiting for child process with pid #{pid} to stop.")
|
93
|
+
|
94
|
+
counter = 0
|
95
|
+
|
96
|
+
while pid_running? && counter < timeout
|
97
|
+
sleep 1
|
98
|
+
counter += 1
|
99
|
+
__log("Sending SIGINT to child process with pid #{pid}. Attempt Count: #{counter}.")
|
100
|
+
send_signal('INT', pid)
|
101
|
+
end
|
102
|
+
|
103
|
+
if pid_running?
|
104
|
+
__log("Child process with pid #{pid} did not stop within #{timeout} seconds of SIGINT. Sending SIGKILL to child process.")
|
105
|
+
send_signal('KILL', pid)
|
106
|
+
sleep 1
|
107
|
+
end
|
108
|
+
|
109
|
+
if pid_running?
|
110
|
+
# SIGKILL didn't work.
|
111
|
+
msg = "Unable to kill child process with PID: #{pid}"
|
112
|
+
__log(msg)
|
113
|
+
raise ChildProcessNotStoppedError, msg
|
99
114
|
end
|
100
115
|
end
|
101
116
|
|
@@ -132,34 +147,10 @@ module Angael
|
|
132
147
|
end
|
133
148
|
|
134
149
|
|
135
|
-
# Note: if the pid is running, but this process doesn't have permissions to
|
136
|
-
# access it, then this will return false.
|
137
150
|
def pid_running?
|
138
|
-
|
139
|
-
Process.kill(0, pid) == 1
|
140
|
-
rescue Errno::ESRCH, Errno::EPERM
|
141
|
-
false
|
142
|
-
end
|
151
|
+
!exit_status(pid)
|
143
152
|
end
|
144
153
|
|
145
|
-
# Will just return if the child process is not running.
|
146
|
-
def wait_for_child(opts={})
|
147
|
-
begin
|
148
|
-
__log("Waiting for child with pid #{pid}.")
|
149
|
-
if opts[:dont_block]
|
150
|
-
# When this is called as the result of a SIGCHLD
|
151
|
-
# we need to pass in Process::WNOHANG as the 2nd argument, otherwise when
|
152
|
-
# there are multiple workers, some workers will trap SIGCHLD when other
|
153
|
-
# workers' child processes die. Without this argument, those workers will
|
154
|
-
# hang forever, which also hangs the worker manager.
|
155
|
-
Process.wait(pid, Process::WNOHANG)
|
156
|
-
else
|
157
|
-
Process.wait(pid)
|
158
|
-
end
|
159
|
-
rescue Errno::ECHILD
|
160
|
-
# The child process has already been reaped.
|
161
|
-
end
|
162
|
-
end
|
163
154
|
|
164
155
|
# This is the standard/default way of doing it. Overwrite this if you want
|
165
156
|
# to wrap it in an exception handler, for example.
|
@@ -40,40 +40,33 @@ describe Angael::Manager do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
context "when :restart_after is set to 0.5" do
|
43
|
-
|
43
|
+
after do
|
44
|
+
clean_up_pid(@pid)
|
45
|
+
end
|
46
|
+
|
47
|
+
subject { Angael::Manager.new(Angael::TestSupport::SampleWorker, 3, [], :restart_after => 1) }
|
48
|
+
|
44
49
|
it "should restart workers 1 at a time, at 1 second intervals" do
|
45
50
|
subject.workers.each do |w|
|
46
|
-
|
51
|
+
# This isn't used for restarts.
|
52
|
+
w.stub(:stop_without_wait)
|
47
53
|
end
|
48
54
|
|
49
|
-
subject.workers[0].
|
50
|
-
subject.workers[1].
|
51
|
-
subject.workers[2].
|
52
|
-
|
53
|
-
subject.workers[0].
|
54
|
-
subject.workers[1].
|
55
|
-
subject.workers[2].
|
56
|
-
|
57
|
-
|
58
|
-
# As an alternative to should_receive_in_child_process, we
|
59
|
-
# fork a process which will send SIGINT to this current process.
|
60
|
-
# Then we start the Manager in this process and wait for it to
|
61
|
-
# get the SIGINT. Finally we rescue SystemExit so that this
|
62
|
-
# process doesn't exit with the Manager stops.
|
63
|
-
# TODO: Be consistent in my use of this technique vs. should_receive_in_child_process.
|
64
|
-
current_pid = $$
|
65
|
-
pid = Process.fork do
|
66
|
-
sleep 0.6 # Add a 0.1 second buffer to the value of :restart_after to give the process a chance to start.
|
67
|
-
Process.kill('INT', current_pid)
|
68
|
-
exit 0
|
69
|
-
end
|
70
|
-
begin
|
55
|
+
subject.workers[0].should_receive_in_child_process(:stop_with_wait).exactly(1).times
|
56
|
+
subject.workers[1].should_receive_in_child_process(:stop_with_wait).exactly(2).times # This is the worker that got restarted.
|
57
|
+
subject.workers[2].should_receive_in_child_process(:stop_with_wait).exactly(1).times
|
58
|
+
|
59
|
+
subject.workers[0].should_receive_in_child_process(:start!).exactly(1).times
|
60
|
+
subject.workers[1].should_receive_in_child_process(:start!).exactly(2).times # This is the worker that got restarted.
|
61
|
+
subject.workers[2].should_receive_in_child_process(:start!).exactly(1).times
|
62
|
+
|
63
|
+
@pid = Process.fork do
|
71
64
|
subject.start!
|
72
|
-
rescue SystemExit
|
73
|
-
nil
|
74
65
|
end
|
75
66
|
|
76
|
-
|
67
|
+
sleep 1.1 # Add a 0.1 second buffer to the value of :restart_after to give the process a chance to start.
|
68
|
+
Process.kill('INT', @pid)
|
69
|
+
|
77
70
|
end
|
78
71
|
end
|
79
72
|
|
@@ -94,7 +87,7 @@ describe Angael::Manager do
|
|
94
87
|
end
|
95
88
|
|
96
89
|
sleep 0.1 # Give the process a chance to start.
|
97
|
-
# This sends
|
90
|
+
# This sends stop_with_wait to all the workers.
|
98
91
|
Process.kill('INT', @pid)
|
99
92
|
sleep 0.1 # Give the TempFile a chance to flush
|
100
93
|
end
|
@@ -157,11 +150,29 @@ describe Angael::Manager do
|
|
157
150
|
|
158
151
|
%w(INT TERM).each do |sig|
|
159
152
|
context "when it receives a SIG#{sig}" do
|
160
|
-
it "should call #
|
153
|
+
it "should call #stop_without_wait on each Worker" do
|
154
|
+
subject.workers.each do |w|
|
155
|
+
w.stub(:start!) # We don't care about the sub-process, so don't start it.
|
156
|
+
|
157
|
+
w.should_receive_in_child_process(:stop_without_wait).at_least(1).times
|
158
|
+
end
|
159
|
+
|
160
|
+
pid = Process.fork do
|
161
|
+
subject.start!
|
162
|
+
end
|
163
|
+
sleep 0.1 # Give the process a chance to start.
|
164
|
+
Process.kill(sig, pid)
|
165
|
+
sleep 0.1 # Give the TempFile a chance to flush
|
166
|
+
|
167
|
+
# Clean up
|
168
|
+
Process.wait(pid)
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should call #stop_with_wait on each Worker" do
|
161
172
|
subject.workers.each do |w|
|
162
173
|
w.stub(:start!) # We don't care about the sub-process, so don't start it.
|
163
174
|
|
164
|
-
w.should_receive_in_child_process(:
|
175
|
+
w.should_receive_in_child_process(:stop_with_wait)
|
165
176
|
end
|
166
177
|
|
167
178
|
pid = Process.fork do
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Angael::ProcessHelper do
|
4
|
+
include Angael::ProcessHelper
|
5
|
+
|
6
|
+
describe "exit_status" do
|
7
|
+
context "when passed the argument nil" do
|
8
|
+
it "should return an array of [nil, nil]" do
|
9
|
+
exit_status(nil).should == [nil, nil]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context "when Process.kill raises Errno::ESRCH" do
|
14
|
+
before { Process.stub(:kill) { raise Errno::ESRCH } }
|
15
|
+
it "should return an array of [pid, nil]" do
|
16
|
+
exit_status(123456).should == [123456, nil]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when Process.kill raises Errno::EPERM" do
|
21
|
+
before { Process.stub(:kill) { raise Errno::EPERM } }
|
22
|
+
it "should return an array of [pid, nil]" do
|
23
|
+
exit_status(123456).should == [123456, nil]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "when Process.kill return 1" do
|
28
|
+
before { Process.stub(:kill).and_return(1) }
|
29
|
+
|
30
|
+
context "when Process.wait2 raises Errno::ECHILD" do
|
31
|
+
before { Process.stub(:wait2) { raise Errno::ECHILD } }
|
32
|
+
it "should return an array of [pid, nil]" do
|
33
|
+
exit_status(123456).should == [123456, nil]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when Process.wait2 returns nil" do
|
38
|
+
before { Process.stub(:wait2).and_return(nil) }
|
39
|
+
it "should return nil" do
|
40
|
+
exit_status(123456).should be_nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when Process.wait2 returns an array of [pid, exitstatus]" do
|
45
|
+
before do
|
46
|
+
@exitstatus = mock(Process::Status)
|
47
|
+
Process.stub(:wait2).and_return([123456, @exitstatus])
|
48
|
+
end
|
49
|
+
it "should return an array of [pid, exitstatus]" do
|
50
|
+
exit_status(123456).should == [123456, @exitstatus]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
describe "send_signal" do
|
58
|
+
it "should call kill" do
|
59
|
+
pid = 123456
|
60
|
+
sig = 'INT'
|
61
|
+
Process.should_receive(:kill).with(sig, pid)
|
62
|
+
send_signal(sig, pid)
|
63
|
+
end
|
64
|
+
|
65
|
+
context "when Process.kill raises Errno::ESRCH" do
|
66
|
+
it "should return nil" do
|
67
|
+
Process.stub(:kill) { raise Errno::ESRCH }
|
68
|
+
send_signal('INT', 123456).should be_nil
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -7,7 +7,7 @@ describe Angael::Worker do
|
|
7
7
|
|
8
8
|
describe "#start!" do
|
9
9
|
before { subject.stub(:work => nil) }
|
10
|
-
after { subject.
|
10
|
+
after { subject.stop_with_wait if subject.started? }
|
11
11
|
|
12
12
|
it "should set #pid" do
|
13
13
|
subject.pid.should be_nil
|
@@ -85,24 +85,68 @@ describe Angael::Worker do
|
|
85
85
|
|
86
86
|
|
87
87
|
|
88
|
-
describe "#
|
88
|
+
describe "#stop_without_wait" do
|
89
89
|
before { subject.stub(:work => nil) }
|
90
|
-
after { subject.
|
90
|
+
after { subject.stop_with_wait }
|
91
91
|
|
92
92
|
context "when stopped" do
|
93
93
|
it "should return false" do
|
94
94
|
subject.should_not be_started
|
95
|
-
subject.
|
95
|
+
subject.stop_without_wait.should be_false
|
96
96
|
end
|
97
97
|
|
98
98
|
it "should not be stopping" do
|
99
|
-
subject.
|
99
|
+
subject.stop_without_wait
|
100
100
|
subject.should_not be_stopping
|
101
101
|
end
|
102
102
|
|
103
103
|
it "should not send a SIGINT to the child process" do
|
104
104
|
should_not_receive_and_run(Process, :kill, 'INT', subject.pid)
|
105
|
-
subject.
|
105
|
+
subject.stop_without_wait
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return false" do
|
109
|
+
subject.stop_without_wait.should be_false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "when started" do
|
114
|
+
before { subject.start! }
|
115
|
+
it "should send a SIGINT to the child process" do
|
116
|
+
should_receive_and_run(Process, :kill, 'INT', subject.pid)
|
117
|
+
subject.stop_without_wait
|
118
|
+
end
|
119
|
+
|
120
|
+
it "should be stopping" do
|
121
|
+
subject.stop_without_wait
|
122
|
+
subject.should be_stopping
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should return true" do
|
126
|
+
subject.stop_without_wait.should be_true
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
|
132
|
+
describe "#stop_with_wait" do
|
133
|
+
before { subject.stub(:work => nil) }
|
134
|
+
after { subject.stop_with_wait }
|
135
|
+
|
136
|
+
context "when stopped" do
|
137
|
+
it "should return false" do
|
138
|
+
subject.should_not be_started
|
139
|
+
subject.stop_with_wait.should be_false
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should not be stopping" do
|
143
|
+
subject.stop_with_wait
|
144
|
+
subject.should_not be_stopping
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should not send a SIGINT to the child process" do
|
148
|
+
should_not_receive_and_run(Process, :kill, 'INT', subject.pid)
|
149
|
+
subject.stop_with_wait
|
106
150
|
end
|
107
151
|
end
|
108
152
|
|
@@ -110,12 +154,12 @@ describe Angael::Worker do
|
|
110
154
|
it "should send a SIGINT to the child process" do
|
111
155
|
subject.start!
|
112
156
|
should_receive_and_run(Process, :kill, 'INT', subject.pid)
|
113
|
-
subject.
|
157
|
+
subject.stop_with_wait
|
114
158
|
end
|
115
159
|
|
116
160
|
it "should be stopping" do
|
117
161
|
subject.start!
|
118
|
-
subject.
|
162
|
+
subject.stop_with_wait
|
119
163
|
subject.should be_stopping
|
120
164
|
end
|
121
165
|
|
@@ -130,23 +174,23 @@ describe Angael::Worker do
|
|
130
174
|
subject.start!
|
131
175
|
end
|
132
176
|
it "should be stopped" do
|
133
|
-
subject.
|
177
|
+
subject.stop_with_wait
|
134
178
|
subject.should be_stopped
|
135
179
|
end
|
136
180
|
|
137
181
|
it "should not have a child process with the pid #pid" do
|
138
|
-
subject.
|
182
|
+
subject.stop_with_wait
|
139
183
|
pid_running?(subject.pid).should be_false
|
140
184
|
end
|
141
185
|
|
142
186
|
it "should not send a SIGKILL to the child process" do
|
143
187
|
should_not_receive_and_run(Process, :kill, 'KILL', subject.pid)
|
144
|
-
subject.
|
188
|
+
subject.stop_with_wait
|
145
189
|
end
|
146
190
|
|
147
191
|
it "should have the (now dead) child process' PID as #pid" do
|
148
192
|
pid = subject.pid
|
149
|
-
subject.
|
193
|
+
subject.stop_with_wait
|
150
194
|
subject.pid.should == pid
|
151
195
|
end
|
152
196
|
end
|
@@ -164,18 +208,18 @@ describe Angael::Worker do
|
|
164
208
|
end
|
165
209
|
|
166
210
|
it "should be stopped" do
|
167
|
-
subject.
|
211
|
+
subject.stop_with_wait
|
168
212
|
subject.should be_stopped
|
169
213
|
end
|
170
214
|
|
171
215
|
it "should not have a child process with the pid #pid" do
|
172
|
-
subject.
|
216
|
+
subject.stop_with_wait
|
173
217
|
pid_running?(subject.pid).should be_false
|
174
218
|
end
|
175
219
|
|
176
220
|
it "should send a SIGKILL to the child process" do
|
177
221
|
should_receive_and_run(Process, :kill, 'KILL', subject.pid)
|
178
|
-
subject.
|
222
|
+
subject.stop_with_wait
|
179
223
|
end
|
180
224
|
|
181
225
|
context "child process does not die after receiving SIGKILL" do
|
@@ -204,7 +248,7 @@ describe Angael::Worker do
|
|
204
248
|
it "should raise an error with the child process' pid in the message" do
|
205
249
|
pid_running?(subject.pid).should be_true
|
206
250
|
lambda do
|
207
|
-
subject.
|
251
|
+
subject.stop_with_wait
|
208
252
|
end.should raise_error(Angael::Worker::ChildProcessNotStoppedError, /#{subject.pid}/)
|
209
253
|
|
210
254
|
# Confirm the PID is still running
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: angael
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.8
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Paul Cortens
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-20 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -51,9 +51,11 @@ files:
|
|
51
51
|
- angael.gemspec
|
52
52
|
- lib/angael.rb
|
53
53
|
- lib/angael/manager.rb
|
54
|
+
- lib/angael/process_helper.rb
|
54
55
|
- lib/angael/version.rb
|
55
56
|
- lib/angael/worker.rb
|
56
57
|
- spec/lib/angael/manager_spec.rb
|
58
|
+
- spec/lib/angael/process_helper_spec.rb
|
57
59
|
- spec/lib/angael/worker_spec.rb
|
58
60
|
- spec/spec_helper.rb
|
59
61
|
- spec/support/io_helpers.rb
|