Class: Actor

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

Defined Under Namespace

Classes: DeadActorError, Filter

Constant Summary collapse

ANY =
Object.new
@@registered_lock =
Queue.new
@@registered =
{}

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeActor

Returns a new instance of Actor.



193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/girl_friday/actor.rb', line 193

def initialize
  @lock = Queue.new

  @filter = nil
  @ready = Queue.new
  @action = nil
  @message = nil

  @mailbox = []
  @interrupts = []
  @links = []
  @alive = true
  @exit_reason = nil
  @trap_exit = false
  @thread = Thread.current

  @lock << nil

  if block_given?
    watchdog { yield self }
  else
    Thread.new { watchdog { @thread.join } }
  end
end

Class Method Details

._unregister(actor) ⇒ Object

:nodoc:



183
184
185
186
187
188
189
190
# File 'lib/girl_friday/actor.rb', line 183

def _unregister(actor) #:nodoc:
  @@registered_lock.pop
  begin
    @@registered.delete_if { |n, a| actor.equal? a }
  ensure
    @@registered_lock << nil
  end
end

.check_for_interruptObject

Polls for exit notifications



92
93
94
95
# File 'lib/girl_friday/actor.rb', line 92

def check_for_interrupt
  current._check_for_interrupt
  self
end

.currentObject



57
58
59
# File 'lib/girl_friday/actor.rb', line 57

def current
  Thread.current[:__current_actor__] ||= private_new
end

Link the current Actor to another one.



118
119
120
121
122
123
# File 'lib/girl_friday/actor.rb', line 118

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

.lookup(name) ⇒ Object Also known as: []

Lookup a locally named service

Raises:

  • (ArgumentError)


152
153
154
155
156
157
158
159
160
# File 'lib/girl_friday/actor.rb', line 152

def lookup(name)
  raise ArgumentError, "name must be a symbol" unless Symbol === name
  @@registered_lock.pop
  begin
    @@registered[name]
  ensure
    @@registered_lock << nil
  end
end

.receiveObject

Waits until a matching message is received in the current actor’s mailbox, and executes the appropriate action. May be interrupted by exit notifications.



100
101
102
103
104
105
106
107
108
# File 'lib/girl_friday/actor.rb', line 100

def receive #:yields: filter
  filter = Filter.new
  if block_given?
    yield filter
  else
    filter.when(ANY) { |m| m }
  end
  current._receive(filter)
end

.register(name, actor) ⇒ Object Also known as: []=

Register an Actor locally as a named service

Raises:

  • (ArgumentError)


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/girl_friday/actor.rb', line 164

def register(name, actor)
  raise ArgumentError, "name must be a symbol" unless Symbol === name
  unless actor.nil? or actor.is_a?(Actor)
    raise ArgumentError, "only actors may be registered"
  end

  @@registered_lock.pop
  begin
    if actor.nil?
      @@registered.delete(name)
    else
      @@registered[name] = actor
    end
  ensure
    @@registered_lock << nil
  end
end

.send_exit(recipient, reason) ⇒ Object

Send a “fake” exit notification to another actor, as if the current actor had exited with reason



112
113
114
115
# File 'lib/girl_friday/actor.rb', line 112

def send_exit(recipient, reason)
  recipient.notify_exited(current, reason)
  self
end

.spawn(*args, &block) ⇒ Object Also known as: new

Spawn a new Actor that will run in its own thread

Raises:

  • (ArgumentError)


62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/girl_friday/actor.rb', line 62

def spawn(*args, &block)
  raise ArgumentError, "no block given" unless block
  spawned = Queue.new
  Thread.new do
    private_new do |actor|
      Thread.current[:__current_actor__] = actor
      spawned << actor
      block.call(*args)
    end
  end
  spawned.pop
end

Atomically spawn an actor and link it to the current actor



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

def spawn_link(*args, &block)
  current = self.current
  link_complete = Queue.new
  spawn do
    begin
      Actor.link(current)
    ensure
      link_complete << Actor.current
    end
    block.call(*args)
  end
  link_complete.pop
end

.trap_exitObject Also known as: trap_exit?

Is the Actor trapping exit?



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

def trap_exit
  current._trap_exit
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. Setting the trap flag may be interrupted by pending exit notifications.



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

def trap_exit=(value)
  current._trap_exit = value
  self
end

Unlink the current Actor from another one



126
127
128
129
130
131
# File 'lib/girl_friday/actor.rb', line 126

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

Instance Method Details

#_check_for_interruptObject

:nodoc:



241
242
243
244
245
246
247
248
249
# File 'lib/girl_friday/actor.rb', line 241

def _check_for_interrupt #:nodoc:
  check_thread
  @lock.pop
  begin
    raise @interrupts.shift unless @interrupts.empty?
  ensure
    @lock << nil
  end
end

#_receive(filter) ⇒ Object

:nodoc:



251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/girl_friday/actor.rb', line 251

def _receive(filter) #:nodoc:
  check_thread

  action = nil
  message = nil
  timed_out = false

  @lock.pop
  begin
    raise @interrupts.shift unless @interrupts.empty?

    if @mailbox.size > 0
      message = @mailbox.shift
      action = filter.action_for(message)
      unless action
        @mailbox << message
        for i in 1...(@mailbox.size)
          message = @mailbox[i]
          action = filter.action_for(message)
          if action
            @mailbox.delete_at(i)
            break
          end
        end
      end
    end

    unless action
      @filter = filter
      @lock << nil
      begin
        if filter.timeout?
          timed_out = @ready.receive_timeout(filter.timeout) == false # TODO Broken!
        else
          @ready.pop
        end
      ensure
        @lock.pop
      end

      if !timed_out and @interrupts.empty?
        action = @action
        message = @message
      else
        @mailbox << @message if @action
      end

      @action = nil
      @message = nil

      raise @interrupts.shift unless @interrupts.empty?
    end
  ensure
    @lock << nil
  end

  if timed_out
    filter.timeout_action.call
  else
    action.call message
  end
end

#_trap_exitObject

:nodoc:



421
422
423
424
425
426
427
428
429
# File 'lib/girl_friday/actor.rb', line 421

def _trap_exit #:nodoc:
  check_thread
  @lock.pop
  begin
    @trap_exit
  ensure
    @lock << nil
  end
end

#_trap_exit=(value) ⇒ Object

:nodoc:



410
411
412
413
414
415
416
417
418
419
# File 'lib/girl_friday/actor.rb', line 410

def _trap_exit=(value) #:nodoc:
  check_thread
  @lock.pop
  begin
    raise @interrupts.shift unless @interrupts.empty?
    @trap_exit = !!value
  ensure
    @lock << nil
  end
end

#notify_exited(actor, reason) ⇒ Object

Notify this actor that one of the Actors it’s linked to has exited; this is not intended to be used directly except by actor implementations. Most users will want to use Actor.send_exit instead.



352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/girl_friday/actor.rb', line 352

def notify_exited(actor, reason)
  exit_message = nil
  @lock.pop
  begin
    return self unless @alive
    @links.delete(actor)
    if @trap_exit
      exit_message = DeadActorError.new(actor, reason)
    elsif reason
      @interrupts << DeadActorError.new(actor, reason)
      if @filter
        @filter = nil
        @ready << nil
      end
    end
  ensure
    @lock << nil
  end
  send exit_message if exit_message
  self
end

Notify this actor that it’s now linked to the given one; this is not intended to be used directly except by actor implementations. Most users will want to use Actor.link instead.



318
319
320
321
322
323
324
325
326
327
328
329
330
331
# File 'lib/girl_friday/actor.rb', line 318

def notify_link(actor)
  @lock.pop
  alive = nil
  exit_reason = nil
  begin
    alive = @alive
    exit_reason = @exit_reason
    @links << actor if alive and not @links.include? actor
  ensure
    @lock << nil
  end
  actor.notify_exited(self, exit_reason) unless alive
  self
end

Notify this actor that it’s now unlinked from the given one; this is not intended to be used directly except by actor implementations. Most users will want to use Actor.unlink instead.



337
338
339
340
341
342
343
344
345
346
# File 'lib/girl_friday/actor.rb', line 337

def notify_unlink(actor)
  @lock.pop
  begin
    return self unless @alive
    @links.delete(actor)
  ensure
    @lock << nil
  end
  self
end

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



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/girl_friday/actor.rb', line 218

def send(message)
  @lock.pop
  begin
    return self unless @alive
    if @filter
      @action = @filter.action_for(message)
      if @action
        @filter = nil
        @message = message
        @ready << nil
      else
        @mailbox << message
      end
    else
      @mailbox << message
    end
  ensure
    @lock << nil
  end
  self
end