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
|
# intelligent fork based demonizer
|
||||||
class Demon::Base
|
class Demon::Base
|
||||||
|
|
||||||
|
def self.demons
|
||||||
|
@demons
|
||||||
|
end
|
||||||
|
|
||||||
def self.start(count=1)
|
def self.start(count=1)
|
||||||
@demons ||= {}
|
@demons ||= {}
|
||||||
count.times do |i|
|
count.times do |i|
|
||||||
|
@ -31,21 +35,57 @@ class Demon::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
attr_reader :pid, :parent_pid, :started, :index
|
||||||
|
attr_accessor :stop_timeout
|
||||||
|
|
||||||
def initialize(index)
|
def initialize(index)
|
||||||
@index = index
|
@index = index
|
||||||
@pid = nil
|
@pid = nil
|
||||||
@parent_pid = Process.pid
|
@parent_pid = Process.pid
|
||||||
@started = false
|
@started = false
|
||||||
|
@stop_timeout = 10
|
||||||
end
|
end
|
||||||
|
|
||||||
def pid_file
|
def pid_file
|
||||||
"#{Rails.root}/tmp/pids/#{self.class.prefix}_#{@index}.pid"
|
"#{Rails.root}/tmp/pids/#{self.class.prefix}_#{@index}.pid"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def alive?
|
||||||
|
if @pid
|
||||||
|
Demon::Base.alive?(@pid)
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def stop
|
def stop
|
||||||
@started = false
|
@started = false
|
||||||
if @pid
|
if @pid
|
||||||
|
# TODO configurable stop signal
|
||||||
Process.kill("HUP",@pid)
|
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
|
@pid = nil
|
||||||
@started = false
|
@started = false
|
||||||
end
|
end
|
||||||
|
@ -96,7 +136,7 @@ class Demon::Base
|
||||||
def already_running?
|
def already_running?
|
||||||
if File.exists? pid_file
|
if File.exists? pid_file
|
||||||
pid = File.read(pid_file).to_i
|
pid = File.read(pid_file).to_i
|
||||||
if alive?(pid)
|
if Demon::Base.alive?(pid)
|
||||||
return pid
|
return pid
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -104,6 +144,15 @@ class Demon::Base
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.alive?(pid)
|
||||||
|
begin
|
||||||
|
Process.kill(0, pid)
|
||||||
|
true
|
||||||
|
rescue
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def write_pid_file
|
def write_pid_file
|
||||||
|
@ -130,14 +179,6 @@ class Demon::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def alive?(pid)
|
|
||||||
begin
|
|
||||||
Process.kill(0, pid)
|
|
||||||
true
|
|
||||||
rescue
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def suppress_stdout
|
def suppress_stdout
|
||||||
true
|
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