celluloid 0.14.1 → 0.15.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/lib/celluloid.rb +92 -108
- data/lib/celluloid/actor.rb +42 -64
- data/lib/celluloid/autostart.rb +1 -1
- data/lib/celluloid/call_chain.rb +13 -0
- data/lib/celluloid/calls.rb +5 -8
- data/lib/celluloid/condition.rb +8 -10
- data/lib/celluloid/cpu_counter.rb +1 -1
- data/lib/celluloid/evented_mailbox.rb +7 -10
- data/lib/celluloid/fsm.rb +1 -1
- data/lib/celluloid/future.rb +1 -2
- data/lib/celluloid/internal_pool.rb +77 -20
- data/lib/celluloid/legacy.rb +0 -38
- data/lib/celluloid/mailbox.rb +17 -10
- data/lib/celluloid/pool_manager.rb +1 -1
- data/lib/celluloid/properties.rb +24 -0
- data/lib/celluloid/proxies/abstract_proxy.rb +3 -0
- data/lib/celluloid/proxies/actor_proxy.rb +3 -32
- data/lib/celluloid/proxies/async_proxy.rb +4 -8
- data/lib/celluloid/proxies/future_proxy.rb +8 -6
- data/lib/celluloid/proxies/sync_proxy.rb +12 -7
- data/lib/celluloid/rspec.rb +3 -1
- data/lib/celluloid/signals.rb +7 -35
- data/lib/celluloid/stack_dump.rb +50 -37
- data/lib/celluloid/supervision_group.rb +5 -5
- data/lib/celluloid/task_set.rb +49 -0
- data/lib/celluloid/tasks.rb +67 -42
- data/lib/celluloid/tasks/task_fiber.rb +3 -1
- data/lib/celluloid/tasks/task_thread.rb +2 -3
- data/lib/celluloid/thread.rb +2 -0
- data/spec/celluloid/actor_spec.rb +5 -0
- data/spec/celluloid/block_spec.rb +54 -0
- data/spec/celluloid/calls_spec.rb +42 -0
- data/spec/celluloid/condition_spec.rb +65 -0
- data/spec/celluloid/evented_mailbox_spec.rb +34 -0
- data/spec/celluloid/fsm_spec.rb +107 -0
- data/spec/celluloid/future_spec.rb +32 -0
- data/spec/celluloid/internal_pool_spec.rb +52 -0
- data/spec/celluloid/links_spec.rb +45 -0
- data/spec/celluloid/logging/ring_buffer_spec.rb +38 -0
- data/spec/celluloid/mailbox_spec.rb +5 -0
- data/spec/celluloid/notifications_spec.rb +120 -0
- data/spec/celluloid/pool_spec.rb +52 -0
- data/spec/celluloid/properties_spec.rb +42 -0
- data/spec/celluloid/registry_spec.rb +64 -0
- data/spec/celluloid/stack_dump_spec.rb +35 -0
- data/spec/celluloid/supervision_group_spec.rb +53 -0
- data/spec/celluloid/supervisor_spec.rb +92 -0
- data/spec/celluloid/tasks/task_fiber_spec.rb +5 -0
- data/spec/celluloid/tasks/task_thread_spec.rb +5 -0
- data/spec/celluloid/thread_handle_spec.rb +22 -0
- data/spec/celluloid/uuid_spec.rb +11 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/actor_examples.rb +161 -10
- data/spec/support/example_actor_class.rb +8 -0
- data/spec/support/mailbox_examples.rb +15 -3
- data/spec/support/task_examples.rb +2 -2
- metadata +28 -3
- data/lib/celluloid/version.rb +0 -4
@@ -0,0 +1,24 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# Properties define inheritable attributes of classes, somewhat similar to
|
3
|
+
# Rails cattr_*/mattr_* or class_attribute
|
4
|
+
module Properties
|
5
|
+
def property(name, opts = {})
|
6
|
+
default = opts.fetch(:default, nil)
|
7
|
+
multi = opts.fetch(:multi, false)
|
8
|
+
ivar_name = "@#{name}".to_sym
|
9
|
+
|
10
|
+
ancestors.first.send(:define_singleton_method, name) do |value = nil, *extra|
|
11
|
+
if value
|
12
|
+
value = value ? [value, *extra] : [] if multi
|
13
|
+
instance_variable_set(ivar_name, value)
|
14
|
+
elsif instance_variables.include?(ivar_name)
|
15
|
+
instance_variable_get(ivar_name)
|
16
|
+
elsif superclass.respond_to? name
|
17
|
+
superclass.send(name)
|
18
|
+
else
|
19
|
+
default
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
1
|
module Celluloid
|
2
2
|
# Base class of all Celluloid proxies
|
3
3
|
class AbstractProxy < BasicObject
|
4
|
+
# Used for reflecting on proxy objects themselves
|
5
|
+
def __class__; AbstractProxy; end
|
6
|
+
|
4
7
|
# Needed for storing proxies in data structures
|
5
8
|
needed = [:object_id, :__id__, :hash] - instance_methods
|
6
9
|
if needed.any?
|
@@ -4,6 +4,9 @@ module Celluloid
|
|
4
4
|
class ActorProxy < SyncProxy
|
5
5
|
attr_reader :thread
|
6
6
|
|
7
|
+
# Used for reflecting on proxy objects themselves
|
8
|
+
def __class__; ActorProxy; end
|
9
|
+
|
7
10
|
def initialize(actor)
|
8
11
|
@thread = actor.thread
|
9
12
|
|
@@ -13,14 +16,6 @@ module Celluloid
|
|
13
16
|
@future_proxy = FutureProxy.new(@mailbox, @klass)
|
14
17
|
end
|
15
18
|
|
16
|
-
def class
|
17
|
-
method_missing :__send__, :class
|
18
|
-
end
|
19
|
-
|
20
|
-
def send(meth, *args, &block)
|
21
|
-
method_missing :send, meth, *args, &block
|
22
|
-
end
|
23
|
-
|
24
19
|
def _send_(meth, *args, &block)
|
25
20
|
method_missing :__send__, meth, *args, &block
|
26
21
|
end
|
@@ -31,26 +26,6 @@ module Celluloid
|
|
31
26
|
"#<Celluloid::ActorProxy(#{@klass}) dead>"
|
32
27
|
end
|
33
28
|
|
34
|
-
def name
|
35
|
-
method_missing :name
|
36
|
-
end
|
37
|
-
|
38
|
-
def is_a?(klass)
|
39
|
-
method_missing :is_a?, klass
|
40
|
-
end
|
41
|
-
|
42
|
-
def kind_of?(klass)
|
43
|
-
method_missing :kind_of?, klass
|
44
|
-
end
|
45
|
-
|
46
|
-
def respond_to?(meth, include_private = false)
|
47
|
-
method_missing :respond_to?, meth, include_private
|
48
|
-
end
|
49
|
-
|
50
|
-
def methods(include_ancestors = true)
|
51
|
-
method_missing :methods, include_ancestors
|
52
|
-
end
|
53
|
-
|
54
29
|
def method(name)
|
55
30
|
Method.new(self, name)
|
56
31
|
end
|
@@ -59,10 +34,6 @@ module Celluloid
|
|
59
34
|
@mailbox.alive?
|
60
35
|
end
|
61
36
|
|
62
|
-
def to_s
|
63
|
-
method_missing :to_s
|
64
|
-
end
|
65
|
-
|
66
37
|
alias_method :sync, :method_missing
|
67
38
|
|
68
39
|
# Obtain an async proxy or explicitly invoke a named async method
|
@@ -3,6 +3,9 @@ module Celluloid
|
|
3
3
|
class AsyncProxy < AbstractProxy
|
4
4
|
attr_reader :mailbox
|
5
5
|
|
6
|
+
# Used for reflecting on proxy objects themselves
|
7
|
+
def __class__; AsyncProxy; end
|
8
|
+
|
6
9
|
def initialize(mailbox, klass)
|
7
10
|
@mailbox, @klass = mailbox, klass
|
8
11
|
end
|
@@ -22,14 +25,7 @@ module Celluloid
|
|
22
25
|
raise "Cannot use blocks with async yet"
|
23
26
|
end
|
24
27
|
|
25
|
-
|
26
|
-
@mailbox << AsyncCall.new(meth, args, block)
|
27
|
-
rescue MailboxError
|
28
|
-
# Silently swallow asynchronous calls to dead actors. There's no way
|
29
|
-
# to reliably generate DeadActorErrors for async calls, so users of
|
30
|
-
# async calls should find other ways to deal with actors dying
|
31
|
-
# during an async call (i.e. linking/supervisors)
|
32
|
-
end
|
28
|
+
@mailbox << AsyncCall.new(meth, args, block)
|
33
29
|
end
|
34
30
|
end
|
35
31
|
end
|
@@ -3,6 +3,9 @@ module Celluloid
|
|
3
3
|
class FutureProxy < AbstractProxy
|
4
4
|
attr_reader :mailbox
|
5
5
|
|
6
|
+
# Used for reflecting on proxy objects themselves
|
7
|
+
def __class__; FutureProxy; end
|
8
|
+
|
6
9
|
def initialize(mailbox, klass)
|
7
10
|
@mailbox, @klass = mailbox, klass
|
8
11
|
end
|
@@ -11,8 +14,11 @@ module Celluloid
|
|
11
14
|
"#<Celluloid::FutureProxy(#{@klass})>"
|
12
15
|
end
|
13
16
|
|
14
|
-
# method_missing black magic to call bang predicate methods asynchronously
|
15
17
|
def method_missing(meth, *args, &block)
|
18
|
+
unless @mailbox.alive?
|
19
|
+
raise DeadActorError, "attempted to call a dead actor"
|
20
|
+
end
|
21
|
+
|
16
22
|
if block_given?
|
17
23
|
# FIXME: nicer exception
|
18
24
|
raise "Cannot use blocks with futures yet"
|
@@ -21,11 +27,7 @@ module Celluloid
|
|
21
27
|
future = Future.new
|
22
28
|
call = SyncCall.new(future, meth, args, block)
|
23
29
|
|
24
|
-
|
25
|
-
@mailbox << call
|
26
|
-
rescue MailboxError
|
27
|
-
raise DeadActorError, "attempted to call a dead actor"
|
28
|
-
end
|
30
|
+
@mailbox << call
|
29
31
|
|
30
32
|
future
|
31
33
|
end
|
@@ -3,6 +3,9 @@ module Celluloid
|
|
3
3
|
class SyncProxy < AbstractProxy
|
4
4
|
attr_reader :mailbox
|
5
5
|
|
6
|
+
# Used for reflecting on proxy objects themselves
|
7
|
+
def __class__; SyncProxy; end
|
8
|
+
|
6
9
|
def initialize(mailbox, klass)
|
7
10
|
@mailbox, @klass = mailbox, klass
|
8
11
|
end
|
@@ -11,20 +14,22 @@ module Celluloid
|
|
11
14
|
"#<Celluloid::SyncProxy(#{@klass})>"
|
12
15
|
end
|
13
16
|
|
17
|
+
def respond_to?(meth, include_private = false)
|
18
|
+
__class__.instance_methods.include?(meth) || super
|
19
|
+
end
|
20
|
+
|
14
21
|
def method_missing(meth, *args, &block)
|
22
|
+
unless @mailbox.alive?
|
23
|
+
raise DeadActorError, "attempted to call a dead actor"
|
24
|
+
end
|
25
|
+
|
15
26
|
if @mailbox == ::Thread.current[:celluloid_mailbox]
|
16
27
|
args.unshift meth
|
17
28
|
meth = :__send__
|
18
29
|
end
|
19
30
|
|
20
31
|
call = SyncCall.new(::Celluloid.mailbox, meth, args, block)
|
21
|
-
|
22
|
-
begin
|
23
|
-
@mailbox << call
|
24
|
-
rescue MailboxError
|
25
|
-
raise DeadActorError, "attempted to call a dead actor"
|
26
|
-
end
|
27
|
-
|
32
|
+
@mailbox << call
|
28
33
|
call.value
|
29
34
|
end
|
30
35
|
end
|
data/lib/celluloid/rspec.rb
CHANGED
data/lib/celluloid/signals.rb
CHANGED
@@ -1,51 +1,23 @@
|
|
1
1
|
module Celluloid
|
2
2
|
# Event signaling between methods of the same object
|
3
3
|
class Signals
|
4
|
-
attr_reader :waiting
|
5
|
-
|
6
4
|
def initialize
|
7
|
-
@
|
5
|
+
@conditions = {}
|
8
6
|
end
|
9
7
|
|
10
8
|
# Wait for the given signal and return the associated value
|
11
|
-
def wait(
|
9
|
+
def wait(name)
|
12
10
|
raise "cannot wait for signals while exclusive" if Celluloid.exclusive?
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
when Array
|
17
|
-
tasks << Task.current
|
18
|
-
when NilClass
|
19
|
-
@waiting[signal] = Task.current
|
20
|
-
else
|
21
|
-
@waiting[signal] = [tasks, Task.current]
|
22
|
-
end
|
23
|
-
|
24
|
-
Task.suspend :sigwait
|
12
|
+
@conditions[name] ||= Condition.new
|
13
|
+
@conditions[name].wait
|
25
14
|
end
|
26
15
|
|
27
16
|
# Send a signal to all method calls waiting for the given name
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
case tasks
|
33
|
-
when Array
|
34
|
-
tasks.each { |task| run_task task, value }
|
35
|
-
true if tasks.size > 0
|
36
|
-
when NilClass
|
37
|
-
false
|
38
|
-
else
|
39
|
-
run_task tasks, value
|
40
|
-
true
|
17
|
+
def broadcast(name, value = nil)
|
18
|
+
if condition = @conditions.delete(name)
|
19
|
+
condition.broadcast(value)
|
41
20
|
end
|
42
21
|
end
|
43
|
-
|
44
|
-
# Run the given task, reporting errors that occur
|
45
|
-
def run_task(task, value)
|
46
|
-
task.resume(value)
|
47
|
-
rescue => ex
|
48
|
-
Logger.crash("signaling error", ex)
|
49
|
-
end
|
50
22
|
end
|
51
23
|
end
|
data/lib/celluloid/stack_dump.rb
CHANGED
@@ -1,16 +1,60 @@
|
|
1
1
|
module Celluloid
|
2
2
|
class StackDump
|
3
|
+
module DisplayBacktrace
|
4
|
+
def display_backtrace(backtrace, output, indent = nil)
|
5
|
+
backtrace ||= ["EMPTY BACKTRACE"]
|
6
|
+
backtrace.each do |line|
|
7
|
+
output << indent if indent
|
8
|
+
output << "\t" << line << "\n"
|
9
|
+
end
|
10
|
+
output << "\n\n"
|
11
|
+
end
|
12
|
+
end
|
3
13
|
|
4
|
-
class TaskState < Struct.new(:task_class, :status, :backtrace)
|
14
|
+
class TaskState < Struct.new(:task_class, :type, :meta, :status, :backtrace)
|
5
15
|
end
|
6
16
|
|
7
17
|
class ActorState
|
18
|
+
include DisplayBacktrace
|
19
|
+
|
8
20
|
attr_accessor :subject_id, :subject_class, :name
|
9
21
|
attr_accessor :status, :tasks
|
10
22
|
attr_accessor :backtrace
|
23
|
+
|
24
|
+
def dump
|
25
|
+
string = ""
|
26
|
+
string << "Celluloid::Actor 0x#{subject_id.to_s(16)}: #{subject_class}"
|
27
|
+
string << " [#{name}]" if name
|
28
|
+
string << "\n"
|
29
|
+
|
30
|
+
if status == :idle
|
31
|
+
string << "State: Idle (waiting for messages)\n"
|
32
|
+
display_backtrace backtrace, string
|
33
|
+
else
|
34
|
+
string << "State: Running (executing tasks)\n"
|
35
|
+
display_backtrace backtrace, string
|
36
|
+
string << "\tTasks:\n"
|
37
|
+
|
38
|
+
tasks.each_with_index do |task, i|
|
39
|
+
string << "\t #{i+1}) #{task.task_class}[#{task.type}]: #{task.status}\n"
|
40
|
+
string << "\t #{task.meta.inspect}\n"
|
41
|
+
display_backtrace task.backtrace, string, "\t"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
string
|
46
|
+
end
|
11
47
|
end
|
12
48
|
|
13
49
|
class ThreadState < Struct.new(:thread_id, :backtrace)
|
50
|
+
include DisplayBacktrace
|
51
|
+
|
52
|
+
def dump
|
53
|
+
string = ""
|
54
|
+
string << "Thread 0x#{thread_id.to_s(16)}:\n"
|
55
|
+
display_backtrace backtrace, string
|
56
|
+
string
|
57
|
+
end
|
14
58
|
end
|
15
59
|
|
16
60
|
attr_accessor :actors, :threads
|
@@ -23,9 +67,8 @@ module Celluloid
|
|
23
67
|
end
|
24
68
|
|
25
69
|
def snapshot
|
26
|
-
|
27
|
-
if thread.
|
28
|
-
next unless thread.role == :actor
|
70
|
+
Celluloid.internal_pool.each do |thread|
|
71
|
+
if thread.role == :actor
|
29
72
|
@actors << snapshot_actor(thread.actor) if thread.actor
|
30
73
|
else
|
31
74
|
@threads << snapshot_thread(thread)
|
@@ -43,7 +86,7 @@ module Celluloid
|
|
43
86
|
state.status = :idle
|
44
87
|
else
|
45
88
|
state.status = :running
|
46
|
-
state.tasks = tasks.
|
89
|
+
state.tasks = tasks.to_a.map { |t| TaskState.new(t.class, t.type, t.meta, t.status, t.backtrace) }
|
47
90
|
end
|
48
91
|
|
49
92
|
state.backtrace = actor.thread.backtrace if actor.thread
|
@@ -56,41 +99,11 @@ module Celluloid
|
|
56
99
|
|
57
100
|
def dump(output = STDERR)
|
58
101
|
@actors.each do |actor|
|
59
|
-
|
60
|
-
string << "Celluloid::Actor 0x#{actor.subject_id.to_s(16)}: #{actor.subject_class}"
|
61
|
-
string << " [#{actor.name}]" if actor.name
|
62
|
-
string << "\n"
|
63
|
-
|
64
|
-
if actor.status == :idle
|
65
|
-
string << "State: Idle (waiting for messages)\n"
|
66
|
-
display_backtrace actor.backtrace, string
|
67
|
-
else
|
68
|
-
string << "State: Running (executing tasks)\n"
|
69
|
-
display_backtrace actor.backtrace, string
|
70
|
-
string << "Tasks:\n"
|
71
|
-
|
72
|
-
actor.tasks.each_with_index do |task, i|
|
73
|
-
string << " #{i+1}) #{task.task_class}: #{task.status}\n"
|
74
|
-
display_backtrace task.backtrace, string
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
output.print string
|
102
|
+
output.print actor.dump
|
79
103
|
end
|
80
104
|
|
81
105
|
@threads.each do |thread|
|
82
|
-
|
83
|
-
string << "Thread 0x#{thread.thread_id.to_s(16)}:\n"
|
84
|
-
display_backtrace thread.backtrace, string
|
85
|
-
output.print string
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
def display_backtrace(backtrace, output)
|
90
|
-
if backtrace
|
91
|
-
output << "\t" << backtrace.join("\n\t") << "\n\n"
|
92
|
-
else
|
93
|
-
output << "EMPTY BACKTRACE\n\n"
|
106
|
+
output.print thread.dump
|
94
107
|
end
|
95
108
|
end
|
96
109
|
end
|
@@ -11,8 +11,8 @@ module Celluloid
|
|
11
11
|
end
|
12
12
|
|
13
13
|
# Start this application (and watch it with a supervisor)
|
14
|
-
def run!
|
15
|
-
group = new do |_group|
|
14
|
+
def run!(registry = nil)
|
15
|
+
group = new(registry) do |_group|
|
16
16
|
blocks.each do |block|
|
17
17
|
block.call(_group)
|
18
18
|
end
|
@@ -21,9 +21,9 @@ module Celluloid
|
|
21
21
|
end
|
22
22
|
|
23
23
|
# Run the application in the foreground with a simple watchdog
|
24
|
-
def run
|
24
|
+
def run(registry = nil)
|
25
25
|
loop do
|
26
|
-
supervisor = run!
|
26
|
+
supervisor = run!(registry)
|
27
27
|
|
28
28
|
# Take five, toplevel supervisor
|
29
29
|
sleep 5 while supervisor.alive?
|
@@ -149,7 +149,7 @@ module Celluloid
|
|
149
149
|
|
150
150
|
def terminate
|
151
151
|
@actor.terminate if @actor
|
152
|
-
rescue DeadActorError
|
152
|
+
rescue DeadActorError
|
153
153
|
end
|
154
154
|
end
|
155
155
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Celluloid
|
5
|
+
if defined? JRUBY_VERSION
|
6
|
+
require 'jruby/synchronized'
|
7
|
+
|
8
|
+
class TaskSet
|
9
|
+
extend Forwardable
|
10
|
+
include JRuby::Synchronized
|
11
|
+
|
12
|
+
def_delegators :@tasks, :<<, :delete, :first, :empty?, :to_a
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
@tasks = Set.new
|
16
|
+
end
|
17
|
+
end
|
18
|
+
elsif defined? Rubinius
|
19
|
+
class TaskSet
|
20
|
+
def initialize
|
21
|
+
@tasks = Set.new
|
22
|
+
end
|
23
|
+
|
24
|
+
def <<(task)
|
25
|
+
Rubinius.synchronize(self) { @tasks << task }
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete(task)
|
29
|
+
Rubinius.synchronize(self) { @tasks.delete task }
|
30
|
+
end
|
31
|
+
|
32
|
+
def first
|
33
|
+
Rubinius.synchronize(self) { @tasks.first }
|
34
|
+
end
|
35
|
+
|
36
|
+
def empty?
|
37
|
+
Rubinius.synchronize(self) { @tasks.empty? }
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_a
|
41
|
+
Rubinius.synchronize(self) { @tasks.to_a }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
# Assume we're on MRI, where we have the GIL. But what about IronRuby?
|
46
|
+
# Or MacRuby. Do people care? This will break Celluloid::StackDumps
|
47
|
+
TaskSet = Set
|
48
|
+
end
|
49
|
+
end
|