celluloid 0.9.0 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +63 -115
- data/lib/celluloid.rb +32 -7
- data/lib/celluloid/actor.rb +7 -0
- data/lib/celluloid/actor_proxy.rb +4 -4
- data/lib/celluloid/calls.rb +11 -6
- data/lib/celluloid/core_ext.rb +2 -0
- data/lib/celluloid/cpu_counter.rb +16 -0
- data/lib/celluloid/fsm.rb +14 -2
- data/lib/celluloid/group.rb +1 -0
- data/lib/celluloid/pool.rb +2 -2
- data/lib/celluloid/responses.rb +4 -4
- data/lib/celluloid/timers.rb +6 -5
- data/lib/celluloid/uuid.rb +38 -0
- data/lib/celluloid/version.rb +1 -1
- data/spec/support/actor_examples.rb +68 -6
- metadata +25 -11
data/README.md
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
![Celluloid](https://github.com/
|
1
|
+
![Celluloid](https://github.com/celluloid/celluloid/raw/master/logo.png)
|
2
2
|
=========
|
3
|
-
[![Build Status](https://secure.travis-ci.org/
|
4
|
-
[![Dependency Status](https://gemnasium.com/
|
3
|
+
[![Build Status](https://secure.travis-ci.org/celluloid/celluloid.png?branch=master)](http://travis-ci.org/celluloid/celluloid)
|
4
|
+
[![Dependency Status](https://gemnasium.com/celluloid/celluloid.png)](https://gemnasium.com/celluloid/celluloid)
|
5
5
|
|
6
6
|
> "I thought of objects being like biological cells and/or individual
|
7
7
|
> computers on a network, only able to communicate with messages"
|
@@ -11,41 +11,62 @@ Celluloid provides a simple and natural way to build fault-tolerant concurrent
|
|
11
11
|
programs in Ruby. With Celluloid, you can build systems out of concurrent
|
12
12
|
objects just as easily as you build sequential programs out of regular objects.
|
13
13
|
Recommended for any developer, including novices, Celluloid should help ease
|
14
|
-
your worries about building multithreaded Ruby programs
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
your worries about building multithreaded Ruby programs.
|
15
|
+
|
16
|
+
Much of the difficulty with building concurrent programs in Ruby arises because
|
17
|
+
the object-oriented mechanisms for structuring code, such as classes and
|
18
|
+
inheritance, are separate from the concurrency mechanisms, such as threads and
|
19
|
+
locks. Celluloid combines these into a single structure, an active object
|
20
|
+
running within a thread, called an "actor".
|
21
|
+
|
22
|
+
By combining concurrency with object oriented programming, Celluloid frees you
|
23
|
+
up from worry about where to use threads and locks. Celluloid combines them
|
24
|
+
together into a single concurrent object oriented programming model,
|
25
|
+
encapsulating state in concurrent objects and thus avoiding many of the
|
26
|
+
problems associated with multithreaded programming. Celluloid provides many
|
27
|
+
features which make concurrent programming simple, easy, and fun:
|
28
|
+
|
29
|
+
* __Automatic "deadlock-free" synchronization:__ Celluloid uses a concurrent
|
30
|
+
object model which combines method dispatch and thread synchronization.
|
31
|
+
Each actor is a concurrent object running in its own thread, and every method
|
32
|
+
invocation is wrapped in a fiber that can be suspended whenever it calls
|
33
|
+
out to other actors, and resumed when the response is available. This means
|
34
|
+
methods which are waiting for responses from other actors, external messages,
|
35
|
+
or other system events (including I/O with Celluloid::IO) can be suspended
|
36
|
+
and will never block other methods that are ready to run. This won't prevent
|
37
|
+
bugs in Celluloid, bugs in other thread-safe libraries you use, and even
|
38
|
+
certain "dangerous" features of Celluloid from causing your program to
|
39
|
+
deadlock, but in general, programs built with Celluloid will be naturally
|
40
|
+
immune to deadlocks.
|
41
|
+
|
42
|
+
* __Fault-tolerance:__ Celluloid has taken to heart many of Erlang's ideas
|
43
|
+
about fault-tolerance in order to enable self-healing applications.
|
44
|
+
The central idea: have you tried turning it off and on again? Celluloid
|
45
|
+
takes care of rebooting subcomponents of your application when they crash,
|
46
|
+
whether it's a single actor, or large (potentially multi-tiered) groups of
|
47
|
+
actors that are all interdependent. This means rather that worrying about
|
48
|
+
rescuing every last exception, you can just sit back, relax, and let parts
|
49
|
+
of your program crash, knowing Celluloid will automatically reboot them in
|
50
|
+
a clean state. Celluloid provides its own implementation of the core
|
51
|
+
fault-tolerance concepts in Erlang including [linking](https://github.com/celluloid/celluloid/wiki/Linking),
|
52
|
+
[supervisors](https://github.com/celluloid/celluloid/wiki/Supervisors),
|
53
|
+
and [supervision trees](https://github.com/celluloid/celluloid/wiki/Groups).
|
54
|
+
|
55
|
+
* __[Futures](https://github.com/celluloid/celluloid/wiki/futures):__
|
19
56
|
Ever wanted to call a method "in the background" and retrieve the
|
20
|
-
value it returns later? Celluloid futures
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
objects together into groups that will crash and restart as a group,
|
27
|
-
ensuring that after a crash all interdependent objects are in a clean and
|
28
|
-
consistent state.
|
29
|
-
|
30
|
-
Under the hood, Celluloid wraps regular objects in threads that talk to each
|
31
|
-
other using messages. These concurrent objects are called "actors". When a
|
32
|
-
caller wants another actor to execute a method, it literally sends it a
|
33
|
-
message object telling it what method to execute. The receiver listens on its
|
34
|
-
mailbox, gets the request, runs the method, and sends the caller the result.
|
35
|
-
The receiver processes messages in its inbox one-at-a-time, which means that
|
36
|
-
you don't need to worry about synchronizing access to an object's instance
|
37
|
-
variables.
|
38
|
-
|
39
|
-
In addition to that, Celluloid also gives you the ability to call methods
|
40
|
-
_asynchronously_, so the receiver to do things in the background for you
|
41
|
-
without the caller having to sit around waiting for the result.
|
57
|
+
value it returns later? Celluloid futures do just that. It's like
|
58
|
+
calling ahead to a restaurant to place an order, so they can work
|
59
|
+
on preparing your food while you're on your way to pick it up.
|
60
|
+
When you ask for a method's return value, it's returned immediately
|
61
|
+
if the method has already completed, or otherwise the current method is
|
62
|
+
suspended until the value becomes available.
|
42
63
|
|
43
64
|
You can also build distributed systems with Celluloid using its
|
44
|
-
[sister project DCell](https://github.com/
|
45
|
-
to EventMachine (
|
46
|
-
[Celluloid::IO](https://github.com/
|
65
|
+
[sister project DCell](https://github.com/celluloid/dcell). Evented IO similar
|
66
|
+
to EventMachine (with a synchronous API) is available through the
|
67
|
+
[Celluloid::IO](https://github.com/celluloid/celluloid-io) library.
|
47
68
|
|
48
|
-
[Please see the Celluloid Wiki](https://github.com/
|
69
|
+
[Please see the Celluloid Wiki](https://github.com/celluloid/celluloid/wiki)
|
49
70
|
for more detailed documentation and usage notes.
|
50
71
|
|
51
72
|
Like Celluloid? [Join the Google Group](http://groups.google.com/group/celluloid-ruby)
|
@@ -54,94 +75,21 @@ or visit us on IRC at #celluloid on freenode
|
|
54
75
|
Supported Platforms
|
55
76
|
-------------------
|
56
77
|
|
57
|
-
Celluloid works on Ruby 1.9.
|
78
|
+
Celluloid works on Ruby 1.9.3, JRuby 1.6 (in 1.9 mode), and Rubinius 2.0. JRuby
|
58
79
|
or Rubinius are the preferred platforms as they support true hardware-level
|
59
|
-
parallelism
|
60
|
-
interpreter lock (GIL).
|
80
|
+
parallelism for Ruby threads, whereas MRI/YARV is constrained by a global
|
81
|
+
interpreter lock (GIL) and can only execute one thread at a time.
|
61
82
|
|
62
83
|
To use JRuby in 1.9 mode, you'll need to pass the "--1.9" command line option
|
63
84
|
to the JRuby executable, or set the "JRUBY_OPTS=--1.9" environment variable.
|
64
85
|
|
65
86
|
Celluloid works on Rubinius in either 1.8 or 1.9 mode.
|
66
87
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
```ruby
|
73
|
-
require 'celluloid'
|
74
|
-
|
75
|
-
class Sheen
|
76
|
-
include Celluloid
|
77
|
-
|
78
|
-
def initialize(name)
|
79
|
-
@name = name
|
80
|
-
end
|
81
|
-
|
82
|
-
def set_status(status)
|
83
|
-
@status = status
|
84
|
-
end
|
85
|
-
|
86
|
-
def report
|
87
|
-
"#{@name} is #{@status}"
|
88
|
-
end
|
89
|
-
end
|
90
|
-
```
|
91
|
-
|
92
|
-
Now when you create new instances of this class, they're actually concurrent
|
93
|
-
objects, each running in their own thread:
|
94
|
-
|
95
|
-
```ruby
|
96
|
-
>> charlie = Sheen.new "Charlie Sheen"
|
97
|
-
=> #<Celluloid::Actor(Sheen:0x00000100a312d0) @name="Charlie Sheen">
|
98
|
-
>> charlie.set_status "winning!"
|
99
|
-
=> "winning!"
|
100
|
-
>> charlie.report
|
101
|
-
=> "Charlie Sheen is winning!"
|
102
|
-
>> charlie.set_status! "asynchronously winning!"
|
103
|
-
=> nil
|
104
|
-
>> charlie.report
|
105
|
-
=> "Charlie Sheen is asynchronously winning!"
|
106
|
-
```
|
107
|
-
|
108
|
-
You can call methods on this concurrent object just like you would any other
|
109
|
-
Ruby object. The Sheen#set_status method works exactly like you'd expect,
|
110
|
-
returning the last expression evaluated.
|
111
|
-
|
112
|
-
However, Celluloid's secret sauce kicks in when you call banged predicate
|
113
|
-
methods (i.e. methods ending in !). Even though the Sheen class has no
|
114
|
-
set_status! method, you can still call it. Why is this? Because bang methods
|
115
|
-
have a special meaning in Celluloid. (Note: this also means you can't define
|
116
|
-
bang methods on Celluloid classes and expect them to be callable from other
|
117
|
-
objects)
|
118
|
-
|
119
|
-
Adding a bang to the end of a method instructs Celluloid that you would like
|
120
|
-
for the given method to be called _asynchronously_. This means that rather
|
121
|
-
than the caller waiting for a response, the caller sends a message to the
|
122
|
-
concurrent object that you'd like the given method invoked, and then the
|
123
|
-
caller proceeds without waiting for a response. The concurrent object
|
124
|
-
receiving the message will then process the method call in the background.
|
125
|
-
|
126
|
-
Adding a bang to a method name is a convention in Ruby used to indicate that
|
127
|
-
the method is in some way "dangerous", and in Celluloid this is no exception.
|
128
|
-
You have no guarantees that just because you made an asynchronous call it was
|
129
|
-
ever actually invoked. Asynchronous calls will never raise an exception, even
|
130
|
-
if an exception occurs when the receiver is processing it. Worse, unhandled
|
131
|
-
exceptions will crash the receiver, and making an asynchronous call to a
|
132
|
-
crashed object will not raise an error.
|
133
|
-
|
134
|
-
However, you can still handle errors created by asynchronous calls using
|
135
|
-
two features of Celluloid called [supervisors](https://github.com/tarcieri/celluloid/wiki/supervisors)
|
136
|
-
and [linking](https://github.com/tarcieri/celluloid/wiki/linking)
|
137
|
-
|
138
|
-
[Please see the Celluloid Wiki](https://github.com/tarcieri/celluloid/wiki)
|
139
|
-
for additional usage information.
|
140
|
-
|
141
|
-
Suggested Reading
|
142
|
-
-----------------
|
143
|
-
|
144
|
-
* [Concurrent Object-Oriented Programming in Python with ATOM](http://python.org/workshops/1997-10/proceedings/atom/)
|
88
|
+
Additional Reading
|
89
|
+
------------------
|
90
|
+
|
91
|
+
* [Concurrent Object-Oriented Programming in Python with ATOM](http://python.org/workshops/1997-10/proceedings/atom/):
|
92
|
+
a similar system to Celluloid written in Python
|
145
93
|
|
146
94
|
Contributing to Celluloid
|
147
95
|
-------------------------
|
data/lib/celluloid.rb
CHANGED
@@ -3,7 +3,7 @@ require 'thread'
|
|
3
3
|
require 'timeout'
|
4
4
|
|
5
5
|
module Celluloid
|
6
|
-
SHUTDOWN_TIMEOUT =
|
6
|
+
SHUTDOWN_TIMEOUT = 120 # How long actors have to terminate
|
7
7
|
@logger = Logger.new STDERR
|
8
8
|
|
9
9
|
class << self
|
@@ -51,6 +51,18 @@ module Celluloid
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
# Generate a Universally Unique Identifier
|
55
|
+
def uuid
|
56
|
+
UUID.generate
|
57
|
+
end
|
58
|
+
|
59
|
+
# Obtain the number of CPUs in the system
|
60
|
+
def cores
|
61
|
+
CPUCounter.cores
|
62
|
+
end
|
63
|
+
alias_method :cpus, :cores
|
64
|
+
alias_method :ncpus, :cores
|
65
|
+
|
54
66
|
# Define an exception handler for actor crashes
|
55
67
|
def exception_handler(&block)
|
56
68
|
Logger.exception_handler(&block)
|
@@ -61,19 +73,25 @@ module Celluloid
|
|
61
73
|
# tree before iterating through all actors and telling them to terminate.
|
62
74
|
def shutdown
|
63
75
|
Timeout.timeout(SHUTDOWN_TIMEOUT) do
|
64
|
-
|
76
|
+
actors = Actor.all
|
77
|
+
Logger.info "Terminating #{actors.size} actors..." if actors.size > 0
|
78
|
+
|
79
|
+
# Actors cannot self-terminate, you must do it for them
|
80
|
+
terminators = actors.each do |actor|
|
65
81
|
begin
|
66
82
|
actor.future(:terminate)
|
67
83
|
rescue DeadActorError, MailboxError
|
68
84
|
end
|
69
85
|
end
|
70
86
|
|
71
|
-
|
87
|
+
terminators.each do |terminator|
|
72
88
|
begin
|
73
|
-
|
89
|
+
terminator.value
|
74
90
|
rescue DeadActorError, MailboxError
|
75
91
|
end
|
76
92
|
end
|
93
|
+
|
94
|
+
Logger.info "Shutdown completed cleanly"
|
77
95
|
end
|
78
96
|
end
|
79
97
|
end
|
@@ -86,7 +104,7 @@ module Celluloid
|
|
86
104
|
# Create a new actor
|
87
105
|
def new(*args, &block)
|
88
106
|
proxy = Actor.new(allocate).proxy
|
89
|
-
proxy.
|
107
|
+
proxy._send_(:initialize, *args, &block)
|
90
108
|
proxy
|
91
109
|
end
|
92
110
|
alias_method :spawn, :new
|
@@ -98,7 +116,7 @@ module Celluloid
|
|
98
116
|
|
99
117
|
proxy = Actor.new(allocate).proxy
|
100
118
|
current_actor.link proxy
|
101
|
-
proxy.
|
119
|
+
proxy._send_(:initialize, *args, &block)
|
102
120
|
proxy
|
103
121
|
end
|
104
122
|
alias_method :spawn_link, :new_link
|
@@ -255,6 +273,11 @@ module Celluloid
|
|
255
273
|
Thread.current[:actor].after(interval, &block)
|
256
274
|
end
|
257
275
|
|
276
|
+
# Call a block every given interval, returning a Celluloid::Timer object
|
277
|
+
def every(interval, &block)
|
278
|
+
Thread.current[:actor].every(interval, &block)
|
279
|
+
end
|
280
|
+
|
258
281
|
# Perform a blocking or computationally intensive action inside an
|
259
282
|
# asynchronous thread pool, allowing the caller to continue processing other
|
260
283
|
# messages in its mailbox in the meantime
|
@@ -280,7 +303,7 @@ module Celluloid
|
|
280
303
|
# during an async call (i.e. linking/supervisors)
|
281
304
|
end
|
282
305
|
|
283
|
-
return
|
306
|
+
return
|
284
307
|
end
|
285
308
|
|
286
309
|
super
|
@@ -291,6 +314,7 @@ require 'celluloid/version'
|
|
291
314
|
require 'celluloid/actor_proxy'
|
292
315
|
require 'celluloid/calls'
|
293
316
|
require 'celluloid/core_ext'
|
317
|
+
require 'celluloid/cpu_counter'
|
294
318
|
require 'celluloid/events'
|
295
319
|
require 'celluloid/fiber'
|
296
320
|
require 'celluloid/fsm'
|
@@ -305,6 +329,7 @@ require 'celluloid/signals'
|
|
305
329
|
require 'celluloid/task'
|
306
330
|
require 'celluloid/timers'
|
307
331
|
require 'celluloid/thread_pool'
|
332
|
+
require 'celluloid/uuid'
|
308
333
|
|
309
334
|
require 'celluloid/actor'
|
310
335
|
require 'celluloid/future'
|
data/lib/celluloid/actor.rb
CHANGED
@@ -184,6 +184,13 @@ module Celluloid
|
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
+
# Schedule a block to run at the given time
|
188
|
+
def every(interval)
|
189
|
+
@timers.add(interval, true) do
|
190
|
+
Task.new(:timer) { yield }.resume
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
187
194
|
# Sleep for the given amount of time
|
188
195
|
def sleep(interval)
|
189
196
|
if Celluloid.exclusive?
|
@@ -9,12 +9,12 @@ module Celluloid
|
|
9
9
|
@mailbox, @klass = mailbox, klass
|
10
10
|
end
|
11
11
|
|
12
|
-
def
|
13
|
-
Actor.call @mailbox, :
|
12
|
+
def _send_(meth, *args, &block)
|
13
|
+
Actor.call @mailbox, :__send__, meth, *args, &block
|
14
14
|
end
|
15
15
|
|
16
16
|
def class
|
17
|
-
Actor.call @mailbox, :
|
17
|
+
Actor.call @mailbox, :__send__, :class
|
18
18
|
end
|
19
19
|
|
20
20
|
def is_a?(klass)
|
@@ -57,7 +57,7 @@ module Celluloid
|
|
57
57
|
raise DeadActorError, "actor already terminated" unless alive?
|
58
58
|
|
59
59
|
begin
|
60
|
-
|
60
|
+
_send_ :terminate
|
61
61
|
rescue DeadActorError
|
62
62
|
# In certain cases this is thrown during termination. This is likely
|
63
63
|
# a bug in Celluloid's internals, but it shouldn't affect the caller.
|
data/lib/celluloid/calls.rb
CHANGED
@@ -12,7 +12,16 @@ module Celluloid
|
|
12
12
|
raise NoMethodError, "undefined method `#{@method}' for #{obj.inspect}"
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
begin
|
16
|
+
arity = obj.method(@method).arity
|
17
|
+
rescue NameError
|
18
|
+
# If the object claims it responds to a method, but it doesn't exist,
|
19
|
+
# then we have to assume method_missing will do what it says
|
20
|
+
@arguments.unshift(@method)
|
21
|
+
@method = :method_missing
|
22
|
+
return
|
23
|
+
end
|
24
|
+
|
16
25
|
if arity >= 0
|
17
26
|
if arguments.size != arity
|
18
27
|
raise ArgumentError, "wrong number of arguments (#{arguments.size} for #{arity})"
|
@@ -38,7 +47,7 @@ module Celluloid
|
|
38
47
|
def dispatch(obj)
|
39
48
|
begin
|
40
49
|
check_signature(obj)
|
41
|
-
rescue
|
50
|
+
rescue => ex
|
42
51
|
respond ErrorResponse.new(self, AbortError.new(ex))
|
43
52
|
return
|
44
53
|
end
|
@@ -68,10 +77,6 @@ module Celluloid
|
|
68
77
|
respond ErrorResponse.new(self, exception)
|
69
78
|
end
|
70
79
|
|
71
|
-
#######
|
72
|
-
private
|
73
|
-
#######
|
74
|
-
|
75
80
|
def respond(message)
|
76
81
|
@caller << message
|
77
82
|
rescue MailboxError
|
data/lib/celluloid/core_ext.rb
CHANGED
@@ -2,6 +2,8 @@ require 'celluloid/fiber'
|
|
2
2
|
|
3
3
|
# Monkeypatch Thread to allow lazy access to its Celluloid::Mailbox
|
4
4
|
class Thread
|
5
|
+
attr_accessor :uuid_counter, :uuid_limit
|
6
|
+
|
5
7
|
# Retrieve the mailbox for the current thread or lazily initialize it
|
6
8
|
def self.mailbox
|
7
9
|
current[:mailbox] ||= Celluloid::Mailbox.new
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rbconfig'
|
2
|
+
|
3
|
+
module Celluloid
|
4
|
+
module CPUCounter
|
5
|
+
case RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
|
6
|
+
when 'darwin'
|
7
|
+
@cores = Integer(`sysctl hw.ncpu`[/\d+/])
|
8
|
+
when 'linux'
|
9
|
+
@cores = File.read("/proc/cpuinfo").scan(/core id\s+: \d+/).uniq.size
|
10
|
+
else
|
11
|
+
@cores = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.cores; @cores; end
|
15
|
+
end
|
16
|
+
end
|
data/lib/celluloid/fsm.rb
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
module Celluloid
|
2
|
-
#
|
3
|
-
# Inspired by Erlang's gen_fsm
|
2
|
+
# Simple finite state machines with integrated Celluloid timeout support
|
3
|
+
# Inspired by Erlang's gen_fsm (http://www.erlang.org/doc/man/gen_fsm.html)
|
4
|
+
#
|
5
|
+
# Basic usage:
|
6
|
+
#
|
7
|
+
# class MyMachine
|
8
|
+
# include Celluloid::FSM # NOTE: this does NOT pull in the Celluloid module
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# Inside an actor:
|
12
|
+
#
|
13
|
+
# #
|
14
|
+
# machine = MyMachine.new(current_actor)
|
4
15
|
module FSM
|
5
16
|
class UnattachedError < StandardError; end # Not attached to an actor
|
6
17
|
|
@@ -52,6 +63,7 @@ module Celluloid
|
|
52
63
|
def initialize(actor = nil)
|
53
64
|
@state = self.class.default_state
|
54
65
|
@actor = actor
|
66
|
+
@actor ||= Celluloid.current_actor if Celluloid.actor?
|
55
67
|
end
|
56
68
|
|
57
69
|
# Obtain the current state of the FSM
|
data/lib/celluloid/group.rb
CHANGED
data/lib/celluloid/pool.rb
CHANGED
@@ -7,12 +7,12 @@ module Celluloid
|
|
7
7
|
# Takes a class of actor to pool and a hash of options:
|
8
8
|
#
|
9
9
|
# * initial_size: how many actors to eagerly create
|
10
|
-
# * max_size: maximum number of actors (default
|
10
|
+
# * max_size: maximum number of actors (default one actor per CPU core)
|
11
11
|
# * args: an array of arguments to pass to the actor's initialize
|
12
12
|
def initialize(klass, options = {})
|
13
13
|
opts = {
|
14
14
|
:initial_size => 1,
|
15
|
-
:max_size =>
|
15
|
+
:max_size => Celluloid.cores,
|
16
16
|
:args => []
|
17
17
|
}.merge(options)
|
18
18
|
|
data/lib/celluloid/responses.rb
CHANGED
@@ -2,22 +2,22 @@ module Celluloid
|
|
2
2
|
# Responses to calls
|
3
3
|
class Response
|
4
4
|
attr_reader :call, :value
|
5
|
-
|
5
|
+
|
6
6
|
def initialize(call, value)
|
7
7
|
@call, @value = call, value
|
8
8
|
end
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
# Call completed successfully
|
12
12
|
class SuccessResponse < Response; end
|
13
|
-
|
13
|
+
|
14
14
|
# Call was aborted due to caller error
|
15
15
|
class ErrorResponse < Response
|
16
16
|
def value
|
17
17
|
if super.is_a? AbortError
|
18
18
|
# Aborts are caused by caller error, so ensure they capture the
|
19
19
|
# caller's backtrace instead of the receiver's
|
20
|
-
raise super.cause.
|
20
|
+
raise super.cause.exception
|
21
21
|
else
|
22
22
|
raise super
|
23
23
|
end
|
data/lib/celluloid/timers.rb
CHANGED
@@ -6,8 +6,8 @@ module Celluloid
|
|
6
6
|
end
|
7
7
|
|
8
8
|
# Call the given block after the given interval
|
9
|
-
def add(interval, &block)
|
10
|
-
Timer.new(self, interval, block)
|
9
|
+
def add(interval, recurring = false, &block)
|
10
|
+
Timer.new(self, interval, recurring, block)
|
11
11
|
end
|
12
12
|
|
13
13
|
# Wait for the next timer and fire it
|
@@ -75,10 +75,10 @@ module Celluloid
|
|
75
75
|
# firing of timers
|
76
76
|
QUANTUM = 0.02
|
77
77
|
|
78
|
-
attr_reader :interval, :time
|
78
|
+
attr_reader :interval, :time, :recurring
|
79
79
|
|
80
|
-
def initialize(timers, interval, block)
|
81
|
-
@timers, @interval = timers, interval
|
80
|
+
def initialize(timers, interval, recurring, block)
|
81
|
+
@timers, @interval, @recurring = timers, interval, recurring
|
82
82
|
@block = block
|
83
83
|
|
84
84
|
reset
|
@@ -102,6 +102,7 @@ module Celluloid
|
|
102
102
|
|
103
103
|
# Fire the block
|
104
104
|
def fire
|
105
|
+
reset if recurring
|
105
106
|
@block.call
|
106
107
|
end
|
107
108
|
alias_method :call, :fire
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Celluloid
|
4
|
+
# Clearly Ruby doesn't have enough UUID libraries
|
5
|
+
# This one aims to be fast and simple with good support for multiple threads
|
6
|
+
# If there's a better UUID library I can use with similar multithreaded
|
7
|
+
# performance, I certainly wouldn't mind using a gem for this!
|
8
|
+
module UUID
|
9
|
+
values = SecureRandom.hex(9).match(/(.{8})(.{4})(.{3})(.{3})/)
|
10
|
+
PREFIX = "#{values[1]}-#{values[2]}-4#{values[3]}-8#{values[4]}".freeze
|
11
|
+
BLOCK_SIZE = 0x10000
|
12
|
+
|
13
|
+
@counter = 0
|
14
|
+
@counter_mutex = Mutex.new
|
15
|
+
|
16
|
+
def self.generate
|
17
|
+
thread = Thread.current
|
18
|
+
|
19
|
+
unless thread.uuid_limit
|
20
|
+
@counter_mutex.synchronize do
|
21
|
+
block_base = @counter
|
22
|
+
@counter += BLOCK_SIZE
|
23
|
+
thread.uuid_counter = block_base
|
24
|
+
thread.uuid_limit = @counter - 1
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
counter = thread.uuid_counter
|
29
|
+
if thread.uuid_counter >= thread.uuid_limit
|
30
|
+
thread.uuid_counter = thread.uuid_limit = nil
|
31
|
+
else
|
32
|
+
thread.uuid_counter += 1
|
33
|
+
end
|
34
|
+
|
35
|
+
"#{PREFIX}-#{sprintf("%012x", counter)}".freeze
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/celluloid/version.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
shared_context "a Celluloid Actor" do |included_module|
|
2
|
-
class ExampleCrash < StandardError
|
2
|
+
class ExampleCrash < StandardError
|
3
|
+
attr_accessor :foo
|
4
|
+
end
|
3
5
|
|
4
6
|
let :actor_class do
|
5
7
|
Class.new do
|
@@ -8,6 +10,7 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
8
10
|
|
9
11
|
def initialize(name)
|
10
12
|
@name = name
|
13
|
+
@delegate = [:bar]
|
11
14
|
end
|
12
15
|
|
13
16
|
def change_name(new_name)
|
@@ -30,8 +33,10 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
30
33
|
raise ExampleCrash, "the spec purposely crashed me :("
|
31
34
|
end
|
32
35
|
|
33
|
-
def crash_with_abort(reason)
|
34
|
-
|
36
|
+
def crash_with_abort(reason, foo = nil)
|
37
|
+
example_crash = ExampleCrash.new(reason)
|
38
|
+
example_crash.foo = foo
|
39
|
+
abort example_crash
|
35
40
|
end
|
36
41
|
|
37
42
|
def internal_hello
|
@@ -41,6 +46,24 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
41
46
|
def external_hello
|
42
47
|
"Hello"
|
43
48
|
end
|
49
|
+
|
50
|
+
def method_missing(method_name, *args, &block)
|
51
|
+
if delegates?(method_name)
|
52
|
+
@delegate.send method_name, *args, &block
|
53
|
+
else
|
54
|
+
super
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def respond_to?(method_name)
|
59
|
+
super || delegates?(method_name)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def delegates?(method_name)
|
65
|
+
@delegate.respond_to?(method_name)
|
66
|
+
end
|
44
67
|
end
|
45
68
|
end
|
46
69
|
|
@@ -87,6 +110,12 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
87
110
|
ponycopter.greet_by_proxy(actor).should == "Hi, I'm a ponycopter!"
|
88
111
|
end
|
89
112
|
|
113
|
+
it "properly handles method_missing" do
|
114
|
+
actor = actor_class.new "Method Missing"
|
115
|
+
actor.should respond_to(:first)
|
116
|
+
actor.first.should be == :bar
|
117
|
+
end
|
118
|
+
|
90
119
|
it "raises NoMethodError when a nonexistent method is called" do
|
91
120
|
actor = actor_class.new "Billy Bob Thornton"
|
92
121
|
|
@@ -106,9 +135,20 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
106
135
|
it "raises exceptions in the caller when abort is called, but keeps running" do
|
107
136
|
actor = actor_class.new "Al Pacino"
|
108
137
|
|
138
|
+
e = nil
|
139
|
+
line_no = nil
|
140
|
+
|
109
141
|
expect do
|
110
|
-
|
111
|
-
|
142
|
+
begin
|
143
|
+
line_no = __LINE__; actor.crash_with_abort "You die motherfucker!", :bar
|
144
|
+
rescue => ex
|
145
|
+
e = ex
|
146
|
+
raise
|
147
|
+
end
|
148
|
+
end.to raise_exception(ExampleCrash, "You die motherfucker!")
|
149
|
+
|
150
|
+
e.backtrace.any? { |line| line.include?([__FILE__, line_no].join(':')) }.should be_true # Check the backtrace is appropriate to the caller
|
151
|
+
e.foo.should be == :bar # Check the exception maintains instance variables
|
112
152
|
|
113
153
|
actor.should be_alive
|
114
154
|
end
|
@@ -379,7 +419,13 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
379
419
|
after(n) { @fired = true }
|
380
420
|
end
|
381
421
|
|
382
|
-
def
|
422
|
+
def fire_every(n)
|
423
|
+
@fired = 0
|
424
|
+
every(n) { @fired += 1 }
|
425
|
+
end
|
426
|
+
|
427
|
+
def fired?; !!@fired end
|
428
|
+
def fired; @fired end
|
383
429
|
end
|
384
430
|
end
|
385
431
|
|
@@ -412,6 +458,22 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
412
458
|
actor.should be_fired
|
413
459
|
end
|
414
460
|
|
461
|
+
it "schedules recurring timers which fire in the future" do
|
462
|
+
actor = @klass.new
|
463
|
+
|
464
|
+
interval = Celluloid::Timer::QUANTUM * 10
|
465
|
+
started_at = Time.now
|
466
|
+
|
467
|
+
timer = actor.fire_every(interval)
|
468
|
+
actor.fired.should be == 0
|
469
|
+
|
470
|
+
sleep(interval + Celluloid::Timer::QUANTUM) # wonky! #/
|
471
|
+
actor.fired.should be == 1
|
472
|
+
|
473
|
+
2.times { sleep(interval + Celluloid::Timer::QUANTUM) } # wonky! #/
|
474
|
+
actor.fired.should be == 3
|
475
|
+
end
|
476
|
+
|
415
477
|
it "cancels timers before they fire" do
|
416
478
|
actor = @klass.new
|
417
479
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-03-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
16
|
-
requirement: &
|
16
|
+
requirement: &70275106519080 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ! '>='
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *70275106519080
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
requirement: &
|
27
|
+
requirement: &70275106518620 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ! '>='
|
@@ -32,10 +32,21 @@ dependencies:
|
|
32
32
|
version: '0'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *70275106518620
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: guard-rspec
|
38
|
+
requirement: &70275106518200 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *70275106518200
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: benchmark_suite
|
38
|
-
requirement: &
|
49
|
+
requirement: &70275106517780 !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
51
|
requirements:
|
41
52
|
- - ! '>='
|
@@ -43,8 +54,9 @@ dependencies:
|
|
43
54
|
version: '0'
|
44
55
|
type: :development
|
45
56
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
-
description: Celluloid
|
57
|
+
version_requirements: *70275106517780
|
58
|
+
description: Celluloid enables people to build concurrent programs out of concurrent
|
59
|
+
objects just as easily as they build sequential programs out of sequential objects
|
48
60
|
email:
|
49
61
|
- tony.arcieri@gmail.com
|
50
62
|
executables: []
|
@@ -56,6 +68,7 @@ files:
|
|
56
68
|
- lib/celluloid/actor_proxy.rb
|
57
69
|
- lib/celluloid/calls.rb
|
58
70
|
- lib/celluloid/core_ext.rb
|
71
|
+
- lib/celluloid/cpu_counter.rb
|
59
72
|
- lib/celluloid/events.rb
|
60
73
|
- lib/celluloid/fiber.rb
|
61
74
|
- lib/celluloid/fsm.rb
|
@@ -74,11 +87,12 @@ files:
|
|
74
87
|
- lib/celluloid/task.rb
|
75
88
|
- lib/celluloid/thread_pool.rb
|
76
89
|
- lib/celluloid/timers.rb
|
90
|
+
- lib/celluloid/uuid.rb
|
77
91
|
- lib/celluloid/version.rb
|
78
92
|
- lib/celluloid.rb
|
79
93
|
- spec/support/actor_examples.rb
|
80
94
|
- spec/support/mailbox_examples.rb
|
81
|
-
homepage: https://github.com/
|
95
|
+
homepage: https://github.com/celluloid/celluloid
|
82
96
|
licenses:
|
83
97
|
- MIT
|
84
98
|
post_install_message:
|
@@ -102,6 +116,6 @@ rubyforge_project:
|
|
102
116
|
rubygems_version: 1.8.10
|
103
117
|
signing_key:
|
104
118
|
specification_version: 3
|
105
|
-
summary:
|
119
|
+
summary: Actor-based concurrent object framework for Ruby
|
106
120
|
test_files: []
|
107
121
|
has_rdoc:
|