class Forkr
Attributes
child_count[R]
The number of children I should maintain This can be adjusted up or down with the TTIN and TTOU signals. @return [Integer]
children[R]
Child process pids. @return [Array<Fixnum>]
inbound[R]
master_pid[R]
The PID of the Forkr
master @return [Fixnum]
outbound[R]
Public Class Methods
new(forklet, num_kids = 1)
click to toggle source
@param forklet [Object] the worker object @param num_kids [Integer] how many children to spawn
# File lib/forkr.rb, line 18 def initialize(forklet, num_kids = 1) @worker_client = forklet @master_pid = $$ @children = [] @child_count = num_kids @in_shutdown = false end
Public Instance Methods
run()
click to toggle source
Start the master, and spawn workers @return [nil]
# File lib/forkr.rb, line 28 def run @inbound, @outbound = IO.pipe Signal.trap('CHLD') { dead_child } Signal.trap('INT') { interrupt } Signal.trap('TERM') { shutdown } Signal.trap('QUIT') { core_dump_quit } Signal.trap('TTIN') { add_worker } Signal.trap('TTOU') { remove_worker } master_loop end
Protected Instance Methods
add_worker()
click to toggle source
# File lib/forkr.rb, line 53 def add_worker send_wake_notice("+") end
child_dead?(pid)
click to toggle source
# File lib/forkr.rb, line 171 def child_dead?(pid) status = Process.waitpid(pid, Process::WNOHANG) unless status.nil? $stderr.puts "Process #{pid} dead: #{status}" end !status.nil? end
core_dump_quit()
click to toggle source
# File lib/forkr.rb, line 49 def core_dump_quit send_wake_notice("Q") end
dead_child()
click to toggle source
# File lib/forkr.rb, line 69 def dead_child send_wake_notice("D") end
decrement_workers()
click to toggle source
# File lib/forkr.rb, line 85 def decrement_workers if @child_count > 1 @child_count = @child_count - 1 end end
ensure_right_worker_count()
click to toggle source
# File lib/forkr.rb, line 133 def ensure_right_worker_count existing_workers = @children.length off_by = @child_count - @children.length if off_by > 0 off_by.times do spawn_worker end elsif off_by < 0 @children.take(off_by.abs).each do |kid| signal_worker(kid, :TERM) end end end
increment_workers()
click to toggle source
# File lib/forkr.rb, line 81 def increment_workers @child_count = @child_count + 1 end
interrupt()
click to toggle source
# File lib/forkr.rb, line 61 def interrupt send_wake_notice("I") end
master_loop()
click to toggle source
# File lib/forkr.rb, line 97 def master_loop catch(:bail_because_im_a_worker) do ensure_right_worker_count loop do fds = IO.select([@inbound],nil,nil,2) unless fds.nil? data_read = fds.first.first.read(1) if data_read == "I" shutdown_using(:INT) elsif data_read == "T" shutdown_using(:TERM) elsif data_read == "Q" shutdown_using(:QUIT) elsif data_read == "+" increment_workers elsif data_read == "-" decrement_workers end end prune_workers ensure_right_worker_count end reap_all_workers @outbound.close @inbound.close end end
prune_workers()
click to toggle source
# File lib/forkr.rb, line 158 def prune_workers @children = @children.reject { |pid| child_dead?(pid) } end
reap_all_workers()
click to toggle source
# File lib/forkr.rb, line 125 def reap_all_workers begin wpid, status = Process.waitpid2(-1, Process::WNOHANG) rescue Errno::ECHILD break end while true end
remove_worker()
click to toggle source
# File lib/forkr.rb, line 57 def remove_worker send_wake_notice("-") end
send_wake_notice(notice)
click to toggle source
# File lib/forkr.rb, line 43 def send_wake_notice(notice) return(nil) if $$ != master_pid return(nil) if @in_shutdown @outbound.write(notice) end
shutdown()
click to toggle source
# File lib/forkr.rb, line 65 def shutdown send_wake_notice("T") end
shutdown_using(sig)
click to toggle source
# File lib/forkr.rb, line 91 def shutdown_using(sig) @in_shutdown = true signal_all_workers(sig) raise StopIteration.new end
signal_all_workers(sig)
click to toggle source
# File lib/forkr.rb, line 147 def signal_all_workers(sig) @children.each { |c| signal_worker(c, sig) } end
signal_worker(wpid, signal)
click to toggle source
# File lib/forkr.rb, line 151 def signal_worker(wpid, signal) begin Process.kill(signal, wpid) rescue Errno::ESRCH end end
spawn_worker()
click to toggle source
# File lib/forkr.rb, line 73 def spawn_worker if new_pid = fork @children << new_pid else worker_loop end end
worker_loop()
click to toggle source
# File lib/forkr.rb, line 162 def worker_loop @worker_client.after_fork if @worker_client.respond_to?(:after_fork) @inbound.close @outbound.close $stderr.puts "Worker spawned as #{$$}!" @worker_client.run throw(:bail_because_im_a_worker) end