class Async::Node
A node in a tree, used for implementing the task hierarchy.
def add_child(child)
def add_child(child) ||= Children.new .append(child) _parent(self)
def annotate(annotation)
def annotate(annotation) if block_given? previous_annotation = @annotation @annotation = annotation yield @annotation = previous_annotation else @annotation = annotation end end
def backtrace(*arguments)
def backtrace(*arguments) nil end
def children?
Whether this node has any children.
def children? @children && !@children.empty? end
def consume
If the node has a parent, and is {finished?}, then remove this node from
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 description
def description @object_name ||= "#{self.class}:#{format '%#018x', object_id}#{@transient ? ' transient' : nil}" if @annotation "#{@object_name} #{@annotation}" elsif line = self.backtrace(0, 1)&.first "#{@object_name} #{line}" else @object_name end end
def finished?
Whether the node can be consumed (deleted) safely. By default, checks if the children set is empty.
def finished? @children.nil? || @children.finished? end
def initialize(parent = nil, annotation: nil, transient: false)
Create a new node in the tree.
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 parent=(parent)
@parameter parent [Node | Nil] The parent to attach to, or nil to detach.
Change the parent of this node.
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 print_backtrace(out, indent, node)
def print_backtrace(out, indent, node) if backtrace = node.backtrace backtrace.each_with_index do |line, index| out.puts "#{indent}#{index.zero? ? "→ " : " "}#{line}" end end end
def print_hierarchy(out = $stdout, backtrace: true)
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
def remove_child(child)
def remove_child(child) .remove(child) _parent(nil)
def root
def root @parent&.root || self end
def set_parent(parent)
def set_parent(parent) parent
def stop(later = false)
Attempt to stop the current node immediately, including all non-transient children. Invokes {#stop_children} to stop all children.
def stop(later = false) # The implementation of this method may defer calling `stop_children`. stop_children(later) end
def stop_children(later = false)
def stop_children(later = false) en&.each do |child| stop(later) unless child.transient?
def stopped?
def stopped? @children.nil? end
def terminate
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 to_s
def to_s "\#<#{self.description}>" end
def transient?
is not directly related to the parent task, and should not prevent the
a child task which is pruning a connection pool is transient, because it
internal to an object rather than explicit user concurrency. For example,
when determining if a node is finished. This is useful for tasks which are
Represents whether a node is transient. Transient nodes are not considered
def transient? @transient end
def traverse(&block)
@returns [Enumerator] An enumerator which will traverse the tree if no block is given.
Traverse the task tree.
def traverse(&block) return enum_for(:traverse) unless block_given? self.traverse_recurse(&block) end
def traverse_recurse(level = 0, &block)
def traverse_recurse(level = 0, &block) f, level &.each do |child| averse_recurse(level + 1, &block)