Skip to content

Instantly share code, notes, and snippets.

@roman
Created January 28, 2010 23:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save roman/289285 to your computer and use it in GitHub Desktop.
Save roman/289285 to your computer and use it in GitHub Desktop.
class WorkerForker
ReloadWorkerMessage = Class.new(StandardError)
class Worker
def initialize(ppid, stream)
@ppid = ppid
@pid = Process.pid
@stream = stream
Kernel.trap('ABRT', @pid) { reap_worker }
Kernel.trap('HUP', @pid) { reload! }
end
def reload!
reap_worker(128)
exit
end
protected
def stream
@stream
end
def reap_worker(status = 0)
begin
if stream
stream.puts(status.to_s)
stream.close
end
rescue SystemCallError
end
end
end
def self.run_in_worker(&block)
start_transaction(&block)
end
def self.start_transaction(&block)
fork_process do |(process, pid, stream)|
case process
when :parent
# pid is the child pid
handle_int_signal(pid)
handle_hup_signal(pid)
wait_for_worker_exit(pid, stream)
when :worker
# pid is the parent pid
worker = nil
begin
worker = Worker.new(pid, stream)
block.call
rescue ReloadWorkerMessage
worker.reload!
end
end
end
end
def self.reload!
raise ReloadWorkerMessage
end
def self.fork_process
reader = writer = nil
ppid = nil
pid = nil
loop do
reader, writer = IO.pipe
ppid = Process.pid
pid = Process.fork
break if pid.nil?
writer.close
yield [:parent, pid, reader]
end
if pid.nil?
reader.close
yield [:worker, ppid, writer]
end
end
def self.handle_int_signal(pid)
Kernel.trap("INT") do
puts " Reaping Workers"
begin
Process.kill("KILL", pid)
rescue SystemCallError
end
exit_gracefully
end
end
def self.handle_hup_signal(wpid)
Kernel.trap('HUP') { Process.kill('HUP', pid) }
end
def self.wait_for_worker_exit(wpid, reader)
reader_ary = [reader]
writer_ary = nil
error_ary = nil
timeout = 0.5
loop do
begin
if result = Process.wait2(wpid, Process::WNOHANG)
(pid, exit_status) = result
exit_status && exit_status.exitstatus == 128 ? break : exit
end
rescue SystemCallError
exit
end
if Kernel.select(reader_ary, writer_ary, error_ary, timeout)
begin
next if reader.eof?
msg = reader.readline
reader.close
if msg.to_i == 128
Process.waitpid(wpid, Process::WNOHANG)
break
else
exit_gracefully
end
rescue SystemCallError
exit_gracefully
end
end
end
end
def self.exit_gracefully
Process.waitall
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment