module Tribe::Actable

Public Instance Methods

alive?() click to toggle source
# File lib/tribe/actable.rb, line 92
def alive?
  @_actable.mailbox.alive?
end
dead?() click to toggle source
# File lib/tribe/actable.rb, line 96
def dead?
  !alive?
end
deliver_event!(event) click to toggle source

Thread safe public methods. Notes: These are the methods that actors use to communicate with each other.

Actors should avoid sharing mutable state in order to remain thread safe.
Methods with a ! are designed for asynchronous communication.
# File lib/tribe/actable.rb, line 46
def deliver_event!(event)
  @_actable.mailbox.push(event) do
    process_events
  end

  nil
end
direct_message!(command, data = nil, src = nil) click to toggle source
# File lib/tribe/actable.rb, line 54
def direct_message!(command, data = nil, src = nil)
  deliver_event!(Tribe::Event.new(command, data, src))

  nil
end
exception() click to toggle source
# File lib/tribe/actable.rb, line 108
def exception
  @_actable.exception
end
identifier() click to toggle source
# File lib/tribe/actable.rb, line 104
def identifier
  @_actable.name ? "#{object_id}:#{@_actable.name}" : object_id
end
logger() click to toggle source
# File lib/tribe/actable.rb, line 120
def logger
  @_actable.logger
end
name() click to toggle source
# File lib/tribe/actable.rb, line 100
def name
  @_actable.name
end
perform!(&block) click to toggle source
# File lib/tribe/actable.rb, line 64
def perform!(&block)
  direct_message!(:__perform__, block)
end
pool() click to toggle source
# File lib/tribe/actable.rb, line 116
def pool
  @_actable.pool
end
registry() click to toggle source
# File lib/tribe/actable.rb, line 112
def registry
  @_actable.registry
end
shutdown!() click to toggle source
# File lib/tribe/actable.rb, line 60
def shutdown!
  direct_message!(:__shutdown__)
end
spawn!(klass, actor_options = {}, spawn_options = {}) click to toggle source
# File lib/tribe/actable.rb, line 68
def spawn!(klass, actor_options = {}, spawn_options = {})
  actor_options[:parent] = self

  child = nil

  if spawn_options[:no_raise_on_failure]
    begin
      child = klass.new(actor_options)
    rescue Exception
      return false
    end
  else
    child = klass.new(actor_options)
  end

  @_actable.children.add(child)

  if spawn_options[:supervise]
    @_actable.supervisees.add(child)
  end

  child
end

Private Instance Methods

blocking!() { || ... } click to toggle source

Wrap blocking code using this method to automatically expand/contract the pool. This way you avoid potential thread starvation. Not needed for dedicated actors since they already have their own thread.

# File lib/tribe/actable.rb, line 355
def blocking!
  if @_actable.dedicated
    yield
  else
    pool.expand(1)
    begin
      yield
    ensure
      pool.contract(1)
    end
  end
end
child_died_handler(child, exception) click to toggle source
# File lib/tribe/actable.rb, line 267
def child_died_handler(child, exception)
  @_actable.children.delete(child)
  supervising = !!@_actable.supervisees.delete?(child)

  on_child_died(Event.new(:child_died, {:child => child, :exception => exception}))

  if !supervising
    raise Tribe::ActorChildDied.new("#{child.identifier} died.")
  end

  nil
end
child_shutdown_handler(child) click to toggle source
# File lib/tribe/actable.rb, line 280
def child_shutdown_handler(child)
  @_actable.children.delete(child)
  @_actable.supervisees.delete(child)

  on_child_shutdown(Event.new(:child_shutdown, {:child => child}))

  nil
end
cleanup_handler(exception = nil) click to toggle source
# File lib/tribe/actable.rb, line 257
def cleanup_handler(exception = nil)
  @_actable.exception = exception
  @_actable.pool.shutdown if @_actable.dedicated
  @_actable.mailbox.kill
  @_actable.registry.unregister(self)
  @_actable.timers.each { |t| t.cancel } if @_actable.timers

  nil
end
custom_event_handler(event) click to toggle source
# File lib/tribe/actable.rb, line 195
def custom_event_handler(event)
  result = nil
  @_actable.active_event = event

  begin
    result = send("on_#{event.command}", event)
  rescue Exception => e
    result = e
    raise
  ensure
    if event.future && @_actable.active_event
      event.future.result = result
    end
    @_actable.active_event = nil
  end

  nil
end
event_handler(event) click to toggle source
# File lib/tribe/actable.rb, line 173
def event_handler(event)
  case event.command
  when :__initialize__
    initialize_handler(event)
  when :__shutdown__
    cleanup_handler
    shutdown_handler(event)
  when :__perform__
    perform_handler(event)
  when :__child_died__
    child_died_handler(event.data[0], event.data[1])
  when :__child_shutdown__
    child_shutdown_handler(event.data)
  when :__parent_died__
    parent_died_handler(event.data[0], event.data[1])
  when :initialize, :shutdown, :perform, :child_died, :child_shutdown, :parent_died
    raise ActorReservedCommand.new("Reserved commands are not allowed (command=#{event.command}).")
  else
    custom_event_handler(event)
  end
end
exception_handler(exception) click to toggle source
# File lib/tribe/actable.rb, line 218
def exception_handler(exception)
  if @_actable.parent
    @_actable.parent.direct_message!(:__child_died__, [self, exception])
  end

  @_actable.children.each { |c| c.direct_message!(:__parent_died__, [self, exception]) }
  @_actable.children.clear
  @_actable.supervisees.clear

  log_exception_handler(exception)
  on_exception(Event.new(:exception, {:exception => exception}))

  nil
