Skip to content

Commit

Permalink
Allow tasks to be marked as transient.
Browse files Browse the repository at this point in the history
Transient tasks are not considered when checking if the reactor is
finished. When the reactor determines that it is finshed, all tasks are
stopped. This allows tasks to be scoped to the lifetime of the reactor.
  • Loading branch information
ioquatix committed Apr 21, 2020
1 parent 9820ab8 commit ffd5552
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 13 deletions.
46 changes: 38 additions & 8 deletions lib/async/node.rb
Expand Up @@ -27,13 +27,18 @@ module Async
class Node
# Create a new node in the tree.
# @param parent [Node, nil] This node will attach to the given parent.
def initialize(parent = nil, annotation: nil)
def initialize(parent = nil, annotation: nil, transient: false)
@children = nil
@parent = nil

# The number of transient children:
@transients = 0

@annotation = annotation
@object_name = nil

@transient = transient

if parent
self.parent = parent
end
Expand All @@ -48,6 +53,16 @@ def initialize(parent = nil, annotation: nil)
# A useful identifier for the current node.
attr :annotation

# Is this node transient?
def transient?
@transient
end

# Does this node have transient children?
def transients?
@transients > 0
end

def annotate(annotation)
if block_given?
previous_annotation = @annotation
Expand All @@ -60,7 +75,7 @@ def annotate(annotation)
end

def description
@object_name ||= "#{self.class}:0x#{object_id.to_s(16)}"
@object_name ||= "#{self.class}:0x#{object_id.to_s(16)}#{@transient ? ' transient' : nil}"

if @annotation
"#{@object_name} #{@annotation}"
Expand All @@ -73,10 +88,6 @@ def to_s
"\#<#{description}>"
end

def inspect
to_s
end

# Change the parent of this node.
# @param parent [Node, nil] the parent to attach to, or nil to detach.
# @return [self]
Expand All @@ -96,22 +107,30 @@ def parent=(parent)
return self
end

protected def set_parent parent
@parent = parent
end

protected def add_child child
@children ||= Set.new
@children << child

if child.transient?
@transients += 1
end
end

# Whether the node can be consumed safely. By default, checks if the
# children set is empty.
# @return [Boolean]
def finished?
@children.nil? or @children.empty?
@children.nil? || @children.empty? || (@children.size == @transients)
end

# If the node has a parent, and is {finished?}, then remove this node from
# the parent.
def consume
if @parent and finished?
if @parent && finished?
@parent.reap(self)
@parent.consume
@parent = nil
Expand All @@ -122,6 +141,17 @@ def consume
# @param child [Node]
def reap(child)
@children.delete(child)

if child.transient?
@transients -= 1
end

child.children&.each do |grand_child|
unless grand_child.finished?
grand_child.set_parent(self)
add_child(grand_child)
end
end
end

# Traverse the tree.
Expand Down
13 changes: 8 additions & 5 deletions lib/async/reactor.rb
Expand Up @@ -184,13 +184,16 @@ def run_once(timeout = nil)
interval = 0
end

# If we are finished, we stop the task tree and exit.
if self.finished?
# If there is nothing to do, then finish:
self.stop

return false
end

# If there is no interval to wait (thus no timers), and no tasks, we could be done:
if interval.nil?
if self.finished?
# If there is nothing to do, then finish:
return false
end

# Allow the user to specify a maximum interval if we would otherwise be sleeping indefinitely:
interval = timeout
elsif interval < 0
Expand Down
20 changes: 20 additions & 0 deletions spec/async/node_spec.rb
Expand Up @@ -95,4 +95,24 @@
expect(subject.annotation).to be == annotation
end
end

describe '#transient' do
let!(:middle) {Async::Node.new(subject)}
let!(:child) {Async::Node.new(middle, transient: true)}

it 'can move transient child to parent' do
expect(child).to be_transient
expect(middle).to be_finished

allow(child).to receive(:finished?).and_return(false)

middle.consume

expect(child).to_not be_finished
expect(subject).to be_finished

expect(child).to receive(:stop)
subject.stop
end
end
end

0 comments on commit ffd5552

Please sign in to comment.