lib/foreman/engine.rb in foreman-0.78.0 vs lib/foreman/engine.rb in foreman-0.80.0
- old
+ new
@@ -1,11 +1,10 @@
require "foreman"
require "foreman/env"
require "foreman/process"
require "foreman/procfile"
require "tempfile"
-require "timeout"
require "fileutils"
require "thread"
class Foreman::Engine
@@ -35,10 +34,11 @@
@mutex = Mutex.new
@names = {}
@processes = []
@running = {}
@readers = {}
+ @shutdown = false
# Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
reader, writer = create_pipe
reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
@@ -55,12 +55,13 @@
register_signal_handlers
startup
spawn_processes
watch_for_output
sleep 0.1
- watch_for_termination { terminate_gracefully }
+ wait_for_shutdown_or_child_termination
shutdown
+ exit(@exitstatus) if @exitstatus
end
# Set up deferred signal handlers
#
def register_signal_handlers
@@ -83,11 +84,11 @@
#
def notice_signal
@selfpipe[:writer].write_nonblock( '.' )
rescue Errno::EAGAIN
# Ignore writes that would block
- rescue Errno::EINT
+ rescue Errno::EINTR
# Retry if another signal arrived while writing
retry
end
# Invoke the real handler for signal +sig+. This shouldn't be called directly
@@ -109,26 +110,26 @@
end
# Handle a TERM signal
#
def handle_term_signal
- puts "SIGTERM received"
- terminate_gracefully
+ system "SIGTERM received, starting shutdown"
+ @shutdown = true
end
# Handle an INT signal
#
def handle_interrupt
- puts "SIGINT received"
- terminate_gracefully
+ system "SIGINT received, starting shutdown"
+ @shutdown = true
end
# Handle a HUP signal
#
def handle_hangup
- puts "SIGHUP received"
- terminate_gracefully
+ system "SIGHUP received, starting shutdown"
+ @shutdown = true
end
# Register a process to be run by this +Engine+
#
# @param [String] name A name for this process
@@ -369,11 +370,11 @@
end
end
def read_self_pipe
@selfpipe[:reader].read_nonblock(11)
- rescue Errno::EAGAIN, Errno::EINTR, Errno::EBADF
+ rescue Errno::EAGAIN, Errno::EINTR, Errno::EBADF, Errno::EWOULDBLOCK
# ignore
end
def handle_signals
while sig = Thread.main[:signal_queue].shift
@@ -408,34 +409,77 @@
puts ex.backtrace
end
end
end
- def watch_for_termination
- pid, status = Process.wait2
+ def wait_for_shutdown_or_child_termination
+ loop do
+ # Stop if it is time to shut down (asked via a signal)
+ break if @shutdown
+
+ # Stop if any of the children died
+ break if check_for_termination
+
+ # Sleep for a moment and do not blow up if any signals are coming our way
+ begin
+ sleep(1)
+ rescue Exception
+ # noop
+ end
+ end
+
+ # Ok, we have exited from the main loop, time to shut down gracefully
+ terminate_gracefully
+ end
+
+ def check_for_termination
+ # Check if any of the children have died off
+ pid, status = begin
+ Process.wait2(-1, Process::WNOHANG)
+ rescue Errno::ECHILD
+ return nil
+ end
+
+ @exitstatus ||= status.exitstatus
+
+ # If no childred have died, nothing to do here
+ return nil unless pid
+
+ # Log the information about the process that exited
output_with_mutex name_for(pid), termination_message_for(status)
+
+ # Delete it from the list of running processes and return its pid
@running.delete(pid)
- yield if block_given?
- pid
- rescue Errno::ECHILD
+ return pid
end
def terminate_gracefully
- return if @terminating
restore_default_signal_handlers
- @terminating = true
+
+ # Tell all children to stop gracefully
if Foreman.windows?
system "sending SIGKILL to all processes"
kill_children "SIGKILL"
else
system "sending SIGTERM to all processes"
kill_children "SIGTERM"
end
- Timeout.timeout(options[:timeout]) do
- watch_for_termination while @running.length > 0
+
+ # Wait for all children to stop or until the time comes to kill them all
+ start_time = Time.now
+ while Time.now - start_time <= options[:timeout]
+ return if @running.empty?
+ check_for_termination
+
+ # Sleep for a moment and do not blow up if more signals are coming our way
+ begin
+ sleep(0.1)
+ rescue Exception
+ # noop
+ end
end
- rescue Timeout::Error
+
+ # Ok, we have no other option than to kill all of our children
system "sending SIGKILL to all processes"
kill_children "SIGKILL"
end
-
end