celluloid 0.16.0 → 0.17.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of celluloid might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGES.md +333 -0
- data/README.md +1 -1
- data/culture/CODE_OF_CONDUCT.md +28 -0
- data/culture/Gemfile +9 -0
- data/culture/README.md +22 -0
- data/culture/Rakefile +5 -0
- data/culture/SYNC.md +70 -0
- data/culture/celluloid-culture.gemspec +18 -0
- data/culture/gems/README.md +39 -0
- data/culture/gems/dependencies.yml +78 -0
- data/culture/gems/loader.rb +101 -0
- data/culture/rubocop/README.md +38 -0
- data/culture/rubocop/lint.yml +8 -0
- data/culture/rubocop/metrics.yml +15 -0
- data/culture/rubocop/rubocop.yml +4 -0
- data/culture/rubocop/style.yml +48 -0
- data/culture/spec/gems_spec.rb +2 -0
- data/culture/spec/spec_helper.rb +0 -0
- data/culture/spec/sync_spec.rb +2 -0
- data/culture/sync.rb +56 -0
- data/culture/tasks/rspec.rake +5 -0
- data/culture/tasks/rubocop.rake +2 -0
- data/examples/basic_usage.rb +49 -0
- data/examples/futures.rb +38 -0
- data/examples/ring.rb +61 -0
- data/examples/simple_pmap.rb +14 -0
- data/examples/timers.rb +72 -0
- data/lib/celluloid.rb +142 -127
- data/lib/celluloid/actor.rb +47 -41
- data/lib/celluloid/actor_system.rb +75 -22
- data/lib/celluloid/autostart.rb +1 -1
- data/lib/celluloid/backported.rb +2 -0
- data/lib/celluloid/call/async.rb +16 -0
- data/lib/celluloid/call/block.rb +22 -0
- data/lib/celluloid/call/sync.rb +70 -0
- data/lib/celluloid/calls.rb +25 -114
- data/lib/celluloid/cell.rb +32 -20
- data/lib/celluloid/condition.rb +3 -3
- data/lib/celluloid/core_ext.rb +1 -1
- data/lib/celluloid/current.rb +2 -0
- data/lib/celluloid/deprecate.rb +18 -0
- data/lib/celluloid/exceptions.rb +1 -1
- data/lib/celluloid/fiber.rb +3 -3
- data/lib/celluloid/future.rb +7 -6
- data/lib/celluloid/group.rb +65 -0
- data/lib/celluloid/group/manager.rb +27 -0
- data/lib/celluloid/group/pool.rb +125 -0
- data/lib/celluloid/group/spawner.rb +71 -0
- data/lib/celluloid/logging.rb +5 -5
- data/lib/celluloid/mailbox.rb +14 -13
- data/lib/celluloid/mailbox/evented.rb +76 -0
- data/lib/celluloid/notices.rb +15 -0
- data/lib/celluloid/proxies.rb +12 -0
- data/lib/celluloid/proxy/abstract.rb +24 -0
- data/lib/celluloid/proxy/actor.rb +46 -0
- data/lib/celluloid/proxy/async.rb +36 -0
- data/lib/celluloid/proxy/block.rb +31 -0
- data/lib/celluloid/proxy/cell.rb +76 -0
- data/lib/celluloid/proxy/future.rb +40 -0
- data/lib/celluloid/proxy/sync.rb +44 -0
- data/lib/celluloid/rspec.rb +9 -10
- data/lib/celluloid/system_events.rb +16 -15
- data/lib/celluloid/{tasks.rb → task.rb} +21 -21
- data/lib/celluloid/task/fibered.rb +45 -0
- data/lib/celluloid/task/threaded.rb +59 -0
- data/lib/celluloid/test.rb +1 -1
- data/lib/celluloid/thread.rb +6 -1
- data/lib/celluloid/version.rb +3 -0
- data/spec/celluloid/actor_spec.rb +2 -2
- data/spec/celluloid/actor_system_spec.rb +35 -21
- data/spec/celluloid/block_spec.rb +3 -5
- data/spec/celluloid/calls_spec.rb +33 -11
- data/spec/celluloid/condition_spec.rb +16 -13
- data/spec/celluloid/evented_mailbox_spec.rb +1 -31
- data/spec/celluloid/future_spec.rb +13 -10
- data/spec/celluloid/group/elastic_spec.rb +0 -0
- data/spec/celluloid/group/manager_spec.rb +0 -0
- data/spec/celluloid/group/pool_spec.rb +8 -0
- data/spec/celluloid/group/spawner_spec.rb +8 -0
- data/spec/celluloid/mailbox/evented_spec.rb +27 -0
- data/spec/celluloid/mailbox_spec.rb +1 -3
- data/spec/celluloid/misc/leak_spec.rb +73 -0
- data/spec/celluloid/task/fibered_spec.rb +5 -0
- data/spec/celluloid/task/threaded_spec.rb +5 -0
- data/spec/celluloid/timer_spec.rb +14 -16
- data/spec/deprecate/actor_system_spec.rb +72 -0
- data/spec/deprecate/block_spec.rb +52 -0
- data/spec/deprecate/calls_spec.rb +57 -0
- data/spec/deprecate/evented_mailbox_spec.rb +34 -0
- data/spec/deprecate/future_spec.rb +32 -0
- data/spec/deprecate/internal_pool_spec.rb +4 -0
- data/spec/shared/actor_examples.rb +1237 -0
- data/spec/shared/group_examples.rb +121 -0
- data/{lib/celluloid/rspec → spec/shared}/mailbox_examples.rb +20 -17
- data/{lib/celluloid/rspec → spec/shared}/task_examples.rb +9 -8
- data/spec/spec_helper.rb +72 -16
- data/spec/support/coverage.rb +4 -0
- data/spec/support/crash_checking.rb +68 -0
- data/spec/support/debugging.rb +31 -0
- data/spec/support/env.rb +16 -0
- data/{lib/celluloid/rspec/example_actor_class.rb → spec/support/examples/actor_class.rb} +21 -2
- data/spec/support/examples/evented_mailbox_class.rb +27 -0
- data/spec/support/includer.rb +9 -0
- data/spec/support/logging.rb +63 -0
- data/spec/support/loose_threads.rb +65 -0
- data/spec/support/reset_class_variables.rb +27 -0
- data/spec/support/sleep_and_wait.rb +14 -0
- data/spec/support/split_logs.rb +1 -0
- data/spec/support/stubbing.rb +14 -0
- metadata +255 -95
- data/lib/celluloid/call_chain.rb +0 -13
- data/lib/celluloid/cpu_counter.rb +0 -34
- data/lib/celluloid/evented_mailbox.rb +0 -73
- data/lib/celluloid/fsm.rb +0 -186
- data/lib/celluloid/handlers.rb +0 -41
- data/lib/celluloid/internal_pool.rb +0 -159
- data/lib/celluloid/legacy.rb +0 -9
- data/lib/celluloid/links.rb +0 -36
- data/lib/celluloid/logger.rb +0 -93
- data/lib/celluloid/logging/incident.rb +0 -21
- data/lib/celluloid/logging/incident_logger.rb +0 -129
- data/lib/celluloid/logging/incident_reporter.rb +0 -48
- data/lib/celluloid/logging/log_event.rb +0 -20
- data/lib/celluloid/logging/ring_buffer.rb +0 -65
- data/lib/celluloid/method.rb +0 -32
- data/lib/celluloid/notifications.rb +0 -83
- data/lib/celluloid/pool_manager.rb +0 -146
- data/lib/celluloid/probe.rb +0 -73
- data/lib/celluloid/properties.rb +0 -24
- data/lib/celluloid/proxies/abstract_proxy.rb +0 -20
- data/lib/celluloid/proxies/actor_proxy.rb +0 -38
- data/lib/celluloid/proxies/async_proxy.rb +0 -31
- data/lib/celluloid/proxies/block_proxy.rb +0 -29
- data/lib/celluloid/proxies/cell_proxy.rb +0 -68
- data/lib/celluloid/proxies/future_proxy.rb +0 -35
- data/lib/celluloid/proxies/sync_proxy.rb +0 -36
- data/lib/celluloid/receivers.rb +0 -63
- data/lib/celluloid/registry.rb +0 -57
- data/lib/celluloid/responses.rb +0 -44
- data/lib/celluloid/rspec/actor_examples.rb +0 -1054
- data/lib/celluloid/signals.rb +0 -23
- data/lib/celluloid/stack_dump.rb +0 -133
- data/lib/celluloid/supervision_group.rb +0 -169
- data/lib/celluloid/supervisor.rb +0 -22
- data/lib/celluloid/task_set.rb +0 -49
- data/lib/celluloid/tasks/task_fiber.rb +0 -43
- data/lib/celluloid/tasks/task_thread.rb +0 -53
- data/lib/celluloid/thread_handle.rb +0 -50
- data/lib/celluloid/uuid.rb +0 -38
- data/spec/celluloid/cpu_counter_spec.rb +0 -82
- data/spec/celluloid/fsm_spec.rb +0 -107
- data/spec/celluloid/internal_pool_spec.rb +0 -52
- data/spec/celluloid/links_spec.rb +0 -45
- data/spec/celluloid/logging/ring_buffer_spec.rb +0 -38
- data/spec/celluloid/notifications_spec.rb +0 -120
- data/spec/celluloid/pool_spec.rb +0 -92
- data/spec/celluloid/probe_spec.rb +0 -121
- data/spec/celluloid/properties_spec.rb +0 -42
- data/spec/celluloid/registry_spec.rb +0 -64
- data/spec/celluloid/stack_dump_spec.rb +0 -64
- data/spec/celluloid/supervision_group_spec.rb +0 -65
- data/spec/celluloid/supervisor_spec.rb +0 -103
- data/spec/celluloid/tasks/task_fiber_spec.rb +0 -5
- data/spec/celluloid/tasks/task_thread_spec.rb +0 -5
- data/spec/celluloid/thread_handle_spec.rb +0 -26
- data/spec/celluloid/uuid_spec.rb +0 -11
@@ -1,68 +0,0 @@
|
|
1
|
-
module Celluloid
|
2
|
-
# A proxy object returned from Celluloid::Actor.new/new_link which converts
|
3
|
-
# the normal Ruby method protocol into an inter-actor message protocol
|
4
|
-
class CellProxy < SyncProxy
|
5
|
-
# Used for reflecting on proxy objects themselves
|
6
|
-
def __class__; CellProxy; end
|
7
|
-
|
8
|
-
def initialize(actor_proxy, mailbox, klass)
|
9
|
-
super(mailbox, klass)
|
10
|
-
@actor_proxy = actor_proxy
|
11
|
-
@sync_proxy = SyncProxy.new(mailbox, klass)
|
12
|
-
@async_proxy = AsyncProxy.new(mailbox, klass)
|
13
|
-
@future_proxy = FutureProxy.new(mailbox, klass)
|
14
|
-
end
|
15
|
-
|
16
|
-
def _send_(meth, *args, &block)
|
17
|
-
method_missing :__send__, meth, *args, &block
|
18
|
-
end
|
19
|
-
|
20
|
-
def inspect
|
21
|
-
method_missing :inspect
|
22
|
-
rescue DeadActorError
|
23
|
-
"#<Celluloid::CellProxy(#{@klass}) dead>"
|
24
|
-
end
|
25
|
-
|
26
|
-
def method(name)
|
27
|
-
Method.new(self, name)
|
28
|
-
end
|
29
|
-
|
30
|
-
alias_method :sync, :method_missing
|
31
|
-
|
32
|
-
# Obtain an async proxy or explicitly invoke a named async method
|
33
|
-
def async(method_name = nil, *args, &block)
|
34
|
-
if method_name
|
35
|
-
@async_proxy.method_missing method_name, *args, &block
|
36
|
-
else
|
37
|
-
@async_proxy
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# Obtain a future proxy or explicitly invoke a named future method
|
42
|
-
def future(method_name = nil, *args, &block)
|
43
|
-
if method_name
|
44
|
-
@future_proxy.method_missing method_name, *args, &block
|
45
|
-
else
|
46
|
-
@future_proxy
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def alive?
|
51
|
-
@actor_proxy.alive?
|
52
|
-
end
|
53
|
-
|
54
|
-
def thread
|
55
|
-
@actor_proxy.thread
|
56
|
-
end
|
57
|
-
|
58
|
-
# Terminate the associated actor
|
59
|
-
def terminate
|
60
|
-
@actor_proxy.terminate
|
61
|
-
end
|
62
|
-
|
63
|
-
# Terminate the associated actor asynchronously
|
64
|
-
def terminate!
|
65
|
-
@actor_proxy.terminate!
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
module Celluloid
|
2
|
-
# A proxy which creates future calls to an actor
|
3
|
-
class FutureProxy < AbstractProxy
|
4
|
-
attr_reader :mailbox
|
5
|
-
|
6
|
-
# Used for reflecting on proxy objects themselves
|
7
|
-
def __class__; FutureProxy; end
|
8
|
-
|
9
|
-
def initialize(mailbox, klass)
|
10
|
-
@mailbox, @klass = mailbox, klass
|
11
|
-
end
|
12
|
-
|
13
|
-
def inspect
|
14
|
-
"#<Celluloid::FutureProxy(#{@klass})>"
|
15
|
-
end
|
16
|
-
|
17
|
-
def method_missing(meth, *args, &block)
|
18
|
-
unless @mailbox.alive?
|
19
|
-
raise DeadActorError, "attempted to call a dead actor"
|
20
|
-
end
|
21
|
-
|
22
|
-
if block_given?
|
23
|
-
# FIXME: nicer exception
|
24
|
-
raise "Cannot use blocks with futures yet"
|
25
|
-
end
|
26
|
-
|
27
|
-
future = Future.new
|
28
|
-
call = SyncCall.new(future, meth, args, block)
|
29
|
-
|
30
|
-
@mailbox << call
|
31
|
-
|
32
|
-
future
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
module Celluloid
|
2
|
-
# A proxy which sends synchronous calls to an actor
|
3
|
-
class SyncProxy < AbstractProxy
|
4
|
-
attr_reader :mailbox
|
5
|
-
|
6
|
-
# Used for reflecting on proxy objects themselves
|
7
|
-
def __class__; SyncProxy; end
|
8
|
-
|
9
|
-
def initialize(mailbox, klass)
|
10
|
-
@mailbox, @klass = mailbox, klass
|
11
|
-
end
|
12
|
-
|
13
|
-
def inspect
|
14
|
-
"#<Celluloid::SyncProxy(#{@klass})>"
|
15
|
-
end
|
16
|
-
|
17
|
-
def respond_to?(meth, include_private = false)
|
18
|
-
__class__.instance_methods.include?(meth) || method_missing(:respond_to?, meth, include_private)
|
19
|
-
end
|
20
|
-
|
21
|
-
def method_missing(meth, *args, &block)
|
22
|
-
unless @mailbox.alive?
|
23
|
-
raise DeadActorError, "attempted to call a dead actor"
|
24
|
-
end
|
25
|
-
|
26
|
-
if @mailbox == ::Thread.current[:celluloid_mailbox]
|
27
|
-
args.unshift meth
|
28
|
-
meth = :__send__
|
29
|
-
end
|
30
|
-
|
31
|
-
call = SyncCall.new(::Celluloid.mailbox, meth, args, block)
|
32
|
-
@mailbox << call
|
33
|
-
call.value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/lib/celluloid/receivers.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require 'set'
|
2
|
-
|
3
|
-
require 'timers'
|
4
|
-
|
5
|
-
module Celluloid
|
6
|
-
# Allow methods to directly interact with the actor protocol
|
7
|
-
class Receivers
|
8
|
-
def initialize(timers)
|
9
|
-
@receivers = Set.new
|
10
|
-
@timers = timers
|
11
|
-
end
|
12
|
-
|
13
|
-
# Receive an asynchronous message
|
14
|
-
def receive(timeout = nil, &block)
|
15
|
-
if Celluloid.exclusive?
|
16
|
-
Celluloid.mailbox.receive(timeout, &block)
|
17
|
-
else
|
18
|
-
receiver = Receiver.new block
|
19
|
-
|
20
|
-
if timeout
|
21
|
-
receiver.timer = @timers.after(timeout) do
|
22
|
-
@receivers.delete receiver
|
23
|
-
receiver.resume
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
@receivers << receiver
|
28
|
-
Task.suspend :receiving
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Handle incoming messages
|
33
|
-
def handle_message(message)
|
34
|
-
receiver = @receivers.find { |r| r.match(message) }
|
35
|
-
return unless receiver
|
36
|
-
|
37
|
-
@receivers.delete receiver
|
38
|
-
receiver.timer.cancel if receiver.timer
|
39
|
-
receiver.resume message
|
40
|
-
message
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# Methods blocking on a call to receive
|
45
|
-
class Receiver
|
46
|
-
attr_accessor :timer
|
47
|
-
|
48
|
-
def initialize(block)
|
49
|
-
@block = block
|
50
|
-
@task = Task.current
|
51
|
-
@timer = nil
|
52
|
-
end
|
53
|
-
|
54
|
-
# Match a message with this receiver's block
|
55
|
-
def match(message)
|
56
|
-
@block ? @block.call(message) : true
|
57
|
-
end
|
58
|
-
|
59
|
-
def resume(message = nil)
|
60
|
-
@task.resume message
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
data/lib/celluloid/registry.rb
DELETED
@@ -1,57 +0,0 @@
|
|
1
|
-
require 'thread'
|
2
|
-
|
3
|
-
module Celluloid
|
4
|
-
# The Registry allows us to refer to specific actors by human-meaningful names
|
5
|
-
class Registry
|
6
|
-
def initialize
|
7
|
-
@registry = {}
|
8
|
-
@registry_lock = Mutex.new
|
9
|
-
end
|
10
|
-
|
11
|
-
# Register an Actor
|
12
|
-
def []=(name, actor)
|
13
|
-
actor_singleton = class << actor; self; end
|
14
|
-
unless actor_singleton.ancestors.include? AbstractProxy
|
15
|
-
raise TypeError, "not an actor"
|
16
|
-
end
|
17
|
-
|
18
|
-
@registry_lock.synchronize do
|
19
|
-
@registry[name.to_sym] = actor
|
20
|
-
end
|
21
|
-
|
22
|
-
actor.mailbox << NamingRequest.new(name.to_sym)
|
23
|
-
end
|
24
|
-
|
25
|
-
# Retrieve an actor by name
|
26
|
-
def [](name)
|
27
|
-
@registry_lock.synchronize do
|
28
|
-
@registry[name.to_sym]
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
alias_method :get, :[]
|
33
|
-
alias_method :set, :[]=
|
34
|
-
|
35
|
-
def delete(name)
|
36
|
-
@registry_lock.synchronize do
|
37
|
-
@registry.delete name.to_sym
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
# List all registered actors by name
|
42
|
-
def names
|
43
|
-
@registry_lock.synchronize { @registry.keys }
|
44
|
-
end
|
45
|
-
|
46
|
-
# removes and returns all registered actors as a hash of `name => actor`
|
47
|
-
# can be used in testing to clear the registry
|
48
|
-
def clear
|
49
|
-
hash = nil
|
50
|
-
@registry_lock.synchronize do
|
51
|
-
hash = @registry.dup
|
52
|
-
@registry.clear
|
53
|
-
end
|
54
|
-
hash
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/celluloid/responses.rb
DELETED
@@ -1,44 +0,0 @@
|
|
1
|
-
module Celluloid
|
2
|
-
# Responses to calls
|
3
|
-
class Response
|
4
|
-
attr_reader :call, :value
|
5
|
-
|
6
|
-
def initialize(call, value)
|
7
|
-
@call, @value = call, value
|
8
|
-
end
|
9
|
-
|
10
|
-
def dispatch
|
11
|
-
@call.task.resume self
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
# Call completed successfully
|
16
|
-
class SuccessResponse < Response; end
|
17
|
-
|
18
|
-
# Call was aborted due to sender error
|
19
|
-
class ErrorResponse < Response
|
20
|
-
def value
|
21
|
-
ex = super
|
22
|
-
ex = ex.cause if ex.is_a? AbortError
|
23
|
-
|
24
|
-
if ex.backtrace
|
25
|
-
ex.backtrace << "(celluloid):0:in `remote procedure call'"
|
26
|
-
ex.backtrace.concat(caller)
|
27
|
-
end
|
28
|
-
|
29
|
-
raise ex
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class BlockResponse
|
34
|
-
def initialize(call, result)
|
35
|
-
@call = call
|
36
|
-
@result = result
|
37
|
-
end
|
38
|
-
|
39
|
-
def dispatch
|
40
|
-
@call.task.resume(@result)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
@@ -1,1054 +0,0 @@
|
|
1
|
-
shared_examples "a Celluloid Actor" do |included_module|
|
2
|
-
describe "using Fibers" do
|
3
|
-
include_examples "Celluloid::Actor examples", included_module, Celluloid::TaskFiber
|
4
|
-
end
|
5
|
-
describe "using Threads" do
|
6
|
-
include_examples "Celluloid::Actor examples", included_module, Celluloid::TaskThread
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
|
11
|
-
class ExampleCrash < StandardError
|
12
|
-
attr_accessor :foo
|
13
|
-
end
|
14
|
-
|
15
|
-
let(:actor_class) { ExampleActorClass.create(included_module, task_klass) }
|
16
|
-
|
17
|
-
it "returns the actor's class, not the proxy's" do
|
18
|
-
actor = actor_class.new "Troy McClure"
|
19
|
-
actor.class.should eq(actor_class)
|
20
|
-
end
|
21
|
-
|
22
|
-
it "compares with the actor's class in a case statement" do
|
23
|
-
case actor_class.new("Troy McClure")
|
24
|
-
when actor_class
|
25
|
-
true
|
26
|
-
else
|
27
|
-
false
|
28
|
-
end.should be_true
|
29
|
-
end
|
30
|
-
|
31
|
-
it "can be stored in hashes" do
|
32
|
-
actor = actor_class.new "Troy McClure"
|
33
|
-
actor.hash.should_not eq(Kernel.hash)
|
34
|
-
actor.object_id.should_not eq(Kernel.object_id)
|
35
|
-
end
|
36
|
-
|
37
|
-
it "implements respond_to? correctly" do
|
38
|
-
actor = actor_class.new 'Troy McClure'
|
39
|
-
actor.should respond_to(:alive?)
|
40
|
-
end
|
41
|
-
|
42
|
-
it "supports synchronous calls" do
|
43
|
-
actor = actor_class.new "Troy McClure"
|
44
|
-
actor.greet.should eq("Hi, I'm Troy McClure")
|
45
|
-
end
|
46
|
-
|
47
|
-
it "supports synchronous calls with blocks" do
|
48
|
-
actor = actor_class.new "Blocky Ralboa"
|
49
|
-
|
50
|
-
block_executed = false
|
51
|
-
actor.run { block_executed = true }
|
52
|
-
block_executed.should be_true
|
53
|
-
end
|
54
|
-
|
55
|
-
it "supports synchronous calls via #method" do
|
56
|
-
method = actor_class.new("Troy McClure").method(:greet)
|
57
|
-
method.call.should eq("Hi, I'm Troy McClure")
|
58
|
-
end
|
59
|
-
|
60
|
-
it "supports #arity calls via #method" do
|
61
|
-
method = actor_class.new("Troy McClure").method(:greet)
|
62
|
-
method.arity.should be(0)
|
63
|
-
|
64
|
-
method = actor_class.new("Troy McClure").method(:change_name)
|
65
|
-
method.arity.should be(1)
|
66
|
-
end
|
67
|
-
|
68
|
-
it "supports #name calls via #method" do
|
69
|
-
method = actor_class.new("Troy McClure").method(:greet)
|
70
|
-
method.name.should == :greet
|
71
|
-
end
|
72
|
-
|
73
|
-
it "supports #parameters via #method" do
|
74
|
-
method = actor_class.new("Troy McClure").method(:greet)
|
75
|
-
method.parameters.should == []
|
76
|
-
|
77
|
-
method = actor_class.new("Troy McClure").method(:change_name)
|
78
|
-
method.parameters.should == [[:req, :new_name]]
|
79
|
-
end
|
80
|
-
|
81
|
-
it "supports future(:method) syntax for synchronous future calls" do
|
82
|
-
actor = actor_class.new "Troy McClure"
|
83
|
-
future = actor.future :greet
|
84
|
-
future.value.should eq("Hi, I'm Troy McClure")
|
85
|
-
end
|
86
|
-
|
87
|
-
it "supports future.method syntax for synchronous future calls" do
|
88
|
-
actor = actor_class.new "Troy McClure"
|
89
|
-
future = actor.future.greet
|
90
|
-
future.value.should eq("Hi, I'm Troy McClure")
|
91
|
-
end
|
92
|
-
|
93
|
-
it "handles circular synchronous calls" do
|
94
|
-
klass = Class.new do
|
95
|
-
include included_module
|
96
|
-
task_class task_klass
|
97
|
-
|
98
|
-
def greet_by_proxy(actor)
|
99
|
-
actor.greet
|
100
|
-
end
|
101
|
-
|
102
|
-
def to_s
|
103
|
-
"a ponycopter!"
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
ponycopter = klass.new
|
108
|
-
actor = actor_class.new ponycopter
|
109
|
-
ponycopter.greet_by_proxy(actor).should eq("Hi, I'm a ponycopter!")
|
110
|
-
end
|
111
|
-
|
112
|
-
it "detects recursion" do
|
113
|
-
klass1 = Class.new do
|
114
|
-
include included_module
|
115
|
-
task_class task_klass
|
116
|
-
|
117
|
-
def recursion_test(recurse_through = nil)
|
118
|
-
if recurse_through
|
119
|
-
recurse_through.recursion_thunk(Celluloid::Actor.current)
|
120
|
-
else
|
121
|
-
Celluloid.detect_recursion
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
klass2 = Class.new do
|
127
|
-
include included_module
|
128
|
-
task_class task_klass
|
129
|
-
|
130
|
-
def recursion_thunk(other)
|
131
|
-
other.recursion_test
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
actor1 = klass1.new
|
136
|
-
actor2 = klass2.new
|
137
|
-
|
138
|
-
actor1.recursion_test.should be_false
|
139
|
-
actor1.recursion_test(actor2).should be_true
|
140
|
-
end
|
141
|
-
|
142
|
-
it "properly handles method_missing" do
|
143
|
-
actor = actor_class.new "Method Missing"
|
144
|
-
actor.should respond_to(:first)
|
145
|
-
actor.first.should be :bar
|
146
|
-
end
|
147
|
-
|
148
|
-
it "properly handles respond_to with include_private" do
|
149
|
-
actor = actor_class.new "Method missing privates"
|
150
|
-
actor.respond_to?(:zomg_private).should be_false
|
151
|
-
actor.respond_to?(:zomg_private, true).should be_true
|
152
|
-
end
|
153
|
-
|
154
|
-
it "warns about suspending the initialize" do
|
155
|
-
klass = Class.new do
|
156
|
-
include included_module
|
157
|
-
task_class task_klass
|
158
|
-
|
159
|
-
def initialize
|
160
|
-
sleep 0.1
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
Celluloid.logger.should_receive(:warn).with(/Dangerously suspending task: type=:call, meta={:method_name=>:initialize}, status=:sleeping/)
|
165
|
-
|
166
|
-
actor = klass.new
|
167
|
-
actor.terminate
|
168
|
-
Celluloid::Actor.join(actor) unless defined?(JRUBY_VERSION)
|
169
|
-
end
|
170
|
-
|
171
|
-
it "calls the user defined finalizer" do
|
172
|
-
actor = actor_class.new "Mr. Bean"
|
173
|
-
actor.wrapped_object.should_receive(:my_finalizer)
|
174
|
-
actor.terminate
|
175
|
-
Celluloid::Actor.join(actor)
|
176
|
-
end
|
177
|
-
|
178
|
-
it "warns about suspending the finalizer" do
|
179
|
-
klass = Class.new do
|
180
|
-
include included_module
|
181
|
-
task_class task_klass
|
182
|
-
|
183
|
-
finalizer :cleanup
|
184
|
-
|
185
|
-
def cleanup
|
186
|
-
sleep 0.1
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
Celluloid.logger.should_receive(:warn).with(/Dangerously suspending task: type=:finalizer, meta={:method_name=>:cleanup}, status=:sleeping/)
|
191
|
-
|
192
|
-
actor = klass.new
|
193
|
-
actor.terminate
|
194
|
-
Celluloid::Actor.join(actor)
|
195
|
-
end
|
196
|
-
|
197
|
-
it "supports async(:method) syntax for asynchronous calls" do
|
198
|
-
actor = actor_class.new "Troy McClure"
|
199
|
-
actor.async :change_name, "Charlie Sheen"
|
200
|
-
actor.greet.should eq("Hi, I'm Charlie Sheen")
|
201
|
-
end
|
202
|
-
|
203
|
-
it "supports async.method syntax for asynchronous calls" do
|
204
|
-
actor = actor_class.new "Troy McClure"
|
205
|
-
actor.async.change_name "Charlie Sheen"
|
206
|
-
actor.greet.should eq("Hi, I'm Charlie Sheen")
|
207
|
-
end
|
208
|
-
|
209
|
-
it "supports async.method syntax for asynchronous calls to itself" do
|
210
|
-
actor = actor_class.new "Troy McClure"
|
211
|
-
actor.change_name_async "Charlie Sheen"
|
212
|
-
actor.greet.should eq("Hi, I'm Charlie Sheen")
|
213
|
-
end
|
214
|
-
|
215
|
-
it "allows an actor to call private methods asynchronously" do
|
216
|
-
actor = actor_class.new "Troy McClure"
|
217
|
-
actor.call_private
|
218
|
-
actor.private_called.should be_true
|
219
|
-
end
|
220
|
-
|
221
|
-
it "knows if it's inside actor scope" do
|
222
|
-
Celluloid.should_not be_actor
|
223
|
-
actor = actor_class.new "Troy McClure"
|
224
|
-
actor.run do
|
225
|
-
Celluloid.actor?
|
226
|
-
end.should be_false
|
227
|
-
actor.run_on_receiver do
|
228
|
-
Celluloid.actor?
|
229
|
-
end.should be_true
|
230
|
-
actor.should be_actor
|
231
|
-
end
|
232
|
-
|
233
|
-
it "inspects properly" do
|
234
|
-
actor = actor_class.new "Troy McClure"
|
235
|
-
actor.inspect.should match(/Celluloid::CellProxy\(/)
|
236
|
-
actor.inspect.should match(/#{actor_class}/)
|
237
|
-
actor.inspect.should include('@name="Troy McClure"')
|
238
|
-
actor.inspect.should_not include("@celluloid")
|
239
|
-
end
|
240
|
-
|
241
|
-
it "inspects properly when dead" do
|
242
|
-
actor = actor_class.new "Troy McClure"
|
243
|
-
actor.terminate
|
244
|
-
actor.inspect.should match(/Celluloid::CellProxy\(/)
|
245
|
-
actor.inspect.should match(/#{actor_class}/)
|
246
|
-
actor.inspect.should include('dead')
|
247
|
-
end
|
248
|
-
|
249
|
-
it "supports recursive inspect with other actors" do
|
250
|
-
klass = Class.new do
|
251
|
-
include included_module
|
252
|
-
task_class task_klass
|
253
|
-
|
254
|
-
attr_accessor :other
|
255
|
-
|
256
|
-
def initialize(other = nil)
|
257
|
-
@other = other
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
itchy = klass.new
|
262
|
-
scratchy = klass.new(itchy)
|
263
|
-
itchy.other = scratchy
|
264
|
-
|
265
|
-
inspection = itchy.inspect
|
266
|
-
inspection.should match(/Celluloid::CellProxy\(/)
|
267
|
-
inspection.should include("...")
|
268
|
-
end
|
269
|
-
|
270
|
-
it "allows access to the wrapped object" do
|
271
|
-
actor = actor_class.new "Troy McClure"
|
272
|
-
actor.wrapped_object.should be_a actor_class
|
273
|
-
end
|
274
|
-
|
275
|
-
it "warns about leaked wrapped objects via #inspect" do
|
276
|
-
actor = actor_class.new "Troy McClure"
|
277
|
-
|
278
|
-
actor.inspect.should_not include Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
279
|
-
actor.inspect_thunk.should_not include Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
280
|
-
actor.wrapped_object.inspect.should include Celluloid::BARE_OBJECT_WARNING_MESSAGE
|
281
|
-
end
|
282
|
-
|
283
|
-
it "can override #send" do
|
284
|
-
actor = actor_class.new "Troy McClure"
|
285
|
-
actor.send('foo').should eq('oof')
|
286
|
-
end
|
287
|
-
|
288
|
-
context "when executing under JRuby" do
|
289
|
-
let(:klass) {
|
290
|
-
Class.new do
|
291
|
-
include included_module
|
292
|
-
task_class task_klass
|
293
|
-
|
294
|
-
def current_thread_name
|
295
|
-
java_thread.get_name
|
296
|
-
end
|
297
|
-
|
298
|
-
def java_thread
|
299
|
-
Thread.current.to_java.getNativeThread
|
300
|
-
end
|
301
|
-
end
|
302
|
-
}
|
303
|
-
|
304
|
-
it "sets execution info" do
|
305
|
-
klass.new.current_thread_name.should == "Class#current_thread_name"
|
306
|
-
end
|
307
|
-
|
308
|
-
it "unsets execution info after task completion" do
|
309
|
-
klass.new.java_thread.get_name.should == "<unused>"
|
310
|
-
end
|
311
|
-
end if RUBY_PLATFORM == "java"
|
312
|
-
|
313
|
-
context "mocking methods" do
|
314
|
-
let(:actor) { actor_class.new "Troy McClure" }
|
315
|
-
|
316
|
-
before do
|
317
|
-
actor.wrapped_object.should_receive(:external_hello).once.and_return "World"
|
318
|
-
end
|
319
|
-
|
320
|
-
it "works externally via the proxy" do
|
321
|
-
actor.external_hello.should eq("World")
|
322
|
-
end
|
323
|
-
|
324
|
-
it "works internally when called on self" do
|
325
|
-
actor.internal_hello.should eq("World")
|
326
|
-
end
|
327
|
-
end
|
328
|
-
|
329
|
-
context :exceptions do
|
330
|
-
it "reraises exceptions which occur during synchronous calls in the sender" do
|
331
|
-
actor = actor_class.new "James Dean" # is this in bad taste?
|
332
|
-
|
333
|
-
expect do
|
334
|
-
actor.crash
|
335
|
-
end.to raise_exception(ExampleCrash)
|
336
|
-
end
|
337
|
-
|
338
|
-
it "includes both sender and receiver in exception traces" do
|
339
|
-
example_receiver = Class.new do
|
340
|
-
include included_module
|
341
|
-
task_class task_klass
|
342
|
-
|
343
|
-
define_method(:receiver_method) do
|
344
|
-
raise ExampleCrash, "the spec purposely crashed me :("
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
excample_caller = Class.new do
|
349
|
-
include included_module
|
350
|
-
task_class task_klass
|
351
|
-
|
352
|
-
define_method(:sender_method) do
|
353
|
-
example_receiver.new.receiver_method
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
ex = nil
|
358
|
-
begin
|
359
|
-
excample_caller.new.sender_method
|
360
|
-
rescue => ex
|
361
|
-
end
|
362
|
-
|
363
|
-
ex.should be_a ExampleCrash
|
364
|
-
ex.backtrace.grep(/`sender_method'/).should be_true
|
365
|
-
ex.backtrace.grep(/`receiver_method'/).should be_true
|
366
|
-
end
|
367
|
-
|
368
|
-
it "raises DeadActorError if methods are synchronously called on a dead actor" do
|
369
|
-
actor = actor_class.new "James Dean"
|
370
|
-
actor.crash rescue nil
|
371
|
-
|
372
|
-
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
373
|
-
|
374
|
-
expect do
|
375
|
-
actor.greet
|
376
|
-
end.to raise_exception(Celluloid::DeadActorError)
|
377
|
-
end
|
378
|
-
end
|
379
|
-
|
380
|
-
context :abort do
|
381
|
-
it "raises exceptions in the sender but keeps running" do
|
382
|
-
actor = actor_class.new "Al Pacino"
|
383
|
-
|
384
|
-
expect do
|
385
|
-
actor.crash_with_abort "You die motherfucker!", :bar
|
386
|
-
end.to raise_exception(ExampleCrash, "You die motherfucker!")
|
387
|
-
|
388
|
-
actor.should be_alive
|
389
|
-
end
|
390
|
-
|
391
|
-
it "converts strings to runtime errors" do
|
392
|
-
actor = actor_class.new "Al Pacino"
|
393
|
-
expect do
|
394
|
-
actor.crash_with_abort_raw "foo"
|
395
|
-
end.to raise_exception(RuntimeError, "foo")
|
396
|
-
end
|
397
|
-
|
398
|
-
it "crashes the sender if we pass neither String nor Exception" do
|
399
|
-
actor = actor_class.new "Al Pacino"
|
400
|
-
expect do
|
401
|
-
actor.crash_with_abort_raw 10
|
402
|
-
end.to raise_exception(TypeError, "Exception object/String expected, but Fixnum received")
|
403
|
-
|
404
|
-
Celluloid::Actor.join(actor)
|
405
|
-
actor.should_not be_alive
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
context :termination do
|
410
|
-
it "terminates" do
|
411
|
-
actor = actor_class.new "Arnold Schwarzenegger"
|
412
|
-
actor.should be_alive
|
413
|
-
actor.terminate
|
414
|
-
Celluloid::Actor.join(actor)
|
415
|
-
actor.should_not be_alive
|
416
|
-
end
|
417
|
-
|
418
|
-
it "can be terminated by a SyncCall" do
|
419
|
-
actor = actor_class.new "Arnold Schwarzenegger"
|
420
|
-
actor.should be_alive
|
421
|
-
actor.shutdown
|
422
|
-
Celluloid::Actor.join(actor)
|
423
|
-
actor.should_not be_alive
|
424
|
-
end
|
425
|
-
|
426
|
-
it "kills" do # THOU SHALT ALWAYS KILL
|
427
|
-
actor = actor_class.new "Woody Harrelson"
|
428
|
-
actor.should be_alive
|
429
|
-
Celluloid::Actor.kill(actor)
|
430
|
-
Celluloid::Actor.join(actor)
|
431
|
-
actor.should_not be_alive
|
432
|
-
end
|
433
|
-
|
434
|
-
it "raises DeadActorError if called after terminated" do
|
435
|
-
actor = actor_class.new "Arnold Schwarzenegger"
|
436
|
-
actor.terminate
|
437
|
-
|
438
|
-
expect do
|
439
|
-
actor.greet
|
440
|
-
end.to raise_exception(Celluloid::DeadActorError)
|
441
|
-
end
|
442
|
-
|
443
|
-
it "terminates cleanly on Celluloid shutdown" do
|
444
|
-
Celluloid::Actor.stub(:kill).and_call_original
|
445
|
-
|
446
|
-
actor = actor_class.new "Arnold Schwarzenegger"
|
447
|
-
|
448
|
-
Celluloid.shutdown
|
449
|
-
Celluloid::Actor.should_not have_received(:kill)
|
450
|
-
end
|
451
|
-
|
452
|
-
it "raises the right DeadActorError if terminate! called after terminated" do
|
453
|
-
actor = actor_class.new "Arnold Schwarzenegger"
|
454
|
-
actor.terminate
|
455
|
-
|
456
|
-
expect do
|
457
|
-
actor.terminate!
|
458
|
-
end.to raise_exception(Celluloid::DeadActorError, "actor already terminated")
|
459
|
-
end
|
460
|
-
|
461
|
-
it "logs a warning when terminating tasks" do
|
462
|
-
Celluloid.logger.should_receive(:warn).with(/^Terminating task: type=:call, meta={:method_name=>:sleepy}, status=:sleeping\n/)
|
463
|
-
|
464
|
-
actor = actor_class.new "Arnold Schwarzenegger"
|
465
|
-
actor.async.sleepy 10
|
466
|
-
actor.greet # make sure the actor has started sleeping
|
467
|
-
|
468
|
-
actor.terminate
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
context :current_actor do
|
473
|
-
it "knows the current actor" do
|
474
|
-
actor = actor_class.new "Roger Daltrey"
|
475
|
-
actor.current_actor.should eq actor
|
476
|
-
end
|
477
|
-
|
478
|
-
it "raises NotActorError if called outside an actor" do
|
479
|
-
expect do
|
480
|
-
Celluloid.current_actor
|
481
|
-
end.to raise_exception(Celluloid::NotActorError)
|
482
|
-
end
|
483
|
-
end
|
484
|
-
|
485
|
-
context :linking do
|
486
|
-
before :each do
|
487
|
-
@kevin = actor_class.new "Kevin Bacon" # Some six degrees action here
|
488
|
-
@charlie = actor_class.new "Charlie Sheen"
|
489
|
-
end
|
490
|
-
|
491
|
-
let(:supervisor_class) do
|
492
|
-
Class.new do # like a boss
|
493
|
-
include included_module
|
494
|
-
task_class task_klass
|
495
|
-
trap_exit :lambaste_subordinate
|
496
|
-
|
497
|
-
def initialize(name)
|
498
|
-
@name = name
|
499
|
-
@subordinate_lambasted = false
|
500
|
-
end
|
501
|
-
|
502
|
-
def subordinate_lambasted?; @subordinate_lambasted; end
|
503
|
-
|
504
|
-
def lambaste_subordinate(actor, reason)
|
505
|
-
@subordinate_lambasted = true
|
506
|
-
end
|
507
|
-
end
|
508
|
-
end
|
509
|
-
|
510
|
-
it "links to other actors" do
|
511
|
-
@kevin.link @charlie
|
512
|
-
@kevin.monitoring?(@charlie).should be_true
|
513
|
-
@kevin.linked_to?(@charlie).should be_true
|
514
|
-
@charlie.monitoring?(@kevin).should be_true
|
515
|
-
@charlie.linked_to?(@kevin).should be_true
|
516
|
-
end
|
517
|
-
|
518
|
-
it "unlinks from other actors" do
|
519
|
-
@kevin.link @charlie
|
520
|
-
@kevin.unlink @charlie
|
521
|
-
|
522
|
-
@kevin.monitoring?(@charlie).should be_false
|
523
|
-
@kevin.linked_to?(@charlie).should be_false
|
524
|
-
@charlie.monitoring?(@kevin).should be_false
|
525
|
-
@charlie.linked_to?(@kevin).should be_false
|
526
|
-
end
|
527
|
-
|
528
|
-
it "monitors other actors unidirectionally" do
|
529
|
-
@kevin.monitor @charlie
|
530
|
-
|
531
|
-
@kevin.monitoring?(@charlie).should be_true
|
532
|
-
@kevin.linked_to?(@charlie).should be_false
|
533
|
-
@charlie.monitoring?(@kevin).should be_false
|
534
|
-
@charlie.linked_to?(@kevin).should be_false
|
535
|
-
end
|
536
|
-
|
537
|
-
it "unmonitors other actors" do
|
538
|
-
@kevin.monitor @charlie
|
539
|
-
@kevin.unmonitor @charlie
|
540
|
-
|
541
|
-
@kevin.monitoring?(@charlie).should be_false
|
542
|
-
@kevin.linked_to?(@charlie).should be_false
|
543
|
-
@charlie.monitoring?(@kevin).should be_false
|
544
|
-
@charlie.linked_to?(@kevin).should be_false
|
545
|
-
end
|
546
|
-
|
547
|
-
it "traps exit messages from other actors" do
|
548
|
-
chuck = supervisor_class.new "Chuck Lorre"
|
549
|
-
chuck.link @charlie
|
550
|
-
|
551
|
-
expect do
|
552
|
-
@charlie.crash
|
553
|
-
end.to raise_exception(ExampleCrash)
|
554
|
-
|
555
|
-
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
556
|
-
chuck.should be_subordinate_lambasted
|
557
|
-
end
|
558
|
-
|
559
|
-
it "traps exit messages from other actors in subclasses" do
|
560
|
-
supervisor_subclass = Class.new(supervisor_class)
|
561
|
-
chuck = supervisor_subclass.new "Chuck Lorre"
|
562
|
-
chuck.link @charlie
|
563
|
-
|
564
|
-
expect do
|
565
|
-
@charlie.crash
|
566
|
-
end.to raise_exception(ExampleCrash)
|
567
|
-
|
568
|
-
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
569
|
-
chuck.should be_subordinate_lambasted
|
570
|
-
end
|
571
|
-
|
572
|
-
it "unlinks from a dead linked actor" do
|
573
|
-
chuck = supervisor_class.new "Chuck Lorre"
|
574
|
-
chuck.link @charlie
|
575
|
-
|
576
|
-
expect do
|
577
|
-
@charlie.crash
|
578
|
-
end.to raise_exception(ExampleCrash)
|
579
|
-
|
580
|
-
sleep 0.1 # hax to prevent a race between exit handling and the next call
|
581
|
-
chuck.links.count.should be(0)
|
582
|
-
end
|
583
|
-
end
|
584
|
-
|
585
|
-
context :signaling do
|
586
|
-
before do
|
587
|
-
@signaler = Class.new do
|
588
|
-
include included_module
|
589
|
-
task_class task_klass
|
590
|
-
|
591
|
-
def initialize
|
592
|
-
@waiting = false
|
593
|
-
@signaled = false
|
594
|
-
end
|
595
|
-
|
596
|
-
def wait_for_signal
|
597
|
-
raise "already signaled" if @signaled
|
598
|
-
|
599
|
-
@waiting = true
|
600
|
-
value = wait :ponycopter
|
601
|
-
|
602
|
-
@waiting = false
|
603
|
-
@signaled = true
|
604
|
-
value
|
605
|
-
end
|
606
|
-
|
607
|
-
def send_signal(value)
|
608
|
-
signal :ponycopter, value
|
609
|
-
end
|
610
|
-
|
611
|
-
def waiting?; @waiting end
|
612
|
-
def signaled?; @signaled end
|
613
|
-
end
|
614
|
-
end
|
615
|
-
|
616
|
-
it "allows methods within the same object to signal each other" do
|
617
|
-
obj = @signaler.new
|
618
|
-
obj.should_not be_signaled
|
619
|
-
|
620
|
-
obj.async.wait_for_signal
|
621
|
-
obj.should_not be_signaled
|
622
|
-
obj.should be_waiting
|
623
|
-
|
624
|
-
obj.send_signal :foobar
|
625
|
-
obj.should be_signaled
|
626
|
-
obj.should_not be_waiting
|
627
|
-
end
|
628
|
-
|
629
|
-
it "sends values along with signals" do
|
630
|
-
obj = @signaler.new
|
631
|
-
obj.should_not be_signaled
|
632
|
-
|
633
|
-
future = obj.future(:wait_for_signal)
|
634
|
-
|
635
|
-
obj.should be_waiting
|
636
|
-
obj.should_not be_signaled
|
637
|
-
|
638
|
-
obj.send_signal(:foobar).should be_true
|
639
|
-
future.value.should be(:foobar)
|
640
|
-
end
|
641
|
-
end
|
642
|
-
|
643
|
-
context :exclusive do
|
644
|
-
subject do
|
645
|
-
Class.new do
|
646
|
-
include included_module
|
647
|
-
task_class task_klass
|
648
|
-
|
649
|
-
attr_reader :tasks
|
650
|
-
|
651
|
-
def initialize
|
652
|
-
@tasks = []
|
653
|
-
end
|
654
|
-
|
655
|
-
def log_task(task)
|
656
|
-
@tasks << task
|
657
|
-
end
|
658
|
-
|
659
|
-
def exclusive_with_block_log_task(task)
|
660
|
-
exclusive do
|
661
|
-
sleep Celluloid::TIMER_QUANTUM
|
662
|
-
log_task(task)
|
663
|
-
end
|
664
|
-
end
|
665
|
-
|
666
|
-
def exclusive_log_task(task)
|
667
|
-
sleep Celluloid::TIMER_QUANTUM
|
668
|
-
log_task(task)
|
669
|
-
end
|
670
|
-
exclusive :exclusive_log_task
|
671
|
-
|
672
|
-
def check_not_exclusive
|
673
|
-
Celluloid.exclusive?
|
674
|
-
end
|
675
|
-
|
676
|
-
def check_exclusive
|
677
|
-
exclusive { Celluloid.exclusive? }
|
678
|
-
end
|
679
|
-
|
680
|
-
def nested_exclusive_example
|
681
|
-
exclusive { exclusive { nil }; Celluloid.exclusive? }
|
682
|
-
end
|
683
|
-
end.new
|
684
|
-
end
|
685
|
-
|
686
|
-
it "executes methods in the proper order with block form" do
|
687
|
-
subject.async.exclusive_with_block_log_task(:one)
|
688
|
-
subject.async.log_task(:two)
|
689
|
-
sleep Celluloid::TIMER_QUANTUM * 2
|
690
|
-
subject.tasks.should eq([:one, :two])
|
691
|
-
end
|
692
|
-
|
693
|
-
it "executes methods in the proper order with a class-level annotation" do
|
694
|
-
subject.async.exclusive_log_task :one
|
695
|
-
subject.async.log_task :two
|
696
|
-
sleep Celluloid::TIMER_QUANTUM * 2
|
697
|
-
subject.tasks.should eq([:one, :two])
|
698
|
-
end
|
699
|
-
|
700
|
-
it "knows when it's in exclusive mode" do
|
701
|
-
subject.check_not_exclusive.should be_false
|
702
|
-
subject.check_exclusive.should be_true
|
703
|
-
end
|
704
|
-
|
705
|
-
it "remains in exclusive mode inside nested blocks" do
|
706
|
-
subject.nested_exclusive_example.should be_true
|
707
|
-
end
|
708
|
-
end
|
709
|
-
|
710
|
-
context "exclusive classes" do
|
711
|
-
subject do
|
712
|
-
Class.new do
|
713
|
-
include included_module
|
714
|
-
task_class task_klass
|
715
|
-
exclusive
|
716
|
-
|
717
|
-
attr_reader :tasks
|
718
|
-
|
719
|
-
def initialize
|
720
|
-
@tasks = []
|
721
|
-
end
|
722
|
-
|
723
|
-
def eat_donuts
|
724
|
-
sleep Celluloid::TIMER_QUANTUM
|
725
|
-
@tasks << 'donuts'
|
726
|
-
end
|
727
|
-
|
728
|
-
def drink_coffee
|
729
|
-
@tasks << 'coffee'
|
730
|
-
end
|
731
|
-
end
|
732
|
-
end
|
733
|
-
|
734
|
-
it "executes two methods in an exclusive order" do
|
735
|
-
actor = subject.new
|
736
|
-
actor.async.eat_donuts
|
737
|
-
actor.async.drink_coffee
|
738
|
-
sleep Celluloid::TIMER_QUANTUM * 2
|
739
|
-
actor.tasks.should eq(['donuts', 'coffee'])
|
740
|
-
end
|
741
|
-
end
|
742
|
-
|
743
|
-
context :receiving do
|
744
|
-
before do
|
745
|
-
@receiver = Class.new do
|
746
|
-
include included_module
|
747
|
-
task_class task_klass
|
748
|
-
execute_block_on_receiver :signal_myself
|
749
|
-
|
750
|
-
def signal_myself(obj, &block)
|
751
|
-
current_actor.mailbox << obj
|
752
|
-
receive(&block)
|
753
|
-
end
|
754
|
-
end
|
755
|
-
end
|
756
|
-
|
757
|
-
let(:receiver) { @receiver.new }
|
758
|
-
let(:message) { Object.new }
|
759
|
-
|
760
|
-
it "allows unconditional receive" do
|
761
|
-
receiver.signal_myself(message).should eq(message)
|
762
|
-
end
|
763
|
-
|
764
|
-
it "allows arbitrary selective receive" do
|
765
|
-
received_obj = receiver.signal_myself(message) { |o| o == message }
|
766
|
-
received_obj.should eq(message)
|
767
|
-
end
|
768
|
-
|
769
|
-
it "times out after the given interval", :pending => ENV['CI'] do
|
770
|
-
interval = 0.1
|
771
|
-
started_at = Time.now
|
772
|
-
|
773
|
-
receiver.receive(interval) { false }.should be_nil
|
774
|
-
(Time.now - started_at).should be_within(Celluloid::TIMER_QUANTUM).of interval
|
775
|
-
end
|
776
|
-
end
|
777
|
-
|
778
|
-
context :timers do
|
779
|
-
before do
|
780
|
-
@klass = Class.new do
|
781
|
-
include included_module
|
782
|
-
task_class task_klass
|
783
|
-
|
784
|
-
def initialize
|
785
|
-
@sleeping = false
|
786
|
-
@fired = false
|
787
|
-
end
|
788
|
-
|
789
|
-
def do_sleep(n)
|
790
|
-
@sleeping = true
|
791
|
-
sleep n
|
792
|
-
@sleeping = false
|
793
|
-
end
|
794
|
-
|
795
|
-
def sleeping?; @sleeping end
|
796
|
-
|
797
|
-
def fire_after(n)
|
798
|
-
after(n) { @fired = true }
|
799
|
-
end
|
800
|
-
|
801
|
-
def fire_every(n)
|
802
|
-
@fired = 0
|
803
|
-
every(n) { @fired += 1 }
|
804
|
-
end
|
805
|
-
|
806
|
-
def fired?; !!@fired end
|
807
|
-
def fired; @fired end
|
808
|
-
end
|
809
|
-
end
|
810
|
-
|
811
|
-
it "suspends execution of a method (but not the actor) for a given time" do
|
812
|
-
actor = @klass.new
|
813
|
-
|
814
|
-
# Sleep long enough to ensure we're actually seeing behavior when asleep
|
815
|
-
# but not so long as to delay the test suite unnecessarily
|
816
|
-
interval = Celluloid::TIMER_QUANTUM * 10
|
817
|
-
started_at = Time.now
|
818
|
-
|
819
|
-
future = actor.future(:do_sleep, interval)
|
820
|
-
sleep(interval / 2) # wonky! :/
|
821
|
-
actor.should be_sleeping
|
822
|
-
|
823
|
-
future.value
|
824
|
-
(Time.now - started_at).should be_within(Celluloid::TIMER_QUANTUM).of interval
|
825
|
-
end
|
826
|
-
|
827
|
-
it "schedules timers which fire in the future" do
|
828
|
-
actor = @klass.new
|
829
|
-
|
830
|
-
interval = Celluloid::TIMER_QUANTUM * 10
|
831
|
-
|
832
|
-
actor.fire_after(interval)
|
833
|
-
actor.should_not be_fired
|
834
|
-
|
835
|
-
sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/
|
836
|
-
actor.should be_fired
|
837
|
-
end
|
838
|
-
|
839
|
-
it "schedules recurring timers which fire in the future" do
|
840
|
-
actor = @klass.new
|
841
|
-
|
842
|
-
interval = Celluloid::TIMER_QUANTUM * 10
|
843
|
-
|
844
|
-
actor.fire_every(interval)
|
845
|
-
actor.fired.should be_zero
|
846
|
-
|
847
|
-
sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/
|
848
|
-
actor.fired.should be 1
|
849
|
-
|
850
|
-
2.times { sleep(interval + Celluloid::TIMER_QUANTUM) } # wonky! #/
|
851
|
-
actor.fired.should be 3
|
852
|
-
end
|
853
|
-
|
854
|
-
it "cancels timers before they fire" do
|
855
|
-
actor = @klass.new
|
856
|
-
|
857
|
-
interval = Celluloid::TIMER_QUANTUM * 10
|
858
|
-
|
859
|
-
timer = actor.fire_after(interval)
|
860
|
-
actor.should_not be_fired
|
861
|
-
timer.cancel
|
862
|
-
|
863
|
-
sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/
|
864
|
-
actor.should_not be_fired
|
865
|
-
end
|
866
|
-
|
867
|
-
it "allows delays from outside the actor" do
|
868
|
-
actor = @klass.new
|
869
|
-
|
870
|
-
interval = Celluloid::TIMER_QUANTUM * 10
|
871
|
-
fired = false
|
872
|
-
|
873
|
-
actor.after(interval) do
|
874
|
-
fired = true
|
875
|
-
end
|
876
|
-
fired.should be_false
|
877
|
-
|
878
|
-
sleep(interval + Celluloid::TIMER_QUANTUM) # wonky! #/
|
879
|
-
fired.should be_true
|
880
|
-
end
|
881
|
-
end
|
882
|
-
|
883
|
-
context :tasks do
|
884
|
-
before do
|
885
|
-
@klass = Class.new do
|
886
|
-
include included_module
|
887
|
-
task_class task_klass
|
888
|
-
attr_reader :blocker
|
889
|
-
|
890
|
-
def initialize
|
891
|
-
@blocker = Blocker.new
|
892
|
-
end
|
893
|
-
|
894
|
-
def blocking_call
|
895
|
-
@blocker.block
|
896
|
-
end
|
897
|
-
end
|
898
|
-
|
899
|
-
class Blocker
|
900
|
-
include Celluloid
|
901
|
-
|
902
|
-
def block
|
903
|
-
wait :unblock
|
904
|
-
end
|
905
|
-
|
906
|
-
def unblock
|
907
|
-
signal :unblock
|
908
|
-
end
|
909
|
-
end
|
910
|
-
end
|
911
|
-
|
912
|
-
it "knows which tasks are waiting on calls to other actors" do
|
913
|
-
actor = @klass.new
|
914
|
-
|
915
|
-
tasks = actor.tasks
|
916
|
-
tasks.size.should be 1
|
917
|
-
|
918
|
-
actor.future(:blocking_call)
|
919
|
-
sleep 0.1 # hax! waiting for ^^^ call to actually start
|
920
|
-
|
921
|
-
tasks = actor.tasks
|
922
|
-
tasks.size.should be 2
|
923
|
-
|
924
|
-
blocking_task = tasks.find { |t| t.status != :running }
|
925
|
-
blocking_task.should be_a task_klass
|
926
|
-
blocking_task.status.should be :callwait
|
927
|
-
|
928
|
-
actor.blocker.unblock
|
929
|
-
sleep 0.1 # hax again :(
|
930
|
-
actor.tasks.size.should be 1
|
931
|
-
end
|
932
|
-
end
|
933
|
-
|
934
|
-
context :mailbox_class do
|
935
|
-
class ExampleMailbox < Celluloid::Mailbox; end
|
936
|
-
|
937
|
-
subject do
|
938
|
-
Class.new do
|
939
|
-
include included_module
|
940
|
-
task_class task_klass
|
941
|
-
mailbox_class ExampleMailbox
|
942
|
-
end
|
943
|
-
end
|
944
|
-
|
945
|
-
it "uses user-specified mailboxes" do
|
946
|
-
subject.new.mailbox.should be_a ExampleMailbox
|
947
|
-
end
|
948
|
-
|
949
|
-
it "retains custom mailboxes when subclassed" do
|
950
|
-
subclass = Class.new(subject)
|
951
|
-
subclass.new.mailbox.should be_a ExampleMailbox
|
952
|
-
end
|
953
|
-
end
|
954
|
-
|
955
|
-
context :mailbox_size do
|
956
|
-
subject do
|
957
|
-
Class.new do
|
958
|
-
include included_module
|
959
|
-
task_class task_klass
|
960
|
-
mailbox_size 100
|
961
|
-
end
|
962
|
-
end
|
963
|
-
|
964
|
-
it "configures the mailbox limit" do
|
965
|
-
subject.new.mailbox.max_size.should == 100
|
966
|
-
end
|
967
|
-
end
|
968
|
-
|
969
|
-
context :proxy_class do
|
970
|
-
class ExampleProxy < Celluloid::CellProxy
|
971
|
-
def subclass_proxy?
|
972
|
-
true
|
973
|
-
end
|
974
|
-
end
|
975
|
-
|
976
|
-
subject do
|
977
|
-
Class.new do
|
978
|
-
include included_module
|
979
|
-
task_class task_klass
|
980
|
-
proxy_class ExampleProxy
|
981
|
-
end
|
982
|
-
end
|
983
|
-
|
984
|
-
it "uses user-specified proxy" do
|
985
|
-
subject.new.should be_subclass_proxy
|
986
|
-
end
|
987
|
-
|
988
|
-
it "retains custom proxy when subclassed" do
|
989
|
-
subclass = Class.new(subject)
|
990
|
-
subclass.new.should be_subclass_proxy
|
991
|
-
end
|
992
|
-
end
|
993
|
-
|
994
|
-
context :task_class do
|
995
|
-
class ExampleTask < Celluloid::TaskFiber; end
|
996
|
-
|
997
|
-
subject do
|
998
|
-
Class.new do
|
999
|
-
include included_module
|
1000
|
-
task_class ExampleTask
|
1001
|
-
end
|
1002
|
-
end
|
1003
|
-
|
1004
|
-
it "overrides the task class" do
|
1005
|
-
subject.new.tasks.first.should be_a ExampleTask
|
1006
|
-
end
|
1007
|
-
|
1008
|
-
it "retains custom task classes when subclassed" do
|
1009
|
-
subclass = Class.new(subject)
|
1010
|
-
subclass.new.tasks.first.should be_a ExampleTask
|
1011
|
-
end
|
1012
|
-
end
|
1013
|
-
|
1014
|
-
context :timeouts do
|
1015
|
-
let :actor_class do
|
1016
|
-
Class.new do
|
1017
|
-
include included_module
|
1018
|
-
|
1019
|
-
def name
|
1020
|
-
sleep 0.5
|
1021
|
-
:foo
|
1022
|
-
end
|
1023
|
-
|
1024
|
-
def ask_name_with_timeout(other, duration)
|
1025
|
-
timeout(duration) { other.name }
|
1026
|
-
end
|
1027
|
-
end
|
1028
|
-
end
|
1029
|
-
|
1030
|
-
it "allows timing out tasks, raising Celluloid::Task::TimeoutError" do
|
1031
|
-
a1 = actor_class.new
|
1032
|
-
a2 = actor_class.new
|
1033
|
-
|
1034
|
-
expect { a1.ask_name_with_timeout a2, 0.3 }.to raise_error(Celluloid::Task::TimeoutError)
|
1035
|
-
end
|
1036
|
-
|
1037
|
-
it "does not raise when it completes in time" do
|
1038
|
-
a1 = actor_class.new
|
1039
|
-
a2 = actor_class.new
|
1040
|
-
|
1041
|
-
a1.ask_name_with_timeout(a2, 0.6).should == :foo
|
1042
|
-
end
|
1043
|
-
end
|
1044
|
-
|
1045
|
-
context "raw message sends" do
|
1046
|
-
it "logs on unhandled messages" do
|
1047
|
-
Celluloid.logger.should_receive(:debug).with("Discarded message (unhandled): first")
|
1048
|
-
|
1049
|
-
actor = actor_class.new "Irma Gladden"
|
1050
|
-
actor.mailbox << :first
|
1051
|
-
sleep Celluloid::TIMER_QUANTUM
|
1052
|
-
end
|
1053
|
-
end
|
1054
|
-
end
|