celluloid 0.11.1 → 0.12.0
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.
- data/README.md +23 -6
- data/lib/celluloid/actor.rb +146 -56
- data/lib/celluloid/boot.rb +9 -0
- data/lib/celluloid/calls.rb +3 -3
- data/lib/celluloid/core_ext.rb +1 -6
- data/lib/celluloid/cpu_counter.rb +2 -0
- data/lib/celluloid/future.rb +5 -0
- data/lib/celluloid/links.rb +1 -3
- data/lib/celluloid/mailbox.rb +14 -20
- data/lib/celluloid/method.rb +19 -0
- data/lib/celluloid/notifications.rb +16 -17
- data/lib/celluloid/pool_manager.rb +40 -11
- data/lib/celluloid/proxies/abstract_proxy.rb +17 -0
- data/lib/celluloid/{actor_proxy.rb → proxies/actor_proxy.rb} +38 -39
- data/lib/celluloid/proxies/async_proxy.rb +19 -0
- data/lib/celluloid/proxies/future_proxy.rb +19 -0
- data/lib/celluloid/registry.rb +8 -5
- data/lib/celluloid/rspec.rb +6 -0
- data/lib/celluloid/supervision_group.rb +23 -10
- data/lib/celluloid/system_events.rb +54 -0
- data/lib/celluloid/task.rb +7 -63
- data/lib/celluloid/tasks/task_fiber.rb +65 -0
- data/lib/celluloid/tasks/task_thread.rb +78 -0
- data/lib/celluloid/thread_handle.rb +2 -1
- data/lib/celluloid/version.rb +1 -1
- data/lib/celluloid.rb +149 -114
- data/spec/support/actor_examples.rb +182 -26
- data/spec/support/example_actor_class.rb +6 -2
- data/spec/support/mailbox_examples.rb +3 -18
- data/spec/support/task_examples.rb +36 -0
- metadata +21 -13
- data/lib/celluloid/events.rb +0 -26
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
module Celluloid
|
|
2
|
-
# A proxy object returned from Celluloid::Actor.
|
|
3
|
-
#
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
attr_reader :mailbox
|
|
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 ActorProxy < AbstractProxy
|
|
5
|
+
attr_reader :mailbox, :thread
|
|
7
6
|
|
|
8
7
|
def initialize(actor)
|
|
9
8
|
@mailbox, @thread, @klass = actor.mailbox, actor.thread, actor.subject.class.to_s
|
|
9
|
+
|
|
10
|
+
@async_proxy = AsyncProxy.new(actor)
|
|
11
|
+
@future_proxy = FutureProxy.new(actor)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def class
|
|
15
|
+
Actor.call @mailbox, :__send__, :class
|
|
10
16
|
end
|
|
11
17
|
|
|
12
18
|
def _send_(meth, *args, &block)
|
|
13
19
|
Actor.call @mailbox, :__send__, meth, *args, &block
|
|
14
20
|
end
|
|
15
21
|
|
|
16
|
-
def
|
|
17
|
-
Actor.call @mailbox, :
|
|
22
|
+
def inspect
|
|
23
|
+
Actor.call @mailbox, :inspect
|
|
24
|
+
rescue DeadActorError
|
|
25
|
+
"#<Celluloid::Actor(#{@klass}) dead>"
|
|
18
26
|
end
|
|
19
27
|
|
|
20
28
|
def name
|
|
@@ -29,14 +37,18 @@ module Celluloid
|
|
|
29
37
|
Actor.call @mailbox, :kind_of?, klass
|
|
30
38
|
end
|
|
31
39
|
|
|
32
|
-
def respond_to?(meth)
|
|
33
|
-
Actor.call @mailbox, :respond_to?, meth
|
|
40
|
+
def respond_to?(meth, include_private = false)
|
|
41
|
+
Actor.call @mailbox, :respond_to?, meth, include_private
|
|
34
42
|
end
|
|
35
43
|
|
|
36
44
|
def methods(include_ancestors = true)
|
|
37
45
|
Actor.call @mailbox, :methods, include_ancestors
|
|
38
46
|
end
|
|
39
47
|
|
|
48
|
+
def method(name)
|
|
49
|
+
Method.new(self, name)
|
|
50
|
+
end
|
|
51
|
+
|
|
40
52
|
def alive?
|
|
41
53
|
@mailbox.alive?
|
|
42
54
|
end
|
|
@@ -45,48 +57,35 @@ module Celluloid
|
|
|
45
57
|
Actor.call @mailbox, :to_s
|
|
46
58
|
end
|
|
47
59
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
# predicate syntax. TIMTOWTDI!
|
|
56
|
-
def async(method_name, *args, &block)
|
|
57
|
-
Actor.async @mailbox, method_name, *args, &block
|
|
60
|
+
# Obtain an async proxy or explicitly invoke a named async method
|
|
61
|
+
def async(method_name = nil, *args, &block)
|
|
62
|
+
if method_name
|
|
63
|
+
Actor.async @mailbox, method_name, *args, &block
|
|
64
|
+
else
|
|
65
|
+
@async_proxy
|
|
66
|
+
end
|
|
58
67
|
end
|
|
59
68
|
|
|
60
|
-
#
|
|
61
|
-
def future(method_name, *args, &block)
|
|
62
|
-
|
|
69
|
+
# Obtain a future proxy or explicitly invoke a named future method
|
|
70
|
+
def future(method_name = nil, *args, &block)
|
|
71
|
+
if method_name
|
|
72
|
+
Actor.future @mailbox, method_name, *args, &block
|
|
73
|
+
else
|
|
74
|
+
@future_proxy
|
|
75
|
+
end
|
|
63
76
|
end
|
|
64
77
|
|
|
65
78
|
# Terminate the associated actor
|
|
66
79
|
def terminate
|
|
67
80
|
terminate!
|
|
68
|
-
join
|
|
81
|
+
Actor.join(self)
|
|
69
82
|
nil
|
|
70
83
|
end
|
|
71
84
|
|
|
72
85
|
# Terminate the associated actor asynchronously
|
|
73
86
|
def terminate!
|
|
74
|
-
raise DeadActorError, "actor already terminated" unless alive?
|
|
75
|
-
@mailbox
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
# Forcibly kill a given actor
|
|
79
|
-
def kill
|
|
80
|
-
@thread.kill
|
|
81
|
-
begin
|
|
82
|
-
@mailbox.shutdown
|
|
83
|
-
rescue DeadActorError
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Wait for an actor to terminate
|
|
88
|
-
def join
|
|
89
|
-
@thread.join
|
|
87
|
+
::Kernel.raise DeadActorError, "actor already terminated" unless alive?
|
|
88
|
+
@mailbox << TerminationRequest.new
|
|
90
89
|
end
|
|
91
90
|
|
|
92
91
|
# method_missing black magic to call bang predicate methods asynchronously
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# A proxy which sends asynchronous calls to an actor
|
|
3
|
+
class AsyncProxy < AbstractProxy
|
|
4
|
+
attr_reader :mailbox
|
|
5
|
+
|
|
6
|
+
def initialize(actor)
|
|
7
|
+
@mailbox, @klass = actor.mailbox, actor.subject.class.to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def inspect
|
|
11
|
+
"#<Celluloid::AsyncProxy(#{@klass})>"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# method_missing black magic to call bang predicate methods asynchronously
|
|
15
|
+
def method_missing(meth, *args, &block)
|
|
16
|
+
Actor.async @mailbox, meth, *args, &block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# A proxy which creates future calls to an actor
|
|
3
|
+
class FutureProxy < AbstractProxy
|
|
4
|
+
attr_reader :mailbox
|
|
5
|
+
|
|
6
|
+
def initialize(actor)
|
|
7
|
+
@mailbox, @klass = actor.mailbox, actor.subject.class.to_s
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def inspect
|
|
11
|
+
"#<Celluloid::FutureProxy(#{@klass})>"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# method_missing black magic to call bang predicate methods asynchronously
|
|
15
|
+
def method_missing(meth, *args, &block)
|
|
16
|
+
Actor.future @mailbox, meth, *args, &block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
data/lib/celluloid/registry.rb
CHANGED
|
@@ -3,8 +3,8 @@ require 'thread'
|
|
|
3
3
|
module Celluloid
|
|
4
4
|
# The Registry allows us to refer to specific actors by human-meaningful names
|
|
5
5
|
class Registry
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
class << self
|
|
7
|
+
attr_reader :root
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def initialize
|
|
@@ -23,7 +23,7 @@ module Celluloid
|
|
|
23
23
|
@registry[name.to_sym] = actor
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
actor.mailbox
|
|
26
|
+
actor.mailbox << NamingRequest.new(name.to_sym)
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# Retrieve an actor by name
|
|
@@ -38,7 +38,7 @@ module Celluloid
|
|
|
38
38
|
|
|
39
39
|
def delete(name)
|
|
40
40
|
@registry_lock.synchronize do
|
|
41
|
-
@registry
|
|
41
|
+
@registry.delete name.to_sym
|
|
42
42
|
end
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -48,7 +48,7 @@ module Celluloid
|
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
# removes and returns all registered actors as a hash of `name => actor`
|
|
51
|
-
# can be used in testing to clear the registry
|
|
51
|
+
# can be used in testing to clear the registry
|
|
52
52
|
def clear
|
|
53
53
|
hash = nil
|
|
54
54
|
@registry_lock.synchronize do
|
|
@@ -57,5 +57,8 @@ module Celluloid
|
|
|
57
57
|
end
|
|
58
58
|
hash
|
|
59
59
|
end
|
|
60
|
+
|
|
61
|
+
# Create the default registry
|
|
62
|
+
@root = new
|
|
60
63
|
end
|
|
61
64
|
end
|
data/lib/celluloid/rspec.rb
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
+
require File.expand_path('../../../spec/support/example_actor_class', __FILE__)
|
|
1
2
|
require File.expand_path('../../../spec/support/actor_examples', __FILE__)
|
|
2
3
|
require File.expand_path('../../../spec/support/mailbox_examples', __FILE__)
|
|
4
|
+
|
|
5
|
+
module Celluloid
|
|
6
|
+
# Timer accuracy enforced by the tests (50ms)
|
|
7
|
+
TIMER_QUANTUM = 0.05
|
|
8
|
+
end
|
|
@@ -28,7 +28,7 @@ module Celluloid
|
|
|
28
28
|
# Take five, toplevel supervisor
|
|
29
29
|
sleep 5 while supervisor.alive?
|
|
30
30
|
|
|
31
|
-
Logger.error "!!! Celluloid::
|
|
31
|
+
Logger.error "!!! Celluloid::SupervisionGroup #{self} crashed. Restarting..."
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
@@ -44,9 +44,13 @@ module Celluloid
|
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Register a pool of actors to be launched on group startup
|
|
47
|
-
|
|
47
|
+
# Available options are:
|
|
48
|
+
#
|
|
49
|
+
# * as: register this application in the Celluloid::Actor[] directory
|
|
50
|
+
# * args: start the actor pool with the given arguments
|
|
51
|
+
def pool(klass, options = {})
|
|
48
52
|
blocks << lambda do |group|
|
|
49
|
-
group.pool klass
|
|
53
|
+
group.pool klass, options
|
|
50
54
|
end
|
|
51
55
|
end
|
|
52
56
|
end
|
|
@@ -67,8 +71,9 @@ module Celluloid
|
|
|
67
71
|
add(klass, :args => args, :block => block, :as => name)
|
|
68
72
|
end
|
|
69
73
|
|
|
70
|
-
def pool(klass)
|
|
71
|
-
|
|
74
|
+
def pool(klass, options = {})
|
|
75
|
+
options[:method] = 'pool_link'
|
|
76
|
+
add(klass, options)
|
|
72
77
|
end
|
|
73
78
|
|
|
74
79
|
def add(klass, options)
|
|
@@ -106,16 +111,27 @@ module Celluloid
|
|
|
106
111
|
options = options.inject({}) { |h,(k,v)| h[k.to_s] = v; h }
|
|
107
112
|
|
|
108
113
|
@name = options['as']
|
|
109
|
-
@args = options['args'] ? Array(options['args']) : []
|
|
110
114
|
@block = options['block']
|
|
115
|
+
@args = options['args'] ? Array(options['args']) : []
|
|
111
116
|
@method = options['method'] || 'new_link'
|
|
117
|
+
@pool = @method == 'pool_link'
|
|
118
|
+
@pool_size = options['size'] if @pool
|
|
112
119
|
|
|
113
120
|
start
|
|
114
121
|
end
|
|
115
122
|
attr_reader :name, :actor
|
|
116
123
|
|
|
117
124
|
def start
|
|
118
|
-
|
|
125
|
+
# when it is a pool, then we don't splat the args
|
|
126
|
+
# and we need to extract the pool size if set
|
|
127
|
+
if @pool
|
|
128
|
+
options = {:args => @args}.tap do |hash|
|
|
129
|
+
hash[:size] = @pool_size if @pool_size
|
|
130
|
+
end
|
|
131
|
+
@actor = @klass.send(@method, options, &@block)
|
|
132
|
+
else
|
|
133
|
+
@actor = @klass.send(@method, *@args, &@block)
|
|
134
|
+
end
|
|
119
135
|
@registry[@name] = @actor if @name
|
|
120
136
|
end
|
|
121
137
|
|
|
@@ -135,7 +151,4 @@ module Celluloid
|
|
|
135
151
|
end
|
|
136
152
|
end
|
|
137
153
|
end
|
|
138
|
-
|
|
139
|
-
# Legacy support for the old name
|
|
140
|
-
Group = SupervisionGroup
|
|
141
154
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# High-priority internal system events
|
|
3
|
+
class SystemEvent; end
|
|
4
|
+
|
|
5
|
+
# Request to link with another actor
|
|
6
|
+
class LinkingRequest < SystemEvent
|
|
7
|
+
attr_reader :actor, :type
|
|
8
|
+
|
|
9
|
+
def initialize(actor, type)
|
|
10
|
+
@actor, @type = actor, type.to_sym
|
|
11
|
+
raise ArgumentError, "type must be link or unlink" unless [:link, :unlink].include?(@type)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def process(links)
|
|
15
|
+
case type
|
|
16
|
+
when :link then links << actor
|
|
17
|
+
when :unlink then links.delete actor
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
actor.mailbox << LinkingResponse.new(Actor.current, type)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Response to a link request
|
|
25
|
+
class LinkingResponse
|
|
26
|
+
attr_reader :actor, :type
|
|
27
|
+
|
|
28
|
+
def initialize(actor, type)
|
|
29
|
+
@actor, @type = actor, type.to_sym
|
|
30
|
+
raise ArgumentError, "type must be link or unlink" unless [:link, :unlink].include?(@type)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# An actor has exited for the given reason
|
|
35
|
+
class ExitEvent < SystemEvent
|
|
36
|
+
attr_reader :actor, :reason
|
|
37
|
+
|
|
38
|
+
def initialize(actor, reason = nil)
|
|
39
|
+
@actor, @reason = actor, reason
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Name an actor at the time it's registered
|
|
44
|
+
class NamingRequest < SystemEvent
|
|
45
|
+
attr_reader :name
|
|
46
|
+
|
|
47
|
+
def initialize(name)
|
|
48
|
+
@name = name
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Request for an actor to terminate
|
|
53
|
+
class TerminationRequest < SystemEvent; end
|
|
54
|
+
end
|
data/lib/celluloid/task.rb
CHANGED
|
@@ -1,78 +1,22 @@
|
|
|
1
|
+
require 'celluloid/tasks/task_fiber'
|
|
2
|
+
require 'celluloid/tasks/task_thread'
|
|
3
|
+
|
|
1
4
|
module Celluloid
|
|
2
5
|
# Trying to resume a dead task
|
|
3
6
|
class DeadTaskError < StandardError; end
|
|
4
7
|
|
|
5
8
|
# Tasks are interruptable/resumable execution contexts used to run methods
|
|
6
|
-
|
|
7
|
-
class TerminatedError < StandardError; end # kill a running
|
|
8
|
-
|
|
9
|
-
attr_reader :type
|
|
10
|
-
attr_accessor :status
|
|
9
|
+
module Task
|
|
10
|
+
class TerminatedError < StandardError; end # kill a running task
|
|
11
11
|
|
|
12
12
|
# Obtain the current task
|
|
13
13
|
def self.current
|
|
14
|
-
|
|
14
|
+
Thread.current[:task] or raise "not within a task context"
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# Suspend the running task, deferring to the scheduler
|
|
18
18
|
def self.suspend(status)
|
|
19
|
-
|
|
20
|
-
task.status = status
|
|
21
|
-
|
|
22
|
-
result = Fiber.yield
|
|
23
|
-
raise TerminatedError, "task was terminated" if result == TerminatedError
|
|
24
|
-
task.status = :running
|
|
25
|
-
|
|
26
|
-
result
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Run the given block within a task
|
|
30
|
-
def initialize(type)
|
|
31
|
-
@type = type
|
|
32
|
-
@status = :new
|
|
33
|
-
|
|
34
|
-
actor = Thread.current[:actor]
|
|
35
|
-
mailbox = Thread.current[:mailbox]
|
|
36
|
-
|
|
37
|
-
@fiber = Fiber.new do
|
|
38
|
-
@status = :running
|
|
39
|
-
Thread.current[:actor] = actor
|
|
40
|
-
Thread.current[:mailbox] = mailbox
|
|
41
|
-
Fiber.current.task = self
|
|
42
|
-
actor.tasks << self
|
|
43
|
-
|
|
44
|
-
begin
|
|
45
|
-
yield
|
|
46
|
-
rescue TerminatedError
|
|
47
|
-
# Task was explicitly terminated
|
|
48
|
-
ensure
|
|
49
|
-
@status = :dead
|
|
50
|
-
actor.tasks.delete self
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Resume a suspended task, giving it a value to return if needed
|
|
56
|
-
def resume(value = nil)
|
|
57
|
-
@fiber.resume value
|
|
58
|
-
nil
|
|
59
|
-
rescue FiberError
|
|
60
|
-
raise DeadTaskError, "cannot resume a dead task"
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Terminate this task
|
|
64
|
-
def terminate
|
|
65
|
-
resume TerminatedError if @fiber.alive?
|
|
66
|
-
rescue FiberError
|
|
67
|
-
# If we're getting this the task should already be dead
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Is the current task still running?
|
|
71
|
-
def running?; @fiber.alive?; end
|
|
72
|
-
|
|
73
|
-
# Nicer string inspect for tasks
|
|
74
|
-
def inspect
|
|
75
|
-
"<Celluloid::Task:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @status=#{@status.inspect}, @running=#{@fiber.alive?}>"
|
|
19
|
+
Task.current.suspend(status)
|
|
76
20
|
end
|
|
77
21
|
end
|
|
78
22
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# Tasks with a Fiber backend
|
|
3
|
+
class TaskFiber
|
|
4
|
+
attr_reader :type, :status
|
|
5
|
+
|
|
6
|
+
# Run the given block within a task
|
|
7
|
+
def initialize(type)
|
|
8
|
+
@type = type
|
|
9
|
+
@status = :new
|
|
10
|
+
|
|
11
|
+
actor, mailbox = Thread.current[:actor], Thread.current[:mailbox]
|
|
12
|
+
raise NotActorError, "can't create tasks outside of actors" unless actor
|
|
13
|
+
|
|
14
|
+
@fiber = Fiber.new do
|
|
15
|
+
@status = :running
|
|
16
|
+
Thread.current[:actor] = actor
|
|
17
|
+
Thread.current[:mailbox] = mailbox
|
|
18
|
+
Thread.current[:task] = self
|
|
19
|
+
actor.tasks << self
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
yield
|
|
23
|
+
rescue Task::TerminatedError
|
|
24
|
+
# Task was explicitly terminated
|
|
25
|
+
ensure
|
|
26
|
+
@status = :dead
|
|
27
|
+
actor.tasks.delete self
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Suspend the current task, changing the status to the given argument
|
|
33
|
+
def suspend(status)
|
|
34
|
+
@status = status
|
|
35
|
+
result = Fiber.yield
|
|
36
|
+
raise result if result.is_a?(Task::TerminatedError)
|
|
37
|
+
@status = :running
|
|
38
|
+
|
|
39
|
+
result
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Resume a suspended task, giving it a value to return if needed
|
|
43
|
+
def resume(value = nil)
|
|
44
|
+
@fiber.resume value
|
|
45
|
+
nil
|
|
46
|
+
rescue FiberError
|
|
47
|
+
raise DeadTaskError, "cannot resume a dead task"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Terminate this task
|
|
51
|
+
def terminate
|
|
52
|
+
resume Task::TerminatedError.new("task was terminated") if @fiber.alive?
|
|
53
|
+
rescue FiberError
|
|
54
|
+
# If we're getting this the task should already be dead
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Is the current task still running?
|
|
58
|
+
def running?; @fiber.alive?; end
|
|
59
|
+
|
|
60
|
+
# Nicer string inspect for tasks
|
|
61
|
+
def inspect
|
|
62
|
+
"<Celluloid::TaskFiber:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @status=#{@status.inspect}>"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# Tasks with a Thread backend
|
|
3
|
+
class TaskThread
|
|
4
|
+
attr_reader :type, :status
|
|
5
|
+
|
|
6
|
+
# Run the given block within a task
|
|
7
|
+
def initialize(type)
|
|
8
|
+
@type = type
|
|
9
|
+
@status = :new
|
|
10
|
+
|
|
11
|
+
@resume_queue = Queue.new
|
|
12
|
+
@yield_mutex = Mutex.new
|
|
13
|
+
@yield_cond = ConditionVariable.new
|
|
14
|
+
|
|
15
|
+
actor, mailbox = Thread.current[:actor], Thread.current[:mailbox]
|
|
16
|
+
raise NotActorError, "can't create tasks outside of actors" unless actor
|
|
17
|
+
|
|
18
|
+
@thread = InternalPool.get do
|
|
19
|
+
begin
|
|
20
|
+
unless @resume_queue.pop.is_a?(Task::TerminatedError)
|
|
21
|
+
@status = :running
|
|
22
|
+
Thread.current[:actor] = actor
|
|
23
|
+
Thread.current[:mailbox] = mailbox
|
|
24
|
+
Thread.current[:task] = self
|
|
25
|
+
actor.tasks << self
|
|
26
|
+
|
|
27
|
+
yield
|
|
28
|
+
end
|
|
29
|
+
rescue Task::TerminatedError
|
|
30
|
+
# Task was explicitly terminated
|
|
31
|
+
ensure
|
|
32
|
+
@status = :dead
|
|
33
|
+
actor.tasks.delete self
|
|
34
|
+
@yield_cond.signal
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Suspend the current task, changing the status to the given argument
|
|
40
|
+
def suspend(status)
|
|
41
|
+
@status = status
|
|
42
|
+
@yield_cond.signal
|
|
43
|
+
value = @resume_queue.pop
|
|
44
|
+
|
|
45
|
+
raise value if value.is_a?(Task::TerminatedError)
|
|
46
|
+
@status = :running
|
|
47
|
+
|
|
48
|
+
value
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Resume a suspended task, giving it a value to return if needed
|
|
52
|
+
def resume(value = nil)
|
|
53
|
+
raise DeadTaskError, "cannot resume a dead task" unless @thread.alive?
|
|
54
|
+
|
|
55
|
+
@yield_mutex.synchronize do
|
|
56
|
+
@resume_queue.push(value)
|
|
57
|
+
@yield_cond.wait(@yield_mutex)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
nil
|
|
61
|
+
rescue ThreadError
|
|
62
|
+
raise DeadTaskError, "cannot resume a dead task"
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Terminate this task
|
|
66
|
+
def terminate
|
|
67
|
+
resume Task::TerminatedError.new("task was terminated") if @thread.alive?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Is the current task still running?
|
|
71
|
+
def running?; @status != :dead; end
|
|
72
|
+
|
|
73
|
+
# Nicer string inspect for tasks
|
|
74
|
+
def inspect
|
|
75
|
+
"<Celluloid::TaskThread:0x#{object_id.to_s(16)} @type=#{@type.inspect}, @status=#{@status.inspect}>"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -27,12 +27,13 @@ module Celluloid
|
|
|
27
27
|
# Forcibly kill the thread
|
|
28
28
|
def kill
|
|
29
29
|
!!@mutex.synchronize { @thread.kill if @thread }
|
|
30
|
+
self
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
# Join to a running thread, blocking until it terminates
|
|
33
34
|
def join
|
|
34
35
|
@mutex.synchronize { @join.wait(@mutex) if @thread }
|
|
35
|
-
|
|
36
|
+
self
|
|
36
37
|
end
|
|
37
38
|
end
|
|
38
39
|
end
|
data/lib/celluloid/version.rb
CHANGED