= Handling of SIGUSR1 When you request a 'graceful' restart of Apache, mod_fastcgi sends a SIGUSR1 to each of the fastcgi worker processes. The intention is that each one should finish the current request, and then exit, at which point Apache will restart it. Of course, if the worker isn't doing anything, it should die immediately. This is implemented in the fcgi C library as follows: - a signal handler is installed for SIGUSR1, which just sets a flag (shutdownPending) and returns - fcgi sits inside an accept() call waiting for a new request - if this accept() call terminates with EINTR, and this flag is set, then it returns with no request Unfortunately, Ruby defeats this mechanism in at least two ways: 1. Ruby installs its own signal handlers for a host of common signals, including USR1. The fcgi library will not install its own handler if it detects that a handler has already been set (i.e. the current handler is not SIG_DFL) 2. When Ruby installs its own signal handlers, it does so with SA_RESTART set. This means that the accept() call does not terminate with EINTR; it is restarted automatically by the OS. When a signal comes in during the accept(), Ruby's own handler does nothing except store it in a queue to be processed later. It is only when the accept() call completes, i.e. when a genuine new request comes in, that Ruby takes action. Unfortunately it's too late by then, and if that already-accepted request is not honoured, a 500 Internal Error will be returned to the client. The simplest solution to this would be to remove Ruby's SIGUSR1 handler before initialising the FastCGI library. However, a cleaner solution is to call rb_thread_select before going into FastCGI's accept loop. If a signal happens during the select, it can be handled using Ruby's normal mechanisms. This also gives a very useful side-benefit, which is that FCGI::accept no longer blocks out other Ruby threads. The program below demonstrates this problem; its background logging thread is supposed to write a message every 10 seconds, but under older versions of ruby-fcgi it does not do so if it is waiting for a new request. #!/usr/local/bin/ruby require "fcgi" Thread.new do f = File.new("/tmp/fcgi.log","a") f.sync=true while true f.puts "#{Time.now.to_s} pid #{$$}" sleep 10 end end FCGI.each_cgi {|cgi| name = cgi['name'][0] puts cgi.header puts "Hey! You are #{name} " if name puts "Connecting from #{cgi.remote_addr}" } Having protected the accept() with a ruby select(), you can then handle signals as follows: - call FCGI::accept (it will raise an exception if USR1 is called) - install a USR1 handler - process the request - remove the USR1 handler The USR1 handler should set a flag to note if a USR1 signal came in while the request was being processed; you terminate the loop if it was set. The overall effect is that USR1 will cause the process to terminate, but without causing a half-completed request.