end
forward!(dest) click to toggle source
# File lib/tribe/actable.rb, line 345
def forward!(dest)
  dest.deliver_event!(@_actable.active_event)
  @_actable.active_event = nil

  nil
end
future!(dest, command, data = nil) click to toggle source
# File lib/tribe/actable.rb, line 311
def future!(dest, command, data = nil)
  event = Tribe::Event.new(command, data, self)
  event.future = future = Tribe::Future.new(self)

  dest.deliver_event!(event)

  future
end
init_actable(options = {}) click to toggle source

Initialization method. Notes: Call this in your constructor.

# File lib/tribe/actable.rb, line 12
def init_actable(options = {})
  # Symbols aren't GCed in JRuby so force string names.
  if options[:name] && !options[:name].is_a?(String)
    raise Tribe::ActorNameError.new('Name must be a string.')
  end

  @_actable = Tribe::ActorState.new

  @_actable.dedicated = options[:dedicated] || false
  @_actable.pool = @_actable.dedicated ? Workers::Pool.new(:size => 1) : (options[:pool] || Workers.pool)
  @_actable.mailbox = Tribe::Mailbox.new(@_actable.pool)
  @_actable.registry = options[:registry] || Tribe.registry
  @_actable.logger = Workers::LogProxy.new(options[:logger] || Tribe.logger)
  @_actable.scheduler = options[:scheduler] || Workers.scheduler
  @_actable.name = options[:name]
  @_actable.parent = options[:parent]
  @_actable.children = Tribe::SafeSet.new
  @_actable.supervisees = Tribe::SafeSet.new
  @_actable.timers = Tribe::SafeSet.new

  @_actable.registry.register(self)

  direct_message!(:__initialize__)
end
initialize_handler(event) click to toggle source
# File lib/tribe/actable.rb, line 214
def initialize_handler(event)
  on_initialize(event)
end
log_exception_handler(exception) click to toggle source
# File lib/tribe/actable.rb, line 233
def log_exception_handler(exception)
  logger.error("EXCEPTION: #{exception.message}\n#{exception.backtrace.join("\n")}\n--")
end
message!(dest, command, data = nil) click to toggle source

Private API methods. Notes: Use these methods internally in your actor.

# File lib/tribe/actable.rb, line 303
def message!(dest, command, data = nil)
  event = Tribe::Event.new(command, data, self)

  dest.deliver_event!(event)

  nil
end
on_child_died(event) click to toggle source
# File lib/tribe/actable.rb, line 140
def on_child_died(event)
end
on_child_shutdown(event) click to toggle source
# File lib/tribe/actable.rb, line 143
def on_child_shutdown(event)
end
on_exception(event) click to toggle source
# File lib/tribe/actable.rb, line 134
def on_exception(event)
end
on_initialize(event) click to toggle source

Private command handlers. Notes: These methods are designed to be overriden in order to respond to actor system events.

# File lib/tribe/actable.rb, line 131
def on_initialize(event)
end
on_parent_died(event) click to toggle source
# File lib/tribe/actable.rb, line 146
def on_parent_died(event)
end
on_shutdown(event) click to toggle source
# File lib/tribe/actable.rb, line 137
def on_shutdown(event)
end
parent_died_handler(parent, exception) click to toggle source
# File lib/tribe/actable.rb, line 289
def parent_died_handler(parent, exception)
  on_parent_died(Event.new(:parent_died, {:parent => parent, :exception => exception}))
  raise Tribe::ActorParentDied.new("#{parent.identifier} died.")

  nil
end
perform_handler(event) click to toggle source
# File lib/tribe/actable.rb, line 251
def perform_handler(event)
  event.data.call

  nil
end
periodic_timer!(delay, command, data = nil) click to toggle source
# File lib/tribe/actable.rb, line 331
def periodic_timer!(delay, command, data = nil)
  timer = Workers::PeriodicTimer.new(delay, :scheduler => @_actable.scheduler) do
    direct_message!(command, data)
    unless alive?
      @_actable.timers.delete(timer)
      timer.cancel
    end
  end

  @_actable.timers.add(timer)

  timer
end
process_events() click to toggle source

All system commands are prefixed with an underscore.

# File lib/tribe/actable.rb, line 157
def process_events
  while (event = @_actable.mailbox.obtain_and_shift)
    event_handler(event)
  end

rescue Exception => exception
  cleanup_handler(exception)
  exception_handler(exception)
ensure
  @_actable.mailbox.release do
    process_events
  end

  nil
end
shutdown_handler(event) click to toggle source
# File lib/tribe/actable.rb, line 237
def shutdown_handler(event)
  if @_actable.parent
    @_actable.parent.direct_message!(:__child_shutdown__, self)
  end

  @_actable.children.each { |c| c.shutdown! }
  @_actable.children.clear
  @_actable.supervisees.clear

  on_shutdown(Event.new(:shutdown, {}))

  nil
end
timer!(delay, command, data = nil) click to toggle source
# File lib/tribe/actable.rb, line 320
def timer!(delay, command, data = nil)
  timer = Workers::Timer.new(delay, :scheduler => @_actable.scheduler) do
    @_actable.timers.delete(timer)
    direct_message!(command, data)
  end

  @_actable.timers.add(timer)

  timer
end
wait!(future) click to toggle source
# File lib/tribe/actable.rb, line 368
def wait!(future)
  blocking! do
    future.wait
  end
end