Class: Actor

Inherits:
Object
  • Object
show all
Defined in:
lib/revactor.rb,
lib/revactor/actor.rb,
lib/revactor/mailbox.rb,
lib/revactor/scheduler.rb

Overview

– Copyright ©2007 Tony Arcieri You can redistribute this under the terms of the Ruby license See file LICENSE for details ++

Defined Under Namespace

Classes: Mailbox, Scheduler

Constant Summary collapse

TCP =
Revactor::TCP
UNIX =
Revactor::UNIX
Filter =
Revactor::Filter
HttpClient =
Revactor::HttpClient
@@registered =
{}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(fiber = Fiber.current) ⇒ Actor

Returns a new instance of Actor.

Raises:

  • (ArgumentError)


152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/revactor/actor.rb', line 152

def initialize(fiber = Fiber.current)
  raise ArgumentError, "use Actor.spawn to create actors" if block_given?
  
  @fiber = fiber
  @scheduler = Actor.scheduler
  @thread = Thread.current
  @mailbox = Mailbox.new
  @links = []
  @events = []
  @trap_exit = false
  @dead = false
  @dictionary = {}
end

Instance Attribute Details

#fiberObject (readonly)

Returns the value of attribute fiber.



39
40
41
# File 'lib/revactor/actor.rb', line 39

def fiber
  @fiber
end

#mailboxObject (readonly)

Returns the value of attribute mailbox.



41
42
43
# File 'lib/revactor/actor.rb', line 41

def mailbox
  @mailbox
end

#schedulerObject (readonly)

Returns the value of attribute scheduler.



40
41
42
# File 'lib/revactor/actor.rb', line 40

def scheduler
  @scheduler
end

Class Method Details

.[](key) ⇒ Object

Look up an actor in the global dictionary



119
120
121
# File 'lib/revactor/actor.rb', line 119

def [](key)
  @@registered[key]
end

.[]=(key, actor) ⇒ Object

Register this actor in the global dictionary



124
125
126
127
128
129
130
# File 'lib/revactor/actor.rb', line 124

def []=(key, actor)
  unless actor.is_a?(Actor)
    raise ArgumentError, "only actors may be registered"
  end

  @@registered[key] = actor
end

.currentObject

Obtain a handle to the current Actor



76
77
78
# File 'lib/revactor/actor.rb', line 76

def current
  Fiber.current._actor
end

.delete(key, &block) ⇒ Object

Delete an actor from the global dictionary



133
134
135
# File 'lib/revactor/actor.rb', line 133

def delete(key, &block)
  @@registered.delete(key, &block)
end

Link the current Actor to another one



66
67
68
# File 'lib/revactor/actor.rb', line 66

def link(actor)
  current.link actor
end

.receive(&filter) ⇒ Object

Wait for messages matching a given filter. The filter object is yielded to be block passed to receive. You can then invoke the when argument which takes a parameter and a block. Messages are compared (using ===) against the parameter. The Case gem includes several tools for matching messages using ===

The first filter to match a message in the mailbox is executed. If no filters match then the actor sleeps.



114
115
116
# File 'lib/revactor/actor.rb', line 114

def receive(&filter)
  current.mailbox.receive(&filter)
end

.rescheduleObject

Reschedule the current actor for execution later



86
87
88
89
90
91
92
93
94
# File 'lib/revactor/actor.rb', line 86

def reschedule
  if scheduler.running? 
    Fiber.yield
  else
    scheduler << current
  end
  
  current.__send__(:process_events)
end

.schedulerObject

Obtain a handle to the current Scheduler



81
82
83
# File 'lib/revactor/actor.rb', line 81

def scheduler
  Thread.current._revactor_scheduler
end

.sleep(seconds) ⇒ Object

Sleep for the specified number of seconds



97
98
99
# File 'lib/revactor/actor.rb', line 97

def sleep(seconds)
  receive { |filter| filter.after(seconds) }
end

.spawn(*args, &block) ⇒ Object

Create a new Actor with the given block and arguments

Raises:

  • (ArgumentError)


47
48
49
50
51
52
53
# File 'lib/revactor/actor.rb', line 47

def spawn(*args, &block)
  raise ArgumentError, "no block given" unless block
  
  actor = _spawn(*args, &block)
  scheduler << actor
  actor
