class Forkner
¶ ↑
Forkner
Constants
- VERSION
version 1.2
Attributes
The maximum number of child processes to run at once.
Temporary directory for storing information from child processes. Defaults to /tmp.
Public Class Methods
container is probably the easiest way to use Forkner
. Run all the code that will have child processes inside a container block. The single parameter for container is the maximum number of child processes to allow. After the container block, Forkner
waits for all remaining child processes to exit.
So, for example, this code loops 100 times, but will not fork more than five children at a time:
Forkner.container(5) do |forkner| 100.times do forkner.child do # do stuff in the child process end end end
# File lib/forkner.rb, line 42 def self.container(max) forkner = Forkner.new(max) yield forkner forkner.wait_all end
Creates a new Forkner
object. The single parameter is the maximum number of child processes to run at once. So, for example, the following code creates a Forkner
object that will allow up to five child processes at once.
forkner = Forkner.new(5)
# File lib/forkner.rb, line 63 def initialize(max) @max = max @children = {} @tmp_dir = '/tmp' @reaper = nil end
Public Instance Methods
Runs a child process. If there are already the maximum number of children running then this method waits until one of them exits. The last line of the block is information that can be stored in JSON, and if you have defined a reaper block, then that information will be conveyed back to the parent process.
For example, this child process generates a random number and a timestamp. The last line of the block is a hash that will be sent back to the parent process.
forkner.child do myrand = rand() timestamp = Time.now {'myrand'=>myrand, 'timestamp'=>timestamp} end
# File lib/forkner.rb, line 94 def child # init json_path = nil # transfer file if @reaper json_path = Random.rand().to_s json_path = json_path.sub(/\A.*\./mu, '') json_path = @tmp_dir + '/' + json_path end # wait until we have a space for a process waiter @max - 1 # parent process if new_child_pid = Process.fork() # set child record child = @children[new_child_pid] = {} # file handle if @reaper child['json_path'] = json_path end # return true return true # child process else # yield if necessary if block_given? rv = yield() # save to json file if necessary if json_path File.write json_path, JSON.generate(rv) end # exit child process exit end # always return false return false end end
Defines the block to run when a child process finishes. The single param passed to the block is a hash or array of information from the child process. For example, if the child process returns a hash of information, you might display it like this:
forkner.reaper() do |rv| puts '-----' puts rv['myrand'] puts rv['timestamp'] end
# File lib/forkner.rb, line 160 def reaper(&block) @reaper = block end
Waits for all child processes to finish. You don't need to call this method if you're using Forkner.container
.
# File lib/forkner.rb, line 175 def wait_all waiter 0 end
Private Instance Methods
# File lib/forkner.rb, line 196 def waiter(wait_max) # loop until we have fewer than @max children while @children.length > wait_max begin # wait old_child_pid = Process.wait(-1, Process::WNOHANG) old_child = @children.delete(old_child_pid) # if child if old_child and old_child['json_path'] # if reaper if @reaper # slurp in JSON rv = JSON.parse(File.read(old_child['json_path'])) # delete transfer file File.unlink old_child['json_path'] # call reaper @reaper.call rv end end # TODO: Handle system errors rescue SystemCallError end end end