class Rack::App::Worker::Daemonizer

Constants

DEFAULT_KILL_SIGNAL

Public Class Methods

new(daemon_name) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 6
def initialize(daemon_name)
  @daemon_name = daemon_name.to_s
  @on_shutdown, @on_halt, @on_reload = proc {}, proc {}, proc {}
end

Public Instance Methods

bind(to_pid) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 63
def bind(to_pid)
  Thread.new do
    sleep(1) while Rack::App::Worker::Utils.process_alive?(to_pid)

    at_shutdown
  end
end
daemonize() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 30
def daemonize
  case try_fork

    when NilClass #child
      subscribe_to_signals
      save_current_process_pid
      redirect

    else #parent
      Kernel.exit

  end
end
has_running_process?() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 44
def has_running_process?
  pids.any? { |pid| Rack::App::Worker::Utils.process_alive?(pid) }
end
id() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 11
def id
  @id ||= SecureRandom.uuid
end
on_halt(&block) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 76
def on_halt(&block)
  raise('block not given!') unless block.is_a?(Proc)
  @on_halt = block
end
on_reload(&block) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 81
def on_reload(&block)
  raise('block not given!') unless block.is_a?(Proc)
  @on_reload = block
end
on_shutdown(&block) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 71
def on_shutdown(&block)
  raise('block not given!') unless block.is_a?(Proc)
  @on_shutdown = block
end
process_title(new_title) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 48
def process_title(new_title)
  if Process.respond_to?(:setproctitle)
    Process.setproctitle(new_title)
  else

    $0 = new_title
  end
end
send_signal(signal, to_amount_of_worker=pids.length) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 57
def send_signal(signal, to_amount_of_worker=pids.length)
  pids.take(to_amount_of_worker).each do |pid|
    kill(signal, pid)
  end
end
spawn(&block) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 15
def spawn(&block)

  parent_pid = current_pid
  spawn_block = proc do
    subscribe_to_signals
    bind(parent_pid)
    save_current_process_pid
    redirect
    block.call(self)
  end

  try_fork(&spawn_block)

end
subscribe_to_signals() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 86
def subscribe_to_signals
  ::Signal.trap('INT'){ at_shutdown }
  ::Signal.trap('HUP'){ at_shutdown }
  ::Signal.trap('TERM'){ at_halt }
  ::Signal.trap('USR1'){ at_reload }
end

Protected Instance Methods

at_halt() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 117
def at_halt
  Timeout.timeout(10) { @on_halt.call } rescue nil
ensure
  at_stop
end
at_reload() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 123
def at_reload
  @on_reload.call
end
at_shutdown() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 111
def at_shutdown
  @on_shutdown.call
ensure
  at_stop
end
at_stop() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 127
def at_stop
  File.write('/Users/aluzsi/Works/rack-app/worker/sandbox/out', pid_file_path)
  File.delete(pid_file_path) if File.exist?(pid_file_path)
  ::Kernel.exit
end
current_pid() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 180
def current_pid
  Process.pid rescue $$
end
kill(signal, pid) click to toggle source

Try and read the existing pid from the pid file and signal the process. Returns true for a non blocking status.

# File lib/rack/app/worker/daemonizer.rb, line 97
def kill(signal, pid)
  ::Process.kill(signal, pid)
  true
rescue Errno::ESRCH
  $stdout.puts "The process #{pid} did not exist: Errno::ESRCH"
  true
rescue Errno::EPERM
  $stderr.puts "Lack of privileges to manage the process #{pid}: Errno::EPERM"
  false
rescue ::Exception => e
  $stderr.puts "While signaling the PID, unexpected #{e.class}: #{e}"
  false
end
pid_file_path() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 170
def pid_file_path
  File.join(pids_folder_path, id)
end
pids() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 165
def pids
  sorted_pid_files = Dir.glob(File.join(pids_folder_path, '*')).sort_by { |fp| File.mtime(fp) }
  sorted_pid_files.map { |file_path| File.read(file_path).to_i }
end
pids_folder_path() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 174
def pids_folder_path
  path = Rack::App::Utils.pwd('pids', 'workers', @daemon_name)
  FileUtils.mkdir_p(path)
  path
end
redirect() click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 149
def redirect
  Timeout.timeout(5) { try_redirect }
rescue Timeout::Error
  raise('Cannot redirect standard io channels!')
end
save_current_process_pid() click to toggle source

Attempts to write the pid of the forked process to the pid file.

# File lib/rack/app/worker/daemonizer.rb, line 142
def save_current_process_pid
  File.write(pid_file_path, current_pid)
rescue ::Exception => e
  $stderr.puts "While writing the PID to file, unexpected #{e.class}: #{e}"
  Kernel.exit
end
try_fork(&block) click to toggle source
# File lib/rack/app/worker/daemonizer.rb, line 133
def try_fork(&block)
  pid = nil
  Timeout.timeout(15) { (Kernel.sleep(1) while (pid = ::Kernel.fork(&block)) == -1) }
  return pid
rescue Timeout::Error
  raise('Fork failed!')
end
try_redirect() click to toggle source

Send stdout and stderr to log files for the child process

# File lib/rack/app/worker/daemonizer.rb, line 156
def try_redirect
  $stdin.reopen(Rack::App::Utils.devnull_path)
  $stdout.reopen(Rack::App::Worker::Environment.stdout)
  $stderr.reopen(Rack::App::Worker::Environment.stderr)
  $stdout.sync = $stderr.sync = true
rescue Errno::ENOENT
  retry
end