celluloid 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
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