celluloid 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +17 -10
- data/lib/celluloid/actor.rb +25 -3
- data/lib/celluloid/actor_proxy.rb +10 -2
- data/lib/celluloid/calls.rb +55 -17
- data/lib/celluloid/version.rb +1 -1
- metadata +2 -2
data/README.md
CHANGED
@@ -6,13 +6,16 @@ Celluloid
|
|
6
6
|
> computers on a network, only able to communicate with messages"
|
7
7
|
> _--Alan Kay, creator of Smalltalk, on the meaning of "object oriented programming"_
|
8
8
|
|
9
|
-
Celluloid is a concurrent object framework for Ruby inspired by Erlang and
|
10
|
-
Actor model. Celluloid gives you thread-backed objects that run
|
11
|
-
providing the simplicity of Ruby objects for the most common
|
12
|
-
also the ability to call methods _asynchronously_, allowing
|
13
|
-
things in the background while the caller carries on
|
14
|
-
These concurrent objects are called "actors". Actors are
|
15
|
-
the kind of object you're typically used to working
|
9
|
+
Celluloid is a concurrent object framework for Ruby inspired by Erlang and
|
10
|
+
the Actor model. Celluloid gives you thread-backed objects that run
|
11
|
+
concurrently, providing the simplicity of Ruby objects for the most common
|
12
|
+
use cases, but also the ability to call methods _asynchronously_, allowing
|
13
|
+
the receiver to do things in the background while the caller carries on
|
14
|
+
with its business. These concurrent objects are called "actors". Actors are
|
15
|
+
somewhere in between the kind of object you're typically used to working
|
16
|
+
with and a network service.
|
17
|
+
|
18
|
+
Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
|
16
19
|
|
17
20
|
Supported Platforms
|
18
21
|
-------------------
|
@@ -87,7 +90,8 @@ exceptions will crash the receiver, and making an asynchronous call to a
|
|
87
90
|
crashed object will not raise an error.
|
88
91
|
|
89
92
|
However, you can still handle errors created by asynchronous calls using
|
90
|
-
two features of Celluloid called _supervisors_ and _linking_.
|
93
|
+
two features of Celluloid called _supervisors_ and _linking_. See the
|
94
|
+
corresponding sections below for more information.
|
91
95
|
|
92
96
|
Futures
|
93
97
|
-------
|
@@ -107,7 +111,10 @@ report method from the charlie object used in the above example using a future:
|
|
107
111
|
The call to charlie.future immediately returns a Celluloid::Future object,
|
108
112
|
regardless of how long it takes to execute the "report" method. To obtain
|
109
113
|
the result of the call to "report", we call the _value_ method of the
|
110
|
-
future object. This call will block until the
|
114
|
+
future object. This call will block until the value returned from the method
|
115
|
+
call is available (i.e. the method has finished executing). If an exception
|
116
|
+
occured during the method call, the call to future.value will reraise the
|
117
|
+
same exception.
|
111
118
|
|
112
119
|
Futures also allow you to background the computation of any block:
|
113
120
|
|
@@ -367,4 +374,4 @@ Contributing to Celluloid
|
|
367
374
|
Copyright
|
368
375
|
---------
|
369
376
|
|
370
|
-
Copyright (c) 2011 Tony Arcieri. See LICENSE.txt for further details.
|
377
|
+
Copyright (c) 2011 Tony Arcieri. See LICENSE.txt for further details.
|
data/lib/celluloid/actor.rb
CHANGED
@@ -60,8 +60,7 @@ module Celluloid
|
|
60
60
|
def spawn_link(*args, &block)
|
61
61
|
current_actor = Thread.current[:actor]
|
62
62
|
raise NotActorError, "can't link outside actor context" unless current_actor
|
63
|
-
|
64
|
-
# FIXME: this is a bit repetitive with the code above
|
63
|
+
|
65
64
|
actor = allocate
|
66
65
|
proxy = actor.__start_actor
|
67
66
|
current_actor.link actor
|
@@ -139,6 +138,30 @@ module Celluloid
|
|
139
138
|
def wait(name)
|
140
139
|
@_signals.wait name
|
141
140
|
end
|
141
|
+
|
142
|
+
#
|
143
|
+
# Async calls
|
144
|
+
#
|
145
|
+
|
146
|
+
def method_missing(meth, *args, &block)
|
147
|
+
# bang methods are async calls
|
148
|
+
if meth.to_s.match(/!$/)
|
149
|
+
unbanged_meth = meth.to_s.sub(/!$/, '')
|
150
|
+
|
151
|
+
begin
|
152
|
+
@_mailbox << AsyncCall.new(@_mailbox, unbanged_meth, args, block)
|
153
|
+
rescue MailboxError
|
154
|
+
# Silently swallow asynchronous calls to dead actors. There's no way
|
155
|
+
# to reliably generate DeadActorErrors for async calls, so users of
|
156
|
+
# async calls should find other ways to deal with actors dying
|
157
|
+
# during an async call (i.e. linking/supervisors)
|
158
|
+
end
|
159
|
+
|
160
|
+
return # casts are async and return immediately
|
161
|
+
end
|
162
|
+
|
163
|
+
super
|
164
|
+
end
|
142
165
|
end
|
143
166
|
|
144
167
|
# Internal methods not intended as part of the public API
|
@@ -242,7 +265,6 @@ module Celluloid
|
|
242
265
|
end
|
243
266
|
|
244
267
|
# Log errors when an actor crashes
|
245
|
-
# FIXME: This should probably thunk to a real logger
|
246
268
|
def __log_error(ex, message = "#{self.class} crashed!")
|
247
269
|
message << "\n#{ex.class}: #{ex.to_s}\n"
|
248
270
|
message << ex.backtrace.join("\n")
|
@@ -92,12 +92,20 @@ module Celluloid
|
|
92
92
|
msg.is_a? Response and msg.call == call
|
93
93
|
end
|
94
94
|
end
|
95
|
-
|
95
|
+
|
96
96
|
case response
|
97
97
|
when SuccessResponse
|
98
98
|
response.value
|
99
99
|
when ErrorResponse
|
100
|
-
|
100
|
+
ex = response.value
|
101
|
+
|
102
|
+
if ex.is_a? AbortError
|
103
|
+
# Aborts are caused by caller error, so ensure they capture the
|
104
|
+
# caller's backtrace instead of the receiver's
|
105
|
+
raise ex.cause.class.new(ex.cause.message)
|
106
|
+
else
|
107
|
+
raise ex
|
108
|
+
end
|
101
109
|
else
|
102
110
|
raise "don't know how to handle #{response.class} messages!"
|
103
111
|
end
|
data/lib/celluloid/calls.rb
CHANGED
@@ -6,50 +6,88 @@ module Celluloid
|
|
6
6
|
def initialize(caller, method, arguments, block)
|
7
7
|
@caller, @method, @arguments, @block = caller, method, arguments, block
|
8
8
|
end
|
9
|
+
|
10
|
+
def check_signature(obj)
|
11
|
+
unless obj.respond_to? @method
|
12
|
+
raise NoMethodError, "undefined method `#{@method}' for #{obj.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
arity = obj.method(@method).arity
|
16
|
+
if arity >= 0
|
17
|
+
if arguments.size != arity
|
18
|
+
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{arity})"
|
19
|
+
end
|
20
|
+
elsif arity < -1
|
21
|
+
mandatory_args = -arity - 1
|
22
|
+
if arguments.size < mandatory_args
|
23
|
+
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{mandatory_args})"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
9
27
|
end
|
10
28
|
|
11
29
|
# Synchronous calls wait for a response
|
12
30
|
class SyncCall < Call
|
13
31
|
def dispatch(obj)
|
14
|
-
|
15
|
-
|
16
|
-
|
32
|
+
begin
|
33
|
+
check_signature(obj)
|
34
|
+
rescue Exception => ex
|
35
|
+
respond ErrorResponse.new(self, AbortError.new(ex))
|
17
36
|
return
|
18
37
|
end
|
19
38
|
|
20
39
|
begin
|
21
40
|
result = obj.send @method, *@arguments, &@block
|
22
|
-
rescue AbortError => exception
|
23
|
-
# Aborting indicates a protocol error on the part of the caller
|
24
|
-
# It should crash the caller, but the exception isn't reraised
|
25
|
-
@caller << ErrorResponse.new(self, exception.cause)
|
26
|
-
return
|
27
41
|
rescue Exception => exception
|
28
42
|
# Exceptions that occur during synchronous calls are reraised in the
|
29
43
|
# context of the caller
|
30
|
-
|
44
|
+
respond ErrorResponse.new(self, exception)
|
31
45
|
|
32
|
-
|
33
|
-
|
46
|
+
if exception.is_a? AbortError
|
47
|
+
# Aborting indicates a protocol error on the part of the caller
|
48
|
+
# It should crash the caller, but the exception isn't reraised
|
49
|
+
return
|
50
|
+
else
|
51
|
+
# Otherwise, it's a bug in this actor and should be reraised
|
52
|
+
raise exception
|
53
|
+
end
|
34
54
|
end
|
35
|
-
|
36
|
-
|
55
|
+
|
56
|
+
respond SuccessResponse.new(self, result)
|
37
57
|
true
|
38
58
|
end
|
39
59
|
|
40
60
|
def cleanup
|
41
61
|
exception = DeadActorError.new("attempted to call a dead actor")
|
42
|
-
|
62
|
+
respond ErrorResponse.new(self, exception)
|
43
63
|
end
|
64
|
+
|
65
|
+
#######
|
66
|
+
private
|
67
|
+
#######
|
68
|
+
|
69
|
+
def respond(message)
|
70
|
+
@caller << message
|
71
|
+
rescue MailboxError
|
72
|
+
# It's possible the caller exited or crashed before we could send a
|
73
|
+
# response to them.
|
74
|
+
end
|
44
75
|
end
|
45
76
|
|
46
77
|
# Asynchronous calls don't wait for a response
|
47
78
|
class AsyncCall < Call
|
48
79
|
def dispatch(obj)
|
49
|
-
|
50
|
-
|
80
|
+
begin
|
81
|
+
check_signature(obj)
|
82
|
+
rescue Exception => ex
|
83
|
+
obj.__log_error ex, "#{obj.class}: async call failed!"
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
obj.send(@method, *@arguments, &@block)
|
88
|
+
rescue AbortError => ex
|
51
89
|
# Swallow aborted async calls, as they indicate the caller made a mistake
|
52
|
-
#
|
90
|
+
obj.__log_error ex, "#{obj.class}: async call aborted!"
|
53
91
|
end
|
54
92
|
end
|
55
93
|
end
|
data/lib/celluloid/version.rb
CHANGED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: celluloid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.2.
|
5
|
+
version: 0.2.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Tony Arcieri
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-
|
13
|
+
date: 2011-09-28 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|