class Spring::ApplicationManager
Attributes
app_env[R]
child[R]
pid[R]
spring_env[R]
status[R]
Public Class Methods
new(app_env)
click to toggle source
# File lib/spring/application_manager.rb, line 5 def initialize(app_env) @app_env = app_env @spring_env = Env.new @mutex = Mutex.new @state = :running end
Public Instance Methods
alive?()
click to toggle source
# File lib/spring/application_manager.rb, line 34 def alive? @pid end
log(message)
click to toggle source
# File lib/spring/application_manager.rb, line 12 def log(message) spring_env.log "[application_manager:#{app_env}] #{message}" end
restart()
click to toggle source
# File lib/spring/application_manager.rb, line 29 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/spring/application_manager.rb, line 59 def run(client) with_child do 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/spring/application_manager.rb, line 25 def start start_child end
stop()
click to toggle source
# File lib/spring/application_manager.rb, line 78 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/spring/application_manager.rb, line 18 def synchronize @mutex.lock yield ensure @mutex.unlock end
with_child() { || ... }
click to toggle source
# File lib/spring/application_manager.rb, line 38 def with_child synchronize do if alive? begin yield 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 end else log "child not running; starting" start yield end end end
Private Instance Methods
start_child(preload = false)
click to toggle source
# File lib/spring/application_manager.rb, line 92 def start_child(preload = false) @child, child_socket = UNIXSocket.pair Bundler.with_clean_env do @pid = Process.spawn( { "RAILS_ENV" => app_env, "RACK_ENV" => app_env, "SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV), "SPRING_PRELOAD" => preload ? "1" : "0" }, "ruby", "-I", File.expand_path("../..", __FILE__), "-e", "require 'spring/application/boot'", 3 => child_socket ) end start_wait_thread(pid, child) if child.gets child_socket.close end
start_wait_thread(pid, child)
click to toggle source
# File lib/spring/application_manager.rb, line 114 def start_wait_thread(pid, child) Process.detach(pid) Thread.new { # 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 end } } end