celluloid 0.13.0 → 0.14.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 +7 -0
- data/README.md +1 -2
- data/lib/celluloid.rb +84 -32
- data/lib/celluloid/actor.rb +35 -30
- data/lib/celluloid/autostart.rb +0 -13
- data/lib/celluloid/calls.rb +71 -23
- data/lib/celluloid/core_ext.rb +3 -14
- data/lib/celluloid/cpu_counter.rb +1 -1
- data/lib/celluloid/evented_mailbox.rb +82 -0
- data/lib/celluloid/fsm.rb +2 -0
- data/lib/celluloid/future.rb +4 -4
- data/lib/celluloid/internal_pool.rb +11 -8
- data/lib/celluloid/legacy.rb +14 -13
- data/lib/celluloid/logging/incident_logger.rb +2 -2
- data/lib/celluloid/mailbox.rb +16 -0
- data/lib/celluloid/method.rb +7 -7
- data/lib/celluloid/notifications.rb +1 -1
- data/lib/celluloid/proxies/abstract_proxy.rb +1 -1
- data/lib/celluloid/proxies/actor_proxy.rb +23 -27
- data/lib/celluloid/proxies/async_proxy.rb +18 -6
- data/lib/celluloid/proxies/block_proxy.rb +29 -0
- data/lib/celluloid/proxies/future_proxy.rb +9 -3
- data/lib/celluloid/proxies/sync_proxy.rb +31 -0
- data/lib/celluloid/receivers.rb +1 -1
- data/lib/celluloid/responses.rb +13 -1
- data/lib/celluloid/stack_dump.rb +8 -5
- data/lib/celluloid/supervision_group.rb +6 -4
- data/lib/celluloid/tasks.rb +63 -2
- data/lib/celluloid/tasks/task_fiber.rb +6 -46
- data/lib/celluloid/tasks/task_thread.rb +8 -44
- data/lib/celluloid/thread.rb +82 -0
- data/lib/celluloid/thread_handle.rb +3 -2
- data/lib/celluloid/version.rb +1 -1
- data/spec/support/actor_examples.rb +116 -53
- data/spec/support/example_actor_class.rb +7 -1
- data/spec/support/mailbox_examples.rb +29 -3
- data/spec/support/task_examples.rb +11 -9
- metadata +21 -29
@@ -3,20 +3,32 @@ module Celluloid
|
|
3
3
|
class AsyncProxy < AbstractProxy
|
4
4
|
attr_reader :mailbox
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@mailbox, @klass =
|
6
|
+
def initialize(mailbox, klass)
|
7
|
+
@mailbox, @klass = mailbox, klass
|
8
8
|
end
|
9
9
|
|
10
10
|
def inspect
|
11
11
|
"#<Celluloid::AsyncProxy(#{@klass})>"
|
12
12
|
end
|
13
13
|
|
14
|
-
# method_missing black magic to call bang predicate methods asynchronously
|
15
14
|
def method_missing(meth, *args, &block)
|
16
15
|
if @mailbox == ::Thread.current[:celluloid_mailbox]
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
args.unshift meth
|
17
|
+
meth = :__send__
|
18
|
+
end
|
19
|
+
|
20
|
+
if block_given?
|
21
|
+
# FIXME: nicer exception
|
22
|
+
raise "Cannot use blocks with async yet"
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
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)
|
20
32
|
end
|
21
33
|
end
|
22
34
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Celluloid
|
2
|
+
class BlockProxy
|
3
|
+
def initialize(call, mailbox, block)
|
4
|
+
@call = call
|
5
|
+
@mailbox = mailbox
|
6
|
+
@block = block
|
7
|
+
@execution = :sender
|
8
|
+
end
|
9
|
+
attr_writer :execution
|
10
|
+
attr_reader :call, :block
|
11
|
+
|
12
|
+
def to_proc
|
13
|
+
if @execution == :sender
|
14
|
+
lambda do |*values|
|
15
|
+
if task = Thread.current[:celluloid_task]
|
16
|
+
@mailbox << BlockCall.new(self, Actor.current.mailbox, values)
|
17
|
+
# TODO: if respond fails, the Task will never be resumed
|
18
|
+
task.suspend(:invokeblock)
|
19
|
+
else
|
20
|
+
# FIXME: better exception
|
21
|
+
raise "No task to suspend"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
else
|
25
|
+
@block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,8 +3,8 @@ module Celluloid
|
|
3
3
|
class FutureProxy < AbstractProxy
|
4
4
|
attr_reader :mailbox
|
5
5
|
|
6
|
-
def initialize(
|
7
|
-
@mailbox, @klass =
|
6
|
+
def initialize(mailbox, klass)
|
7
|
+
@mailbox, @klass = mailbox, klass
|
8
8
|
end
|
9
9
|
|
10
10
|
def inspect
|
@@ -13,7 +13,13 @@ module Celluloid
|
|
13
13
|
|
14
14
|
# method_missing black magic to call bang predicate methods asynchronously
|
15
15
|
def method_missing(meth, *args, &block)
|
16
|
-
|
16
|
+
if block_given?
|
17
|
+
# FIXME: nicer exception
|
18
|
+
raise "Cannot use blocks with futures yet"
|
19
|
+
end
|
20
|
+
future = Future.new
|
21
|
+
future.execute(@mailbox, meth, args, block)
|
22
|
+
future
|
17
23
|
end
|
18
24
|
end
|
19
25
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# A proxy which sends synchronous calls to an actor
|
3
|
+
class SyncProxy < AbstractProxy
|
4
|
+
attr_reader :mailbox
|
5
|
+
|
6
|
+
def initialize(mailbox, klass)
|
7
|
+
@mailbox, @klass = mailbox, klass
|
8
|
+
end
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"#<Celluloid::SyncProxy(#{@klass})>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(meth, *args, &block)
|
15
|
+
if @mailbox == ::Thread.current[:celluloid_mailbox]
|
16
|
+
args.unshift meth
|
17
|
+
meth = :__send__
|
18
|
+
end
|
19
|
+
|
20
|
+
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
|
+
|
28
|
+
call.value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/celluloid/receivers.rb
CHANGED
data/lib/celluloid/responses.rb
CHANGED
@@ -15,7 +15,7 @@ module Celluloid
|
|
15
15
|
# Call completed successfully
|
16
16
|
class SuccessResponse < Response; end
|
17
17
|
|
18
|
-
# Call was aborted due to
|
18
|
+
# Call was aborted due to sender error
|
19
19
|
class ErrorResponse < Response
|
20
20
|
def value
|
21
21
|
ex = super
|
@@ -29,4 +29,16 @@ module Celluloid
|
|
29
29
|
raise ex
|
30
30
|
end
|
31
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
|
+
|
32
44
|
end
|
data/lib/celluloid/stack_dump.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Celluloid
|
2
2
|
class StackDump
|
3
3
|
|
4
|
-
class TaskState < Struct.new(:task_class, :status)
|
4
|
+
class TaskState < Struct.new(:task_class, :status, :backtrace)
|
5
5
|
end
|
6
6
|
|
7
7
|
class ActorState
|
@@ -24,8 +24,9 @@ module Celluloid
|
|
24
24
|
|
25
25
|
def snapshot
|
26
26
|
Thread.list.each do |thread|
|
27
|
-
if
|
28
|
-
|
27
|
+
if thread.celluloid?
|
28
|
+
next if thread.task
|
29
|
+
@actors << snapshot_actor(thread.actor) if thread.actor
|
29
30
|
else
|
30
31
|
@threads << snapshot_thread(thread)
|
31
32
|
end
|
@@ -42,7 +43,7 @@ module Celluloid
|
|
42
43
|
state.status = :idle
|
43
44
|
else
|
44
45
|
state.status = :running
|
45
|
-
state.tasks = tasks.collect { |t| TaskState.new(t.class, t.status) }
|
46
|
+
state.tasks = tasks.collect { |t| TaskState.new(t.class, t.status, t.backtrace) }
|
46
47
|
end
|
47
48
|
|
48
49
|
state.backtrace = actor.thread.backtrace if actor.thread
|
@@ -62,16 +63,18 @@ module Celluloid
|
|
62
63
|
|
63
64
|
if actor.status == :idle
|
64
65
|
string << "State: Idle (waiting for messages)\n"
|
66
|
+
display_backtrace actor.backtrace, string
|
65
67
|
else
|
66
68
|
string << "State: Running (executing tasks)\n"
|
69
|
+
display_backtrace actor.backtrace, string
|
67
70
|
string << "Tasks:\n"
|
68
71
|
|
69
72
|
actor.tasks.each_with_index do |task, i|
|
70
73
|
string << " #{i+1}) #{task.task_class}: #{task.status}\n"
|
74
|
+
display_backtrace task.backtrace, string
|
71
75
|
end
|
72
76
|
end
|
73
77
|
|
74
|
-
display_backtrace actor.backtrace, string
|
75
78
|
output.print string
|
76
79
|
end
|
77
80
|
|
@@ -12,9 +12,9 @@ module Celluloid
|
|
12
12
|
|
13
13
|
# Start this application (and watch it with a supervisor)
|
14
14
|
def run!
|
15
|
-
group = new do |
|
15
|
+
group = new do |_group|
|
16
16
|
blocks.each do |block|
|
17
|
-
block.call(
|
17
|
+
block.call(_group)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
group
|
@@ -63,6 +63,8 @@ module Celluloid
|
|
63
63
|
yield current_actor if block_given?
|
64
64
|
end
|
65
65
|
|
66
|
+
execute_block_on_receiver :initialize, :supervise, :supervise_as
|
67
|
+
|
66
68
|
def supervise(klass, *args, &block)
|
67
69
|
add(klass, :args => args, :block => block)
|
68
70
|
end
|
@@ -95,8 +97,8 @@ module Celluloid
|
|
95
97
|
|
96
98
|
# Restart a crashed actor
|
97
99
|
def restart_actor(actor, reason)
|
98
|
-
member = @members.find do |
|
99
|
-
|
100
|
+
member = @members.find do |_member|
|
101
|
+
_member.actor == actor
|
100
102
|
end
|
101
103
|
raise "a group member went missing. This shouldn't be!" unless member
|
102
104
|
|
data/lib/celluloid/tasks.rb
CHANGED
@@ -19,10 +19,71 @@ module Celluloid
|
|
19
19
|
Task.current.suspend(status)
|
20
20
|
end
|
21
21
|
|
22
|
+
attr_reader :type, :status
|
23
|
+
|
22
24
|
# Create a new task
|
23
25
|
def initialize(type)
|
24
26
|
@type = type
|
25
27
|
@status = :new
|
28
|
+
|
29
|
+
actor = Thread.current[:celluloid_actor]
|
30
|
+
chain_id = Thread.current[:celluloid_chain_id]
|
31
|
+
|
32
|
+
raise NotActorError, "can't create tasks outside of actors" unless actor
|
33
|
+
|
34
|
+
create do
|
35
|
+
begin
|
36
|
+
@status = :running
|
37
|
+
actor.setup_thread
|
38
|
+
Thread.current[:celluloid_task] = self
|
39
|
+
Thread.current[:celluloid_chain_id] = chain_id
|
40
|
+
|
41
|
+
actor.tasks << self
|
42
|
+
yield
|
43
|
+
rescue Task::TerminatedError
|
44
|
+
# Task was explicitly terminated
|
45
|
+
ensure
|
46
|
+
@status = :dead
|
47
|
+
actor.tasks.delete self
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def create(&block)
|
53
|
+
raise "Implement #{self.class}#create"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Suspend the current task, changing the status to the given argument
|
57
|
+
def suspend(status)
|
58
|
+
@status = status
|
59
|
+
value = signal
|
60
|
+
|
61
|
+
raise value if value.is_a?(Task::TerminatedError)
|
62
|
+
@status = :running
|
63
|
+
|
64
|
+
value
|
65
|
+
end
|
66
|
+
|
67
|
+
# Resume a suspended task, giving it a value to return if needed
|
68
|
+
def resume(value = nil)
|
69
|
+
deliver(value)
|
70
|
+
nil
|
71
|
+
end
|
72
|
+
|
73
|
+
# Terminate this task
|
74
|
+
def terminate
|
75
|
+
resume Task::TerminatedError.new("task was terminated") if running?
|
76
|
+
end
|
77
|
+
|
78
|
+
def backtrace
|
79
|
+
end
|
80
|
+
|
81
|
+
# Is the current task still running?
|
82
|
+
def running?; @status != :dead; end
|
83
|
+
|
84
|
+
# Nicer string inspect for tasks
|
85
|
+
def inspect
|
86
|
+
"#<#{self.class}:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @status=#{@status.inspect}>"
|
26
87
|
end
|
27
88
|
end
|
28
89
|
|
@@ -42,7 +103,7 @@ module Celluloid
|
|
42
103
|
end
|
43
104
|
|
44
105
|
def each(&blk)
|
45
|
-
@tasks.each
|
106
|
+
@tasks.each(&blk)
|
46
107
|
end
|
47
108
|
|
48
109
|
def first
|
@@ -56,4 +117,4 @@ module Celluloid
|
|
56
117
|
end
|
57
118
|
|
58
119
|
require 'celluloid/tasks/task_fiber'
|
59
|
-
require 'celluloid/tasks/task_thread'
|
120
|
+
require 'celluloid/tasks/task_thread'
|
@@ -3,52 +3,20 @@ module Celluloid
|
|
3
3
|
|
4
4
|
# Tasks with a Fiber backend
|
5
5
|
class TaskFiber < Task
|
6
|
-
attr_reader :type, :status
|
7
|
-
|
8
|
-
# Run the given block within a task
|
9
|
-
def initialize(type)
|
10
|
-
super
|
11
|
-
|
12
|
-
actor = Thread.current[:celluloid_actor]
|
13
|
-
mailbox = Thread.current[:celluloid_mailbox]
|
14
|
-
chain_id = Thread.current[:celluloid_chain_id]
|
15
|
-
|
16
|
-
raise NotActorError, "can't create tasks outside of actors" unless actor
|
17
6
|
|
7
|
+
def create
|
18
8
|
@fiber = Fiber.new do
|
19
|
-
|
20
|
-
Thread.current[:celluloid_actor] = actor
|
21
|
-
Thread.current[:celluloid_mailbox] = mailbox
|
22
|
-
Thread.current[:celluloid_task] = self
|
23
|
-
Thread.current[:celluloid_chain_id] = chain_id
|
24
|
-
|
25
|
-
actor.tasks << self
|
26
|
-
|
27
|
-
begin
|
28
|
-
yield
|
29
|
-
rescue Task::TerminatedError
|
30
|
-
# Task was explicitly terminated
|
31
|
-
ensure
|
32
|
-
@status = :dead
|
33
|
-
actor.tasks.delete self
|
34
|
-
end
|
9
|
+
yield
|
35
10
|
end
|
36
11
|
end
|
37
12
|
|
38
|
-
|
39
|
-
|
40
|
-
@status = status
|
41
|
-
result = Fiber.yield
|
42
|
-
raise result if result.is_a?(Task::TerminatedError)
|
43
|
-
@status = :running
|
44
|
-
|
45
|
-
result
|
13
|
+
def signal
|
14
|
+
Fiber.yield
|
46
15
|
end
|
47
16
|
|
48
17
|
# Resume a suspended task, giving it a value to return if needed
|
49
|
-
def
|
18
|
+
def deliver(value)
|
50
19
|
@fiber.resume value
|
51
|
-
nil
|
52
20
|
rescue SystemStackError => ex
|
53
21
|
raise FiberStackError, "#{ex} (please see https://github.com/celluloid/celluloid/wiki/Fiber-stack-errors)"
|
54
22
|
rescue FiberError => ex
|
@@ -57,17 +25,9 @@ module Celluloid
|
|
57
25
|
|
58
26
|
# Terminate this task
|
59
27
|
def terminate
|
60
|
-
|
28
|
+
super
|
61
29
|
rescue FiberError
|
62
30
|
# If we're getting this the task should already be dead
|
63
31
|
end
|
64
|
-
|
65
|
-
# Is the current task still running?
|
66
|
-
def running?; @fiber.alive?; end
|
67
|
-
|
68
|
-
# Nicer string inspect for tasks
|
69
|
-
def inspect
|
70
|
-
"<Celluloid::TaskFiber:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @status=#{@status.inspect}>"
|
71
|
-
end
|
72
32
|
end
|
73
33
|
end
|
@@ -1,62 +1,37 @@
|
|
1
1
|
module Celluloid
|
2
2
|
# Tasks with a Thread backend
|
3
3
|
class TaskThread < Task
|
4
|
-
attr_reader :type, :status
|
5
|
-
|
6
4
|
# Run the given block within a task
|
7
5
|
def initialize(type)
|
8
|
-
super
|
9
|
-
|
10
6
|
@resume_queue = Queue.new
|
11
7
|
@exception_queue = Queue.new
|
12
8
|
@yield_mutex = Mutex.new
|
13
9
|
@yield_cond = ConditionVariable.new
|
14
10
|
|
15
|
-
|
16
|
-
|
17
|
-
chain_id = Thread.current[:celluloid_chain_id]
|
18
|
-
|
19
|
-
raise NotActorError, "can't create tasks outside of actors" unless actor
|
11
|
+
super
|
12
|
+
end
|
20
13
|
|
14
|
+
def create
|
21
15
|
@thread = Celluloid.internal_pool.get do
|
22
16
|
begin
|
23
17
|
ex = @resume_queue.pop
|
24
18
|
raise ex if ex.is_a?(Task::TerminatedError)
|
25
19
|
|
26
|
-
@status = :running
|
27
|
-
Thread.current[:celluloid_actor] = actor
|
28
|
-
Thread.current[:celluloid_mailbox] = mailbox
|
29
|
-
Thread.current[:celluloid_task] = self
|
30
|
-
Thread.current[:celluloid_chain_id] = chain_id
|
31
|
-
|
32
|
-
actor.tasks << self
|
33
20
|
yield
|
34
|
-
rescue Task::TerminatedError
|
35
|
-
# Task was explicitly terminated
|
36
21
|
rescue Exception => ex
|
37
22
|
@exception_queue << ex
|
38
23
|
ensure
|
39
|
-
@status = :dead
|
40
|
-
actor.tasks.delete self
|
41
24
|
@yield_cond.signal
|
42
25
|
end
|
43
26
|
end
|
44
27
|
end
|
45
28
|
|
46
|
-
|
47
|
-
def suspend(status)
|
48
|
-
@status = status
|
29
|
+
def signal
|
49
30
|
@yield_cond.signal
|
50
|
-
|
51
|
-
|
52
|
-
raise value if value.is_a?(Task::TerminatedError)
|
53
|
-
@status = :running
|
54
|
-
|
55
|
-
value
|
31
|
+
@resume_queue.pop
|
56
32
|
end
|
57
33
|
|
58
|
-
|
59
|
-
def resume(value = nil)
|
34
|
+
def deliver(value)
|
60
35
|
raise DeadTaskError, "cannot resume a dead task" unless @thread.alive?
|
61
36
|
|
62
37
|
@yield_mutex.synchronize do
|
@@ -66,23 +41,12 @@ module Celluloid
|
|
66
41
|
raise @exception_queue.pop
|
67
42
|
end
|
68
43
|
end
|
69
|
-
|
70
|
-
nil
|
71
44
|
rescue ThreadError
|
72
45
|
raise DeadTaskError, "cannot resume a dead task"
|
73
46
|
end
|
74
47
|
|
75
|
-
|
76
|
-
|
77
|
-
resume Task::TerminatedError.new("task was terminated") if @thread.alive?
|
78
|
-
end
|
79
|
-
|
80
|
-
# Is the current task still running?
|
81
|
-
def running?; @status != :dead; end
|
82
|
-
|
83
|
-
# Nicer string inspect for tasks
|
84
|
-
def inspect
|
85
|
-
"<Celluloid::TaskThread:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @status=#{@status.inspect}>"
|
48
|
+
def backtrace
|
49
|
+
@thread.backtrace
|
86
50
|
end
|
87
51
|
end
|
88
52
|
end
|