end

Spawn an Actor and immediately link it to the current one

Raises:

  • (ArgumentError)


56
57
58
59
60
61
62
63
# File 'lib/revactor/actor.rb', line 56

def spawn_link(*args, &block)
  raise ArgumentError, "no block given" unless block
  
  actor = _spawn(*args, &block)
  current.link actor
  scheduler << actor
  actor
end

.tickObject

Run the event loop and return after processing all outstanding messages



102
103
104
# File 'lib/revactor/actor.rb', line 102

def tick
  sleep 0
end

Unlink the current Actor from another one



71
72
73
# File 'lib/revactor/actor.rb', line 71

def unlink(actor)
  current.unlink actor
end

Instance Method Details

#<<(message) ⇒ Object Also known as: send

Send a message to an actor



187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
# File 'lib/revactor/actor.rb', line 187

def <<(message)
  # Use the scheduler mailbox to send messages across threads
  unless @thread == Thread.current
    scheduler.mailbox.send(self, message)
    return message
  end
      
  # Erlang discards messages sent to dead actors, and if Erlang does it,
  # it must be the right thing to do, right?  Hooray for the Erlang 
  # cargo cult!  I think they do this because dealing with errors raised
  # from dead actors greatly overcomplicates overall error handling
  return message if dead?
  
  @mailbox << message    
  @scheduler << self
  
  message
end

#[](key) ⇒ Object

Look up value in the actor’s dictionary



169
170
171
# File 'lib/revactor/actor.rb', line 169

def [](key)
  @dictionary[key]
end

#[]=(key, value) ⇒ Object

Store a value in the actor’s dictionary



174
175
176
# File 'lib/revactor/actor.rb', line 174

def []=(key, value)
  @dictionary[key] = value
end

#dead?Boolean

Is the current actor dead?

Returns:

  • (Boolean)


184
# File 'lib/revactor/actor.rb', line 184

def dead?; @dead; end

#delete(key, &block) ⇒ Object

Delete a value from the actor’s dictionary



179
180
181
# File 'lib/revactor/actor.rb', line 179

def delete(key, &block)
  @dictionary.delete(key, &block)
end

Establish a bidirectional link to the given Actor and notify it of any system events which occur in this Actor (namely exits due to exceptions)



210
211
212
213
# File 'lib/revactor/actor.rb', line 210

def link(actor)
  actor.notify_link self
  self.notify_link actor
end

#notify_exited(actor, reason) ⇒ Object

Notify this actor that one of the Actors it’s linked to has exited



245
246
247
# File 'lib/revactor/actor.rb', line 245

def notify_exited(actor, reason)
  @events << T[:exit, actor, reason]
end

Notify this actor that it’s now linked to the given one

Raises:

  • (ArgumentError)


222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/revactor/actor.rb', line 222

def notify_link(actor)
  raise ArgumentError, "can only link to Actors" unless actor.is_a? Actor
  
  # Don't allow linking to dead actors
  raise DeadActorError, "actor is dead" if actor.dead?
  
  # Ignore circular links
  return true if actor == self
  
  # Ignore duplicate links
  return true if @links.include? actor
  
  @links << actor
  true
end

Notify this actor that it’s now unlinked from the given one



239
240
241
242
# File 'lib/revactor/actor.rb', line 239

def notify_unlink(actor)
  @links.delete(actor)
  true
end

#trap_exit=(value) ⇒ Object

Actors trapping exit do not die when an error occurs in an Actor they are linked to. Instead the exit message is sent to their regular mailbox in the form [:exit, actor, reason]. This allows certain Actors to supervise sets of others and restart them in the event of an error.

Raises:

  • (ArgumentError)


254
255
256
257
# File 'lib/revactor/actor.rb', line 254

def trap_exit=(value)
  raise ArgumentError, "must be true or false" unless value == true or value == false
  @trap_exit = value
end

#trap_exit?Boolean

Is the Actor trapping exit?

Returns:

  • (Boolean)


260
261
262
# File 'lib/revactor/actor.rb', line 260

def trap_exit?
  @trap_exit
end

Unestablish a link with the given actor



216
217
218
219
# File 'lib/revactor/actor.rb', line 216

def unlink(actor)
  actor.notify_unlink self
  self.notify_unlink actor
end