class AWS::Flow::Core::BeginRescueEnsure

Enables asynchronous error handling within the AWS Flow Framework for Ruby. Calling {#begin}/{#rescue}/{#ensure} is similar to Ruby’s native ‘begin`/`rescue`/`end` semantics.

Attributes

__context__[R]
backtrace[R]
begin_task[RW]
cancelled[RW]
ensure_task[RW]
executor[RW]
failure[RW]
heirs[RW]
nonDaemonHeirsCount[RW]
parent[RW]
rescue_hash[RW]
result[RW]

Public Class Methods

new(options = {}) click to toggle source

Create a new ‘BeginRescueEnsure` object, with the provided options.

@param options

Options to set for the class.

@option options [Object] :parent

The parent object.

@api private

Calls superclass method
# File lib/aws/flow/begin_rescue_ensure.rb, line 42
def initialize(options = {})
  # We have two different arrays, rather than a hash,
  # because we want to ensure that we process the rescues in the order
  # they are written, and because prior to Ruby 1.9, hashes will not
  # return their elements in the order they were inserted.
  @rescue_hash = {}
  @parent = options[:parent] || Fiber.current.__context__
  @current = @parent
  @executor = @parent.executor
  @__context__ = self
  @nonDaemonHeirsCount = 0
  @current_state ||= self.class.get_start_state
  @heirs = Set.new
  @backtrace = make_backtrace(@parent.backtrace)
  @result = Future.new
  super() { consume(:run) }
end

Public Instance Methods

<<(async_task) click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 68
def <<(async_task)
  # Not going to include the promise to wait for, as it would appear that
  # fibers can wait on futures from their point of origin as part of their
  # implementation, as opposed to adding the callback here.
  check_closed
  if ! @heirs.member? async_task
    @heirs << async_task
    if ! async_task.is_daemon?
      @nonDaemonHeirsCount += 1
    end
  end
  @executor << async_task
  self
end
alive?() click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 185
def alive?
  @current_state != :closed
end
begin(block) click to toggle source

Binds the block to a lambda to be called when we get to the begin part of the data flow analysis.

@param block

The code block to be called when asynchronous *begin* starts.
# File lib/aws/flow/begin_rescue_ensure.rb, line 268
def begin(block)
  raise "Duplicated begin" if @begin_task
  # @begin_task = lambda { block.call }
  @begin_task = Task.new(self) { @result.set(block.call) }
end
cancel(error) click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 147
def cancel(error)
  if @current_state == :created
    @current_state = :closed
    @parent.remove(self)
    return
  end
  if @failure == nil
    @cancelled = true
    details = (error.respond_to? :details) ? error.details : nil
    reason = (error.respond_to? :reason) ? error.reason : nil
    @failure = CancellationException.new(reason, details)
    @failure.set_backtrace(@backtrace.backtrace) if @backtrace
    if @current_state == :begin
      cancelHeirs
    end
  end
end
cancelHeirs() click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 135
def cancelHeirs
  toCancel = @heirs.dup
  toCancel.each { |heir|  heir.cancel(@failure) }
end
check_closed() click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 92
def check_closed
  raise IllegalStateException, @failure if @current_state == :closed
end
ensure(block) click to toggle source

Binds the block to a lambda to be called when we get to the ensure part of the data flow analysis.

@param block

The code block to be called when asynchronous *ensure* starts.
# File lib/aws/flow/begin_rescue_ensure.rb, line 296
def ensure(block)
  raise "Duplicated ensure" if @ensure_task
  @ensure_task = Task.new(self) { block.call }
end
fail(this_task, error) click to toggle source

Fails the task, cancels all of its heirs, and then updates the state.

@param this_task

The task to fail.

@param error

The error associated with the failure.

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 105
def fail(this_task, error)
  check_closed
  if ( ! (error.class <= CancellationException) || @failure == nil && !@daemondCausedCancellation)
    backtrace = AsyncBacktrace.create_from_exception(@backtrace, error)
    error.set_backtrace(backtrace.backtrace) if backtrace
    @failure = error
  end
  task_out = @heirs.delete?(this_task)
  raise "There was a task attempted to be removed from a BRE, when the BRE did not have that task as an heir" unless task_out
  @nonDaemonHeirsCount -= 1 unless this_task.is_daemon?
  cancelHeirs
  update_state
end
get_closest_containing_scope() click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 84
def get_closest_containing_scope
  # BeginRescueEnsure's are special in that they act as a containing scope, so that things
  # created in BeginRescueEnsure's treat it as the parent, so that it can track the heirs
  # correctly and close only when nonDaemonHeirsCount is 0.
  self
end
get_heirs() click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 206
def get_heirs
  # TODO fix this so it returns string instead of printing to stdout
  str =  "I am a BeginRescueEnsure with #{heirs.length} heirs
  my begin block looks like #{@begin_task}" +
    @heirs.map(&:get_heirs).to_s

  # (@heirs.each(&:get_heirs) + [self]).flatten
end
is_daemon?() click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 62
def is_daemon?
  false
end
merge_stacktraces(failure, this_backtrace, error) click to toggle source

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 141
def merge_stacktraces(failure, this_backtrace, error)
  backtrace = AsyncBacktrace.create_from_exception(this_backtrace, error)
  failure.set_backtrace(backtrace.backtrace) if backtrace
end
remove(this_task) click to toggle source

Removes the task and updates the state.

@param this_task

The task to remove.

@api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 125
def remove(this_task)
  check_closed

  task_out = @heirs.delete?(this_task)
  raise "There was a task attempted to be removed from a BRE, when the BRE did not have that task as an heir" unless task_out
  @nonDaemonHeirsCount -= 1 unless this_task.is_daemon?
  update_state
end
rescue(error_types, block) click to toggle source

Binds the block to a lambda to be called when we get to the rescue part of the DFA

@param error_types

The error types.

@param block

The code block to be called when asynchronous *rescue* starts.
# File lib/aws/flow/begin_rescue_ensure.rb, line 282
def rescue(error_types, block)
  error_types = [error_types] unless error_types.is_a? Array
  this_task = lambda { |failure| block.call(failure) }
  if @rescue_hash.key? error_types
    raise "You have already registered #{error_types}"
  end
  @rescue_hash[error_types] = this_task
end
run() click to toggle source

Actually runs the BeginRescueEnsure, by going through the data flow analysis with the symbol :run. @api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 167
def run
  this_failure = @failure
  begin
    consume(:run)
  rescue Exception => error
    if this_failure != error
      backtrace = AsyncBacktrace.create_from_exception(@backtrace, error)
      error.set_backtrace(backtrace.backtrace) if backtrace
    end
    @failure = error
    cancelHeirs
  ensure
    update_state
    raise @failure if (!!@failure && @current_state == :closed)
  end
end
schedule() click to toggle source
# File lib/aws/flow/begin_rescue_ensure.rb, line 301
def schedule
  @parent << self
end
update_state() click to toggle source

Updates the state based on the most recent transitions in the data flow analysis. @api private

# File lib/aws/flow/begin_rescue_ensure.rb, line 191
def update_state
  #TODO ? Add the ! @executed part
  #return if @current_state == :closed || ! @executed
  return if @current_state == :closed
  if @nonDaemonHeirsCount == 0
    if @heirs.empty?
      consume(:update_state)
    else
      @daemondCausedCancellation = true if @failure == nil
      cancelHeirs
    end
  end
end