######################################################
#
# **** DO NOT EDIT ****
#
# **** THIS IS A GENERATED FILE *****
#
######################################################
require 'rake/comp_tree/bucket_ipc'
require 'rake/comp_tree/quix/diagnostic'
require 'rake/comp_tree/quix/kernel'
require 'rake/comp_tree/algorithm'
require 'rake/comp_tree/node'
require 'rake/comp_tree/task_node'
require 'rake/comp_tree/error'
require 'thread'
module Rake::CompTree
#
# Driver is the main interface to the computation tree. It is
# responsible for defining nodes and running computations.
#
class Driver
DEFAULTS = {
:threads => 1,
:fork => false,
:timeout => 5.0,
:wait_interval => 0.02,
}
include Quix::Diagnostic #:nodoc:
include Quix::Kernel #:nodoc:
#
# Begin a new computation tree.
#
# Options hash:
#
# :node_class -- (Class) Rake::CompTree::Node subclass from
# which nodes are created.
#
# :discard_result -- (boolean) If you are not
# interested in the final answer, but only in the actions which
# complete the computation, then set this to +true+. This is
# equivalent to saying :node_class => Rake::CompTree::TaskNode.
# (If you are forking processes, it is good to know that IPC is
# not needed to communicate the result.)
#
def initialize(opts = nil)
if opts and opts[:node_class] and opts[:discard_result]
raise(
Error::ArgumentError,
"#{self.class.name}.new: :discard_result and :node_class " +
"are mutually exclusive")
end
@node_class =
if opts and opts[:node_class]
opts[:node_class]
elsif opts and opts[:discard_result]
TaskNode
else
Node
end
@nodes = Hash.new
if block_given?
yield self
end
end
#
# Name-to-node hash.
#
attr_reader :nodes
#
# Define a computation node.
#
# There are three distinct forms of a node definition. In each of
# the following examples, a computation node named +area+ is
# defined which depends on the nodes +height+, +width+, +offset+.
#
# The method_missing form:
# driver.define_area(:width, :height, :offset) { |width, height, offset|
# width*height - offset
# }
#
# The eval form:
# driver.define_area :width, :height, :offset, %{
# width*height - offset
# }
# (Note the '%' before the brace.)
#
# The raw form:
# driver.define(:area, :width, :height, :offset) { |width, height, offset|
# width*height - offset
# }
#
def define(*args, &block)
parent_name = args.first
children_names = args[1..-1]
unless parent_name
raise Error::ArgumentError, "No name given for node"
end
#
# retrieve or create parent and children
#
parent =
if t = @nodes[parent_name]
t
else
@nodes[parent_name] = @node_class.new(parent_name)
end
if parent.function
raise Error::RedefinitionError, "Node #{parent.name} already defined."
end
parent.function = block
children = children_names.map { |child_name|
if t = @nodes[child_name]
t
else
@nodes[child_name] = @node_class.new(child_name)
end
}
#
# link
#
parent.children = children
children.each { |child|
child.parents << parent
}
end
#
# parsing/evaling helper
#
def evaling_define(*args) #:nodoc:
function_name = args[0]
function_arg_names = args[1..-2]
function_string = args.last.to_str
comma_separated = function_arg_names.map { |name|
name.to_s
}.join(",")
eval_me = %{
lambda { |#{comma_separated}|
#{function_string}
}
}
function = eval(eval_me, TOPLEVEL_BINDING)
define(function_name, *function_arg_names, &function)
end
def method_missing(symbol, *args, &block) #:nodoc:
if match = symbol.to_s.match(%r!\Adefine_(\w+)\Z!)
method_name = match.captures.first.to_sym
if block
define(method_name, *args, &block)
else
evaling_define(method_name, *args)
end
else
super(symbol, *args, &block)
end
end
#
# Mark this node and all its children as uncomputed.
#
# Arguments:
#
# +name+ -- (Symbol) node name.
#
def reset(name)
@nodes[name].reset
end
#
# Check for a cyclic graph below the given node. Raises
# Rake::CompTree::Error::CircularError if found.
#
# Arguments:
#
# +name+ -- (Symbol) node name.
#
def check_circular(name)
helper = lambda { |root, chain|
if chain.include? root
raise Error::CircularError,
"Circular dependency detected: #{root} => #{chain.last} => #{root}"
end
@nodes[root].children.each { |child|
helper.call(child.name, chain + [root])
}
}
helper.call(name, [])
end
#
# Compute this node.
#
# Arguments:
#
# +name+ -- (Symbol) node name.
#
# Options hash:
#
# :threads -- (Integer) Number of parallel threads.
#
# :fork -- (boolean) Whether to fork each computation
# node into its own process.
#
# Defaults options are taken from Driver::DEFAULTS.
#
def compute(name, opts = nil)
#
# Undocumented options:
#
# :wait_interval -- (seconds) (Obscure) How long to
# wait after an IPC failure.
#
# :timeout -- (seconds) (Obscure) Give up after this
# period of persistent IPC failures.
#
abort_on_exception {
compute_private(name, opts || Hash.new)
}
end
private
def compute_private(name, opts_in)
opts = DEFAULTS.merge(opts_in)
root = @nodes[name]
if opts[:threads] < 1
raise Error::ArgumentError, "threads is #{opts[:threads]}"
end
if opts[:threads] == 1
root.result = root.compute_now
elsif opts[:fork] and not @node_class.discard_result?
#
# Use buckets to send results across forks.
#
result = nil
BucketIPC::Driver.new(opts[:threads], opts) { |buckets|
result =
Algorithm.compute_multithreaded(
root, opts[:threads], opts[:fork], buckets)
}
result
else
#
# Multithreaded computation without fork.
#
Algorithm.compute_multithreaded(
root, opts[:threads], opts[:fork], nil)
end
end
end
end
######################################################
#
# **** DO NOT EDIT ****
#
# **** THIS IS A GENERATED FILE *****
#
######################################################