Class: Thespian::Actor

Inherits:
Object
  • Object
show all
Defined in:
lib/thespian/actor.rb

Constant Summary collapse

STATE_INITIALIZED =

The state an actor is in after it has been created, but before it enters the message processing loop.

:initialized
STATE_RUNNING =

The state for when an actor is in the message processing loop.

:running
STATE_FINISHED =

The state for when an actor has exited the message processing loop (either by error or intentially).

:finished
DEFAULT_OPTIONS =
{
  :mode      => :thread,
  :strict    => true,
  :trap_exit => false,
  :object    => nil
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &block) ⇒ Actor

call-seq:

new(options = {}){ |message| ... }

Create a new actor using the given block to handles messages. The actor will be in the :initialized state, meaning the message processing loop hasn’t started yet (see #start).

options are the same as specified by #options.



42
43
44
45
46
47
48
49
50
# File 'lib/thespian/actor.rb', line 42

def initialize(options = {}, &block)
  self.options(DEFAULT_OPTIONS.merge(options))

  @state         = :initialized
  @receive_block = block
  @linked_actors = Set.new

  @strategy = strategy_class.new{ run }
end

Instance Attribute Details

#exceptionObject (readonly)

If an actor died due to an exception, it is stored here.



11
12
13
# File 'lib/thespian/actor.rb', line 11

def exception
  @exception
end

#stateObject (readonly)

Returns the actor’s state.



14
15
16
# File 'lib/thespian/actor.rb', line 14

def state
  @state
end

Instance Method Details

#<<(message) ⇒ Object

Add a message to the actor’s mailbox. May raise an exception according to #check_alive!



123
124
125
126
127
128
# File 'lib/thespian/actor.rb', line 123

def <<(message)
  check_alive! if options(:strict)
  message = message.new if message == Stop
  @strategy << message
  self
end

#error?Boolean

Returns true if an error occurred that caused the actor to enter the :finished state.

Returns:

  • (Boolean)


156
157
158
# File 'lib/thespian/actor.rb', line 156

def error?
  !!@exception
end

#finished?Boolean

#state == :finished

Returns:

  • (Boolean)


151
152
153
# File 'lib/thespian/actor.rb', line 151

def finished?
  state == :finished
end

#initialized?Boolean

#state == :initialized

Returns:

  • (Boolean)


141
142
143
# File 'lib/thespian/actor.rb', line 141

def initialized?
  state == :initialized
end

call-seq:

link(actor)

Exceptions in actors will be propogated to actors that are linked to them. How they are propogated is determined by the :trap_exit option (see #options).

actor can either be an Actor instance or an instance of a class that included Thespian.



96
97
98
99
100
# File 'lib/thespian/actor.rb', line 96

def link(object)
  actor = object.kind_of?(Actor) ? object : object.actor
  actor.send(:_link, self)
  object
end

#mailbox_sizeObject

Returns how many messages are in the actor’s mailbox.



161
162
163
# File 'lib/thespian/actor.rb', line 161

def mailbox_size
  @strategy.mailbox_size
end

#options(arg = nil) ⇒ Object

call-seq:

options -> Hash
options(hash) -> Hash
options(symbol) -> value

Get or set options. Valid options are:

:mode (default :thread)

What mode to create the actor in. Choices are :fiber or :thread. When running in fibered mode, be sure that EventMachine’s reactor is running and there is a root fiber.

:strict (default true)

Require an actor to be running in order to put messages into its mailbox.

:trap_exit (default false)

If true, the actor will get a DeadActorError message in its mailbox when a linked actor raises an unhandled exception.

If given no arguments, returns a hash of options.

If given a hash, sets the options specified in the hash.

If given a symbol, returns that option’s value.



76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/thespian/actor.rb', line 76

def options(arg = nil)
  @options ||= {}

  case arg
  when Hash
    @options.merge!(arg)
  when Symbol
    @options[arg]
  when nil
    @options
  end
end

#running?Boolean

#state == :running

Returns:

  • (Boolean)


146
147
148
# File 'lib/thespian/actor.rb', line 146

def running?
  state == :running
end

#salvage_mailboxObject

Salvage mailbox contents from a dead actor (including the message it died on). Useful for restarting a dead actor while preserving its mailbox.



167
168
169
170
171
172
# File 'lib/thespian/actor.rb', line 167

def salvage_mailbox
  raise "cannot salvage mailbox from an actor that isn't finished" unless finished?
  @strategy.messages.tap do |messages|
    messages.unshift(@last_message) if @last_message
  end
end

#startObject

Start the actor’s message processing loop. The thread that the loop is run on is guaranteed to have started by the time this method returns.

Raises:



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/thespian/actor.rb', line 104

def start

  # Don't let them start an already started actor.
  raise "already running" if running?

  # Can't raise an actor from the dead.
  raise @exception if @exception

  # IMPORTANT - Race condition!
  # This method and the thread both set @state. We don't want this method to
  # possibly overwrite how the thread sets @state, so we set the @state
  # before staring the thread.
  @state = :running

  @strategy.start
end

#stopObject

Stop the actor. All pending messages will be processed before stopping the actor. Raises an exception if the actor is not #running? and :strict (see #options) is true.

Raises:



133
134
135
136
137
138
# File 'lib/thespian/actor.rb', line 133

def stop
  check_alive! if options(:strict)
  self << Stop.new
  @strategy.stop
  raise @exception if @exception
end

#strategy_classObject

:nodoc:



52
53
54
55
# File 'lib/thespian/actor.rb', line 52

def strategy_class #:nodoc:
  class_name = options[:mode].to_s.capitalize
  Strategy.const_get(class_name)
end