FIX: ensure child demon is correctly terminated from parent on stop
This commit is contained in:
parent
69ad0358c2
commit
861cd5d9b0
|
@ -3,6 +3,10 @@ module Demon; end
|
|||
# intelligent fork based demonizer
|
||||
class Demon::Base
|
||||
|
||||
def self.demons
|
||||
@demons
|
||||
end
|
||||
|
||||
def self.start(count=1)
|
||||
@demons ||= {}
|
||||
count.times do |i|
|
||||
|
@ -31,21 +35,57 @@ class Demon::Base
|
|||
end
|
||||
end
|
||||
|
||||
attr_reader :pid, :parent_pid, :started, :index
|
||||
attr_accessor :stop_timeout
|
||||
|
||||
def initialize(index)
|
||||
@index = index
|
||||
@pid = nil
|
||||
@parent_pid = Process.pid
|
||||
@started = false
|
||||
@stop_timeout = 10
|
||||
end
|
||||
|
||||
def pid_file
|
||||
"#{Rails.root}/tmp/pids/#{self.class.prefix}_#{@index}.pid"
|
||||
end
|
||||
|
||||
def alive?
|
||||
if @pid
|
||||
Demon::Base.alive?(@pid)
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def stop
|
||||
@started = false
|
||||
if @pid
|
||||
# TODO configurable stop signal
|
||||
Process.kill("HUP",@pid)
|
||||
|
||||
wait_for_stop = lambda {
|
||||
timeout = @stop_timeout
|
||||
|
||||
while alive? && timeout > 0
|
||||
timeout -= (@stop_timeout/10.0)
|
||||
sleep(@stop_timeout/10.0)
|
||||
Process.waitpid(@pid, Process::WNOHANG) rescue -1
|
||||
end
|
||||
|
||||
Process.waitpid(@pid, Process::WNOHANG) rescue -1
|
||||
}
|
||||
|
||||
wait_for_stop.call
|
||||
|
||||
if alive?
|
||||
STDERR.puts "Process would not terminate cleanly, force quitting. pid: #{@pid}"
|
||||
Process.kill("KILL", @pid)
|
||||
end
|
||||
|
||||
wait_for_stop.call
|
||||
|
||||
|
||||
@pid = nil
|
||||
@started = false
|
||||
end
|
||||
|
@ -96,7 +136,7 @@ class Demon::Base
|
|||
def already_running?
|
||||
if File.exists? pid_file
|
||||
pid = File.read(pid_file).to_i
|
||||
if alive?(pid)
|
||||
if Demon::Base.alive?(pid)
|
||||
return pid
|
||||
end
|
||||
end
|
||||
|
@ -104,6 +144,15 @@ class Demon::Base
|
|||
nil
|
||||
end
|
||||
|
||||
def self.alive?(pid)
|
||||
begin
|
||||
Process.kill(0, pid)
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def write_pid_file
|
||||
|
@ -130,14 +179,6 @@ class Demon::Base
|
|||
end
|
||||
end
|
||||
|
||||
def alive?(pid)
|
||||
begin
|
||||
Process.kill(0, pid)
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def suppress_stdout
|
||||
true
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
require 'spec_helper'
|
||||
require 'demon/base'
|
||||
|
||||
describe Demon do
|
||||
|
||||
class RudeDemon < Demon::Base
|
||||
def self.prefix
|
||||
"rude"
|
||||
end
|
||||
|
||||
def after_fork
|
||||
Signal.trap("HUP"){}
|
||||
Signal.trap("TERM"){}
|
||||
sleep 999999
|
||||
end
|
||||
end
|
||||
|
||||
it "can terminate rude demons" do
|
||||
|
||||
skip("forking rspec has side effects")
|
||||
# Forking rspec has all sorts of weird side effects
|
||||
# this spec works but we must skip it to keep rspec
|
||||
# state happy
|
||||
|
||||
|
||||
RudeDemon.start
|
||||
_,demon = RudeDemon.demons.first
|
||||
pid = demon.pid
|
||||
wait_for {
|
||||
demon.alive?
|
||||
}
|
||||
|
||||
demon.stop_timeout = 0.05
|
||||
demon.stop
|
||||
demon.start
|
||||
|
||||
running = !!(Process.kill(0, pid)) rescue false
|
||||
expect(running).to eq(false)
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue