celluloid 0.6.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -329,6 +329,35 @@ working, freshly-restarted version.
329
329
  The main use of the registry is for interfacing with actors that are
330
330
  automatically restarted by supervisors when they crash.
331
331
 
332
+ Applications
333
+ ------------
334
+
335
+ Celluloid provides a DSL for describing all of the actors in a given
336
+ application. This lets you start a group of actors in one swoop and
337
+ also provides an additional level of supervision: applications supervise
338
+ the supervisors of all the actors in your system, an approach known
339
+ as supervision trees.
340
+
341
+ Define Celluloid::Applications with the following syntax:
342
+
343
+ class MyApplication < Celluloid::Application
344
+ supervise MyActor, :as => :my_actor
345
+ supervise AnotherActor, :as => :another_actor
346
+ end
347
+
348
+ This will start the MyActor and AnotherActor actors under a supervisor and
349
+ automatically register them as Celluloid::Actor[:my_actor] and
350
+ Celluloid::Actor[:another_actor].
351
+
352
+ To launch your application, do:
353
+
354
+ MyApplication.run
355
+
356
+ This launches your application in the foreground. To launch in in the
357
+ background, do:
358
+
359
+ MyApplication.run!
360
+
332
361
  Signaling
333
362
  ---------
334
363
 
@@ -458,9 +487,11 @@ Here are a few rules you can follow to keep this from happening:
458
487
 
459
488
  1. ***NEVER RETURN SELF*** (or pass self as an argument to other actors): in
460
489
  cases where you want to pass an actor around to other actors or threads,
461
- use Celluloid.current_actor. If you grab the latest master of Celluloid
462
- off of Github, you can just use the #current_actor method when you are
463
- inside of an actor itself.
490
+ use Celluloid.current_actor, or if you're within an actor itself, you can
491
+ just call the #current_actor method. If you really need to get ahold of
492
+ "self" in order to add instance-specific behavior, e.g for metaprogramming
493
+ purposes or adding stubs during tests, call MyActor#wrapped_object to
494
+ obtain the actual object an actor is wrapping.
464
495
 
465
496
  2. Don't mutate the state of objects you've sent in calls to other actors:
466
497
  This means you must think about data in one of two different ways: either
@@ -1,6 +1,5 @@
1
1
  require 'logger'
2
2
  require 'thread'
3
- require 'celluloid/fibers_are_hard'
4
3
 
5
4
  module Celluloid
6
5
  @logger = Logger.new STDERR
@@ -9,8 +8,7 @@ module Celluloid
9
8
  attr_accessor :logger # Thread-safe logger class
10
9
 
11
10
  def included(klass)
12
- klass.send :extend, ClassMethods
13
- klass.send :include, Linking
11
+ klass.send :extend, ClassMethods
14
12
  end
15
13
 
16
14
  # Are we currently inside of an actor?
@@ -20,10 +18,10 @@ module Celluloid
20
18
 
21
19
  # Obtain the currently running actor (if one exists)
22
20
  def current_actor
23
- actor = Thread.current[:actor_proxy]
21
+ actor = Thread.current[:actor]
24
22
  raise NotActorError, "not in actor scope" unless actor
25
23
 
26
- actor
24
+ actor.proxy
27
25
  end
28
26
 
29
27
  # Receive an asynchronous message
@@ -32,33 +30,13 @@ module Celluloid
32
30
  if actor
33
31
  actor.receive(&block)
34
32
  else
35
- Thread.current.mailbox.receive(&block)
36
- end
37
- end
38
-
39
- # Create a fiber that participates in the Celluloid protocol
40
- def fiber(*args)
41
- actor = Thread.current[:actor]
42
- proxy = Thread.current[:actor_proxy]
43
- mailbox = Thread.current[:mailbox]
44
-
45
- Fiber.new do
46
- Thread.current[:actor] = actor
47
- Thread.current[:actor_proxy] = proxy
48
- Thread.current[:mailbox] = mailbox
49
-
50
- yield(*args)
33
+ Thread.mailbox.receive(&block)
51
34
  end
52
35
  end
53
36
 
54
37
  # Resume a fiber that participates in the Celluloid protocol
55
38
  def resume_fiber(fiber, value = nil)
56
- actor = Thread.current[:actor]
57
- if actor
58
- actor.run_fiber fiber, value
59
- else
60
- fiber.resume value
61
- end
39
+ fiber.resume value
62
40
  end
63
41
  end
64
42
 
@@ -74,7 +52,7 @@ module Celluloid
74
52
 
75
53
  # Create a new actor and link to the current one
76
54
  def new_link(*args, &block)
77
- current_actor = Thread.current[:actor]
55
+ current_actor = Celluloid.current_actor
78
56
  raise NotActorError, "can't link outside actor context" unless current_actor
79
57
 
80
58
  proxy = Celluloid::Actor.new(allocate).proxy
@@ -165,6 +143,36 @@ module Celluloid
165
143
  # be shared with at least the actor thread. Tread carefully.
166
144
  def wrapped_object; self; end
167
145
 
146
+ # Obtain the Celluloid::Links for this actor
147
+ def links
148
+ Thread.current[:actor].links
149
+ end
150
+
151
+ # Link this actor to another, allowing it to crash or react to errors
152
+ def link(actor)
153
+ actor.notify_link current_actor
154
+ notify_link actor
155
+ end
156
+
157
+ # Remove links to another actor
158
+ def unlink(actor)
159
+ actor.notify_unlink current_actor
160
+ notify_unlink actor
161
+ end
162
+
163
+ def notify_link(actor)
164
+ links << actor
165
+ end
166
+
167
+ def notify_unlink(actor)
168
+ links.delete actor
169
+ end
170
+
171
+ # Is this actor linked to another?
172
+ def linked_to?(actor)
173
+ Thread.current[:actor].links.include? actor
174
+ end
175
+
168
176
  # Receive an asynchronous message via the actor protocol
169
177
  def receive(&block)
170
178
  Celluloid.receive(&block)
@@ -207,7 +215,8 @@ require 'celluloid/actor_proxy'
207
215
  require 'celluloid/calls'
208
216
  require 'celluloid/core_ext'
209
217
  require 'celluloid/events'
210
- require 'celluloid/linking'
218
+ require 'celluloid/fiber'
219
+ require 'celluloid/links'
211
220
  require 'celluloid/mailbox'
212
221
  require 'celluloid/receivers'
213
222
  require 'celluloid/registry'
@@ -20,7 +20,6 @@ module Celluloid
20
20
  # messages.
21
21
  class Actor
22
22
  extend Registry
23
- include Linking
24
23
 
25
24
  attr_reader :proxy
26
25
  attr_reader :links
@@ -28,8 +27,7 @@ module Celluloid
28
27
 
29
28
  # Invoke a method on the given actor via its mailbox
30
29
  def self.call(mailbox, meth, *args, &block)
31
- our_mailbox = Thread.current.mailbox
32
- call = SyncCall.new(our_mailbox, meth, args, block)
30
+ call = SyncCall.new(Thread.mailbox, meth, args, block)
33
31
 
34
32
  begin
35
33
  mailbox << call
@@ -42,8 +40,8 @@ module Celluloid
42
40
  response = Fiber.yield(call)
43
41
  else
44
42
  # Otherwise we're inside a normal thread, so block
45
- response = our_mailbox.receive do |msg|
46
- msg.is_a? Response and msg.call_id == call.id
43
+ response = Thread.mailbox.receive do |msg|
44
+ msg.respond_to?(:call_id) and msg.call_id == call.id
47
45
  end
48
46
  end
49
47
 
@@ -52,9 +50,8 @@ module Celluloid
52
50
 
53
51
  # Invoke a method asynchronously on an actor via its mailbox
54
52
  def self.async(mailbox, meth, *args, &block)
55
- our_mailbox = Thread.current.mailbox
56
53
  begin
57
- mailbox << AsyncCall.new(our_mailbox, meth, args, block)
54
+ mailbox << AsyncCall.new(Thread.mailbox, meth, args, block)
58
55
  rescue MailboxError
59
56
  # Silently swallow asynchronous calls to dead actors. There's no way
60
57
  # to reliably generate DeadActorErrors for async calls, so users of
@@ -76,14 +73,13 @@ module Celluloid
76
73
  @links = Links.new
77
74
  @signals = Signals.new
78
75
  @receivers = Receivers.new
79
- @proxy = ActorProxy.new(@mailbox)
76
+ @proxy = ActorProxy.new(@mailbox, self.class.to_s)
80
77
  @running = true
81
78
  @pending_calls = {}
82
79
 
83
80
  @thread = Pool.get do
84
- Thread.current[:actor] = self
85
- Thread.current[:actor_proxy] = @proxy
86
- Thread.current[:mailbox] = @mailbox
81
+ Thread.current[:actor] = self
82
+ Thread.current[:mailbox] = @mailbox
87
83
 
88
84
  run
89
85
  end
@@ -121,8 +117,7 @@ module Celluloid
121
117
  begin
122
118
  message = @mailbox.receive
123
119
  rescue ExitEvent => exit_event
124
- fiber = Celluloid.fiber { handle_exit_event exit_event; nil }
125
- run_fiber fiber
120
+ Celluloid::Fiber.new { handle_exit_event exit_event; nil }.resume
126
121
  retry
127
122
  end
128
123
 
@@ -140,29 +135,22 @@ module Celluloid
140
135
  Pool.put @thread
141
136
  end
142
137
 
143
- # Run a method, handling when its Fiber is suspended
144
- def run_fiber(fiber, value = nil)
145
- result = fiber.resume value
146
- if result.is_a? Celluloid::Call
147
- @pending_calls[result.id] = fiber if fiber.alive?
148
- elsif result
149
- warning = "non-call returned from fiber: #{result.class}"
150
- Celluloid.logger.debug warning if Celluloid.logger
151
- end
152
- nil
138
+ # Register a fiber waiting for the response to a Celluloid::Call
139
+ def register_fiber(call, fiber)
140
+ raise ArgumentError, "attempted to register a dead fiber" unless fiber.alive?
141
+ @pending_calls[call.id] = fiber
153
142
  end
154
143
 
155
144
  # Handle an incoming message
156
145
  def handle_message(message)
157
146
  case message
158
147
  when Call
159
- fiber = Celluloid.fiber { message.dispatch(@subject); nil }
160
- run_fiber fiber
148
+ Celluloid::Fiber.new { message.dispatch(@subject); nil }.resume
161
149
  when Response
162
150
  fiber = @pending_calls.delete(message.call_id)
163
151
 
164
152
  if fiber
165
- run_fiber fiber, message
153
+ fiber.resume message
166
154
  else
167
155
  warning = "spurious response to call #{message.call_id}"
168
156
  Celluloid.logger.debug if Celluloid.logger
@@ -13,6 +13,10 @@ module Celluloid
13
13
  Actor.call @mailbox, :send, meth, *args, &block
14
14
  end
15
15
 
16
+ def class
17
+ Actor.call @mailbox, :send, :class
18
+ end
19
+
16
20
  def respond_to?(meth)
17
21
  Actor.call @mailbox, :respond_to?, meth
18
22
  end
@@ -87,7 +87,7 @@ module Celluloid
87
87
  obj.send(@method, *@arguments, &@block)
88
88
  rescue AbortError => ex
89
89
  # Swallow aborted async calls, as they indicate the caller made a mistake
90
- obj.log_error ex, "#{obj.class}: async call aborted!"
90
+ log_error ex, "#{obj.class}: async call aborted!"
91
91
  end
92
92
 
93
93
  def log_error(ex, message)
@@ -1,13 +1,7 @@
1
1
  # Monkeypatch Thread to allow lazy access to its Celluloid::Mailbox
2
2
  class Thread
3
- # Retrieve the current mailbox or lazily initialize it
4
- def mailbox
5
- self[:mailbox] || begin
6
- if Thread.current != self
7
- raise "attempt to access an uninitialized mailbox"
8
- end
9
-
10
- self[:mailbox] = Celluloid::Mailbox.new
11
- end
3
+ # Retrieve the mailbox for the current thread or lazily initialize it
4
+ def self.mailbox
5
+ current[:mailbox] ||= Celluloid::Mailbox.new
12
6
  end
13
7
  end
@@ -1,4 +1,4 @@
1
- # Let's go shopping!
1
+ # Every time I look at this code a little part of me dies...
2
2
  begin
3
3
  require 'fiber'
4
4
  rescue LoadError => ex
@@ -31,3 +31,33 @@ rescue LoadError => ex
31
31
  raise ex
32
32
  end
33
33
  end
34
+
35
+ module Celluloid
36
+ class Fiber < ::Fiber
37
+ def initialize(*args)
38
+ actor = Thread.current[:actor]
39
+ mailbox = Thread.current[:mailbox]
40
+
41
+ super do
42
+ Thread.current[:actor] = actor
43
+ Thread.current[:mailbox] = mailbox
44
+
45
+ yield(*args)
46
+ end
47
+ end
48
+
49
+ def resume(value = nil)
50
+ result = super
51
+ actor = Thread.current[:actor]
52
+ return result unless actor
53
+
54
+ if result.is_a? Celluloid::Call
55
+ actor.register_fiber result, self
56
+ elsif result
57
+ warning = "non-call returned from fiber: #{result.class}"
58
+ Celluloid.logger.debug warning if Celluloid.logger
59
+ end
60
+ nil
61
+ end
62
+ end
63
+ end
@@ -32,7 +32,7 @@ module Celluloid
32
32
  [[readers, @readers], [writers, @writers]].each do |ios, registered|
33
33
  ios.each do |io|
34
34
  fiber = registered.delete io
35
- Celluloid.resume_fiber(fiber) if fiber
35
+ fiber.resume if fiber
36
36
  end
37
37
  end
38
38
  end
@@ -0,0 +1,61 @@
1
+ require 'thread'
2
+
3
+ module Celluloid
4
+ # Thread safe storage of inter-actor links
5
+ class Links
6
+ include Enumerable
7
+
8
+ def initialize
9
+ @links = {}
10
+ @lock = Mutex.new
11
+ end
12
+
13
+ # Add an actor to the current links
14
+ def <<(actor)
15
+ @lock.synchronize do
16
+ @links[actor.mailbox.address] = actor
17
+ end
18
+ actor
19
+ end
20
+
21
+ # Do links include the given actor?
22
+ def include?(actor)
23
+ @lock.synchronize do
24
+ @links.has_key? actor.mailbox.address
25
+ end
26
+ end
27
+
28
+ # Remove an actor from the links
29
+ def delete(actor)
30
+ @lock.synchronize do
31
+ @links.delete actor.mailbox.address
32
+ end
33
+ actor
34
+ end
35
+
36
+ # Iterate through all links
37
+ def each
38
+ @lock.synchronize do
39
+ @links.each { |_, actor| yield(actor) }
40
+ end
41
+ end
42
+
43
+ # Map across links
44
+ def map
45
+ result = []
46
+ each { |actor| result << yield(actor) }
47
+ result
48
+ end
49
+
50
+ # Send an event message to all actors
51
+ def send_event(event)
52
+ each { |actor| actor.mailbox.system_event event }
53
+ end
54
+
55
+ # Generate a string representation
56
+ def inspect
57
+ links = self.map(&:inspect).join(',')
58
+ "#<#{self.class}[#{links}]>"
59
+ end
60
+ end
61
+ end
@@ -9,6 +9,9 @@ module Celluloid
9
9
  class Mailbox
10
10
  include Enumerable
11
11
 
12
+ # A unique address at which this mailbox can be found
13
+ alias_method :address, :object_id
14
+
12
15
  def initialize
13
16
  @messages = []
14
17
  @lock = Mutex.new
@@ -26,7 +26,7 @@ module Celluloid
26
26
 
27
27
  if handler
28
28
  fiber, _ = @handlers.delete_at handler
29
- Celluloid.resume_fiber fiber, message
29
+ fiber.resume message
30
30
  true
31
31
  else
32
32
  false
@@ -18,10 +18,8 @@ module Celluloid
18
18
  fibers = @waiting.delete name
19
19
  return unless fibers
20
20
 
21
- fibers.each do |fiber|
22
- Celluloid.resume_fiber fiber, value
23
- end
24
- true
21
+ fibers.each { |fiber| fiber.resume value }
22
+ value
25
23
  end
26
24
  end
27
25
  end
@@ -26,11 +26,16 @@ module Celluloid
26
26
 
27
27
  begin
28
28
  @actor = @klass.new_link(*@args, &@block)
29
- rescue
29
+ rescue => ex
30
30
  failures += 1
31
31
  if failures >= start_attempts
32
32
  failures = 0
33
- Celluloid.logger.warn "#{@klass} is crashing on initialize repeatedly, sleeping for #{sleep_interval} seconds"
33
+
34
+ warning = "#{@klass} is crashing on initialize repeatedly, sleeping for #{sleep_interval} seconds\n"
35
+ warning << "#{ex.class}: #{ex}\n "
36
+ warning << "#{ex.backtrace.join("\n ")}"
37
+
38
+ Celluloid.logger.warn warning if Celluloid.logger
34
39
  sleep sleep_interval
35
40
  end
36
41
  retry
@@ -1,4 +1,4 @@
1
1
  module Celluloid
2
- VERSION = '0.6.0'
2
+ VERSION = '0.6.1'
3
3
  def self.version; VERSION; end
4
4
  end
metadata CHANGED
@@ -1,38 +1,38 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: celluloid
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.6.0
4
+ version: 0.6.1
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Tony Arcieri
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-19 00:00:00.000000000Z
12
+ date: 2011-11-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
16
- version_requirements: &2056 !ruby/object:Gem::Requirement
16
+ requirement: &70242345718900 !ruby/object:Gem::Requirement
17
+ none: false
17
18
  requirements:
18
19
  - - ! '>='
19
20
  - !ruby/object:Gem::Version
20
21
  version: '0'
21
- none: false
22
- requirement: *2056
23
- prerelease: false
24
22
  type: :development
23
+ prerelease: false
24
+ version_requirements: *70242345718900
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- version_requirements: &2074 !ruby/object:Gem::Requirement
27
+ requirement: &70242345718300 !ruby/object:Gem::Requirement
28
+ none: false
28
29
  requirements:
29
30
  - - ! '>='
30
31
  - !ruby/object:Gem::Version
31
32
  version: 2.7.0
32
- none: false
33
- requirement: *2074
34
- prerelease: false
35
33
  type: :development
34
+ prerelease: false
35
+ version_requirements: *70242345718300
36
36
  description: Celluloid is a concurrent object framework inspired by the Actor Model
37
37
  email:
38
38
  - tony@medioh.com
@@ -41,7 +41,6 @@ extensions: []
41
41
  extra_rdoc_files: []
42
42
  files:
43
43
  - README.md
44
- - lib/celluloid.rb
45
44
  - lib/celluloid/actor.rb
46
45
  - lib/celluloid/actor_pool.rb
47
46
  - lib/celluloid/actor_proxy.rb
@@ -49,10 +48,13 @@ files:
49
48
  - lib/celluloid/calls.rb
50
49
  - lib/celluloid/core_ext.rb
51
50
  - lib/celluloid/events.rb
52
- - lib/celluloid/fibers_are_hard.rb
51
+ - lib/celluloid/fiber.rb
53
52
  - lib/celluloid/future.rb
53
+ - lib/celluloid/io/mailbox.rb
54
+ - lib/celluloid/io/reactor.rb
55
+ - lib/celluloid/io/waker.rb
54
56
  - lib/celluloid/io.rb
55
- - lib/celluloid/linking.rb
57
+ - lib/celluloid/links.rb
56
58
  - lib/celluloid/mailbox.rb
57
59
  - lib/celluloid/receivers.rb
58
60
  - lib/celluloid/registry.rb
@@ -62,34 +64,31 @@ files:
62
64
  - lib/celluloid/supervisor.rb
63
65
  - lib/celluloid/tcp_server.rb
64
66
  - lib/celluloid/version.rb
65
- - lib/celluloid/io/mailbox.rb
66
- - lib/celluloid/io/reactor.rb
67
- - lib/celluloid/io/waker.rb
67
+ - lib/celluloid.rb
68
68
  homepage: https://github.com/tarcieri/celluloid
69
69
  licenses:
70
70
  - MIT
71
- post_install_message:
71
+ post_install_message:
72
72
  rdoc_options: []
73
73
  require_paths:
74
74
  - lib
75
75
  required_ruby_version: !ruby/object:Gem::Requirement
76
+ none: false
76
77
  requirements:
77
78
  - - ! '>='
78
79
  - !ruby/object:Gem::Version
79
80
  version: '0'
80
- none: false
81
81
  required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
82
83
  requirements:
83
84
  - - ! '>='
84
85
  - !ruby/object:Gem::Version
85
86
  version: 1.3.6
86
- none: false
87
87
  requirements: []
88
- rubyforge_project:
89
- rubygems_version: 1.8.9
90
- signing_key:
88
+ rubyforge_project:
89
+ rubygems_version: 1.8.10
90
+ signing_key:
91
91
  specification_version: 3
92
92
  summary: Celluloid is a concurrent object framework inspired by the Actor Model
93
93
  test_files: []
94
- has_rdoc:
95
- ...
94
+ has_rdoc:
@@ -1,89 +0,0 @@
1
- require 'set'
2
- require 'thread'
3
-
4
- module Celluloid
5
- # Thread safe storage of inter-actor links
6
- class Links
7
- include Enumerable
8
-
9
- def initialize
10
- @links = Set.new
11
- @lock = Mutex.new
12
- end
13
-
14
- # Add an actor to the current links
15
- def <<(actor)
16
- @lock.synchronize do
17
- @links << actor
18
- end
19
- actor
20
- end
21
-
22
- # Do links include the given actor?
23
- def include?(actor)
24
- @lock.synchronize do
25
- @links.include? actor
26
- end
27
- end
28
-
29
- # Remove an actor from the links
30
- def delete(actor)
31
- @lock.synchronize do
32
- @links.delete actor
33
- end
34
- actor
35
- end
36
-
37
- # Iterate through all links
38
- def each(&block)
39
- @lock.synchronize do
40
- @links.each(&block)
41
- end
42
- end
43
-
44
- # Send an event message to all actors
45
- def send_event(event)
46
- each { |actor| actor.mailbox.system_event event }
47
- end
48
-
49
- # Generate a string representation
50
- def inspect
51
- @lock.synchronize do
52
- links = @links.to_a.map { |l| "#{l.class}:#{l.object_id}" }.join(',')
53
- "#<#{self.class}[#{links}]>"
54
- end
55
- end
56
- end
57
-
58
- # Support for linking actors together so they can crash or react to errors
59
- module Linking
60
- # Link this actor to another, allowing it to crash or react to errors
61
- def link(actor)
62
- current_actor = Thread.current[:actor]
63
-
64
- actor.notify_link(current_actor.proxy)
65
- current_actor.notify_link(actor)
66
- end
67
-
68
- # Remove links to another actor
69
- def unlink(actor)
70
- current_actor = Thread.current[:actor]
71
-
72
- actor.notify_unlink(current_actor.proxy)
73
- current_actor.notify_unlink(actor)
74
- end
75
-
76
- def notify_link(actor)
77
- Thread.current[:actor].links << actor
78
- end
79
-
80
- def notify_unlink(actor)
81
- Thread.current[:actor].links.delete actor
82
- end
83
-
84
- # Is this actor linked to another?
85
- def linked_to?(actor)
86
- Thread.current[:actor].links.include? actor
87
- end
88
- end
89
- end