class Node
A node in a tree, used for implementing the task hierarchy.
Definitions
def initialize(parent = nil, annotation: nil, transient: false)
Create a new node in the tree.
Signature
-
parameter
parentNode | Nil This node will attach to the given parent.
Implementation
def initialize(parent = nil, annotation: nil, transient: false)
@parent = nil
@children = nil
@annotation = annotation
@object_name = nil
@transient = transient
@head = nil
@tail = nil
if parent
parent.add_child(self)
end
end
def root
Signature
-
returns
Node The root node in the hierarchy.
Implementation
def root
@parent&.root || self
end
attr_accessor :head
- private
Signature
- private
attr_accessor :tail
- private
Signature
- private
attr :parent
Signature
-
attribute
Node The parent node.
attr :children
Signature
-
attribute
Children | Nil Optional list of children.
attr :annotation
Signature
-
attribute
String | Nil A useful identifier for the current node.
def children?
Whether this node has any children.
Signature
-
returns
Boolean
Implementation
def children?
@children && !@children.empty?
end
def transient?
Represents whether a node is transient. Transient nodes are not considered when determining if a node is finished. This is useful for tasks which are internal to an object rather than explicit user concurrency. For example, a child task which is pruning a connection pool is transient, because it is not directly related to the parent task, and should not prevent the parent task from finishing.
Implementation
def transient?
@transient
end
def transient=(value)
Change the transient state of the node.
A transient node is not considered when determining if a node is finished, and propagates up if the parent is consumed.
Signature
-
parameter
valueBoolean Whether the node is transient.
Implementation
def transient=(value)
if @transient != value
@transient = value
@parent&.children&.adjust_transient_count(value)
end
end
def annotate(annotation)
Annotate the node with a description.
Signature
-
parameter
annotationString The description to annotate the node with.
Implementation
def annotate(annotation)
if block_given?
begin
current_annotation = @annotation
@annotation = annotation
return yield
ensure
@annotation = current_annotation
end
else
@annotation = annotation
end
end
def description
A description of the node, including the annotation and object name.
Signature
-
returns
String The description of the node.
Implementation
def description
@object_name ||= "#{self.class}:#{format '%#018x', object_id}#{@transient ? ' transient' : nil}"
if annotation = self.annotation
"#{@object_name} #{annotation}"
elsif line = self.backtrace(0, 1)&.first
"#{@object_name} #{line}"
else
@object_name
end
end
def backtrace(*arguments)
Provides a backtrace for nodes that have an active execution context.
Signature
-
returns
Array(Thread::Backtrace::Locations) | Nil The backtrace of the node, if available.
Implementation
def backtrace(*arguments)
nil
end
def to_s
Signature
-
returns
String A description of the node.
Implementation
def to_s
"\#<#{self.description}>"
end
alias inspect to_s
Signature
-
returns
String A description of the node.
def parent=(parent)
Change the parent of this node.
Signature
-
parameter
parentNode | Nil The parent to attach to, or nil to detach.
-
returns
Node Itself.
Implementation
def parent=(parent)
return if @parent.equal?(parent)
if @parent
@parent.remove_child(self)
@parent = nil
end
if parent
parent.add_child(self)
end
return self
end
def finished?
Whether the node can be consumed (deleted) safely. By default, checks if the children set is empty.
Signature
-
returns
Boolean
Implementation
def finished?
@children.nil? || @children.finished?
end
def consume
If the node has a parent, and is Async::Node#finished?, then remove this node from
the parent.
Implementation
def consume
if parent = @parent and finished?
parent.remove_child(self)
# If we have children, then we need to move them to our the parent if they are not finished:
if @children
while child = @children.shift
if child.finished?
child.set_parent(nil)
else
parent.add_child(child)
end
end
@children = nil
end
parent.consume
end
end
def traverse(&block)
Traverse the task tree.
Signature
-
returns
Enumerator An enumerator which will traverse the tree if no block is given.
-
yields
{|node, level| ...} The node and the level relative to the given root.
Implementation
def traverse(&block)
return enum_for(:traverse) unless block_given?
self.traverse_recurse(&block)
end
def terminate
Immediately terminate all children tasks, including transient tasks. Internally invokes stop(false) on all children. This should be considered a last ditch effort and is used when closing the scheduler.
Implementation
def terminate
# Attempt to stop the current task immediately, and all children:
stop(false)
# If that doesn't work, take more serious action:
@children&.each do |child|
child.terminate
end
return @children.nil?
end
def stop(later = false)
Attempt to stop the current node immediately, including all non-transient children. Invokes #stop_children to stop all children.
Signature
-
parameter
laterBoolean Whether to defer stopping until some point in the future.
Implementation
def stop(later = false)
# The implementation of this method may defer calling `stop_children`.
stop_children(later)
end
def wait
Wait for this node to complete. By default, nodes cannot be waited on. Subclasses like Task override this method to provide waiting functionality.
Implementation
def wait
nil
end
def stopped?
Whether the node has been stopped.
Implementation
def stopped?
@children.nil?
end
def print_hierarchy(out = $stdout, backtrace: true)
Print the hierarchy of the task tree from the given node.
Signature
-
parameter
outIO The output stream to write to.
-
parameter
backtraceBoolean Whether to print the backtrace of each node.
Implementation
def print_hierarchy(out = $stdout, backtrace: true)
self.traverse do |node, level|
indent = "\t" * level
out.puts "#{indent}#{node}"
print_backtrace(out, indent, node) if backtrace
end
end