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