diff options
Diffstat (limited to 'lib/unicorn/worker.rb')
-rw-r--r-- | lib/unicorn/worker.rb | 64 |
1 files changed, 64 insertions, 0 deletions
diff --git a/lib/unicorn/worker.rb b/lib/unicorn/worker.rb index 1fb6a4a..e74a1c9 100644 --- a/lib/unicorn/worker.rb +++ b/lib/unicorn/worker.rb @@ -12,6 +12,7 @@ class Unicorn::Worker # :stopdoc: attr_accessor :nr, :switched attr_writer :tmp + attr_reader :to_io # IO.select-compatible PER_DROP = Raindrops::PAGE_SIZE / Raindrops::SIZE DROPS = [] @@ -23,6 +24,66 @@ class Unicorn::Worker @raindrop[@offset] = 0 @nr = nr @tmp = @switched = false + @to_io, @master = Unicorn.pipe + end + + def atfork_child # :nodoc: + # we _must_ close in child, parent just holds this open to signal + @master = @master.close + end + + # master fakes SIGQUIT using this + def quit # :nodoc: + @master = @master.close if @master + end + + # parent does not read + def atfork_parent # :nodoc: + @to_io = @to_io.close + end + + # call a signal handler immediately without triggering EINTR + # We do not use the more obvious Process.kill(sig, $$) here since + # that signal delivery may be deferred. We want to avoid signal delivery + # while the Rack app.call is running because some database drivers + # (e.g. ruby-pg) may cancel pending requests. + def fake_sig(sig) # :nodoc: + old_cb = trap(sig, "IGNORE") + old_cb.call + ensure + trap(sig, old_cb) + end + + # master sends fake signals to children + def soft_kill(sig) # :nodoc: + case sig + when Integer + signum = sig + else + signum = Signal.list[sig.to_s] or + raise ArgumentError, "BUG: bad signal: #{sig.inspect}" + end + # writing and reading 4 bytes on a pipe is atomic on all POSIX platforms + # Do not care in the odd case the buffer is full, here. + @master.kgio_trywrite([signum].pack('l')) + rescue Errno::EPIPE + # worker will be reaped soon + end + + # this only runs when the Rack app.call is not running + # act like a listener + def kgio_tryaccept # :nodoc: + case buf = @to_io.kgio_tryread(4) + when String + # unpack the buffer and trigger the signal handler + signum = buf.unpack('l') + fake_sig(signum[0]) + # keep looping, more signals may be queued + when nil # EOF: master died, but we are at a safe place to exit + fake_sig(:QUIT) + when :wait_readable # keep waiting + return false + end while true # loop, as multiple signals may be sent end # worker objects may be compared to just plain Integers @@ -49,8 +110,11 @@ class Unicorn::Worker end end + # called in both the master (reaping worker) and worker (SIGQUIT handler) def close # :nodoc: @tmp.close if @tmp + @master.close if @master + @to_io.close if @to_io end # :startdoc: |