class Expedite::ApplicationManager
Attributes
child[R]
env[R]
name[R]
pid[R]
status[R]
variant[R]
Public Class Methods
new(name, env)
click to toggle source
# File lib/expedite/application_manager.rb, line 14 def initialize(name, env) @name = name @env = env @mutex = Mutex.new @state = :running @pid = nil @variant = Expedite::Variants.lookup(@name) end
Public Instance Methods
alive?()
click to toggle source
# File lib/expedite/application_manager.rb, line 46 def alive? @pid end
keep_alive()
click to toggle source
# File lib/expedite/application_manager.rb, line 103 def keep_alive variant.keep_alive end
log(message)
click to toggle source
# File lib/expedite/application_manager.rb, line 24 def log(message) env.log "[application_manager:#{name}] #{message}" end
parent()
click to toggle source
# File lib/expedite/application_manager.rb, line 107 def parent variant.parent end
restart()
click to toggle source
# File lib/expedite/application_manager.rb, line 41 def restart return if @state == :stopping start_child(true) end
run(client)
click to toggle source
Returns the pid of the process running the command, or nil if the application process died.
# File lib/expedite/application_manager.rb, line 71 def run(client) @client = client with_child do |child| child.send_io client child.gets or raise Errno::EPIPE end pid = child.gets.to_i unless pid.zero? log "got worker pid #{pid}" pid end rescue Errno::ECONNRESET, Errno::EPIPE => e log "#{e} while reading from child; returning no pid" nil ensure client.close end
start()
click to toggle source
# File lib/expedite/application_manager.rb, line 37 def start start_child end
stop()
click to toggle source
# File lib/expedite/application_manager.rb, line 91 def stop log "stopping" @state = :stopping if pid Process.kill('TERM', pid) Process.wait(pid) end rescue Errno::ESRCH, Errno::ECHILD # Don't care end
synchronize() { || ... }
click to toggle source
We're not using @mutex.synchronize to avoid the weird “<internal:prelude>:10” line which messes with backtraces in e.g. rspec
# File lib/expedite/application_manager.rb, line 30 def synchronize @mutex.lock yield ensure @mutex.unlock end
with_child() { |child| ... }
click to toggle source
# File lib/expedite/application_manager.rb, line 50 def with_child synchronize do if alive? begin yield child rescue Errno::ECONNRESET, Errno::EPIPE # The child has died but has not been collected by the wait thread yet, # so start a new child and try again. log "child dead; starting" start yield child end else log "child not running; starting" start yield child end end end
Private Instance Methods
fork_child(preload = false)
click to toggle source
# File lib/expedite/application_manager.rb, line 121 def fork_child(preload = false) @child, child_socket = UNIXSocket.pair # Compose command wr, rd = UNIXSocket.pair wr.send_io STDOUT wr.send_io STDERR wr.send_io STDIN send_json wr, 'args' => ['expedite/boot', name], 'env' => {} wr.send_io child_socket wr.send_io env.log_file wr.close @pid = env.applications[parent].run(rd) start_wait_thread(pid, child) if child.gets child_socket.close end
spawn_child(preload = false)
click to toggle source
# File lib/expedite/application_manager.rb, line 141 def spawn_child(preload = false) @child, child_socket = UNIXSocket.pair Bundler.with_original_env do bundler_dir = File.expand_path("../..", $LOADED_FEATURES.grep(/bundler\/setup\.rb$/).first) @pid = Process.spawn( { "EXPEDITE_VARIANT" => name, "EXPEDITE_ROOT" => env.root, }, "ruby", *(bundler_dir != RbConfig::CONFIG["rubylibdir"] ? ["-I", bundler_dir] : []), "-I", File.expand_path("../..", __FILE__), "-e", "require 'expedite/application/boot'", 3 => child_socket, 4 => env.log_file, ) end start_wait_thread(pid, child) if child.gets child_socket.close end
start_child(preload = false)
click to toggle source
# File lib/expedite/application_manager.rb, line 113 def start_child(preload = false) if parent fork_child(preload) else spawn_child(preload) end end
start_wait_thread(pid, child)
click to toggle source
# File lib/expedite/application_manager.rb, line 164 def start_wait_thread(pid, child) Process.detach(pid) Expedite.failsafe_thread do # The recv can raise an ECONNRESET, killing the thread, but that's ok # as if it does we're no longer interested in the child loop do IO.select([child]) break if child.recv(1, Socket::MSG_PEEK).empty? sleep 0.01 end log "child #{pid} shutdown" synchronize { if @pid == pid @pid = nil restart if keep_alive end } end end