celluloid 0.14.1 → 0.15.0.pre
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/celluloid/autostart.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Celluloid
|
2
|
+
class CallChain
|
3
|
+
def self.current_id=(value)
|
4
|
+
Thread.current[:celluloid_chain_id] = value
|
5
|
+
task = Thread.current[:celluloid_task]
|
6
|
+
task.chain_id = value if task
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.current_id
|
10
|
+
Thread.current[:celluloid_chain_id]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/celluloid/calls.rb
CHANGED
@@ -54,7 +54,7 @@ module Celluloid
|
|
54
54
|
class SyncCall < Call
|
55
55
|
attr_reader :sender, :task, :chain_id
|
56
56
|
|
57
|
-
def initialize(sender, method, arguments = [], block = nil, task = Thread.current[:celluloid_task], chain_id =
|
57
|
+
def initialize(sender, method, arguments = [], block = nil, task = Thread.current[:celluloid_task], chain_id = CallChain.current_id)
|
58
58
|
super(method, arguments, block)
|
59
59
|
|
60
60
|
@sender = sender
|
@@ -63,7 +63,7 @@ module Celluloid
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def dispatch(obj)
|
66
|
-
|
66
|
+
CallChain.current_id = @chain_id
|
67
67
|
result = super(obj)
|
68
68
|
respond SuccessResponse.new(self, result)
|
69
69
|
rescue Exception => ex
|
@@ -76,7 +76,7 @@ module Celluloid
|
|
76
76
|
# Otherwise, it's a bug in this actor and should be reraised
|
77
77
|
raise unless ex.is_a?(AbortError)
|
78
78
|
ensure
|
79
|
-
|
79
|
+
CallChain.current_id = nil
|
80
80
|
end
|
81
81
|
|
82
82
|
def cleanup
|
@@ -86,9 +86,6 @@ module Celluloid
|
|
86
86
|
|
87
87
|
def respond(message)
|
88
88
|
@sender << message
|
89
|
-
rescue MailboxError
|
90
|
-
# It's possible the sender exited or crashed before we could send a
|
91
|
-
# response to them.
|
92
89
|
end
|
93
90
|
|
94
91
|
def value
|
@@ -121,13 +118,13 @@ module Celluloid
|
|
121
118
|
class AsyncCall < Call
|
122
119
|
|
123
120
|
def dispatch(obj)
|
124
|
-
|
121
|
+
CallChain.current_id = Celluloid.uuid
|
125
122
|
super(obj)
|
126
123
|
rescue AbortError => ex
|
127
124
|
# Swallow aborted async calls, as they indicate the sender made a mistake
|
128
125
|
Logger.debug("#{obj.class}: async call `#@method` aborted!\n#{Logger.format_exception(ex.cause)}")
|
129
126
|
ensure
|
130
|
-
|
127
|
+
CallChain.current_id = nil
|
131
128
|
end
|
132
129
|
|
133
130
|
end
|
data/lib/celluloid/condition.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module Celluloid
|
2
|
-
class ConditionError <
|
2
|
+
class ConditionError < Celluloid::Error; end
|
3
3
|
|
4
|
-
# ConditionVariable-like signaling between tasks and
|
4
|
+
# ConditionVariable-like signaling between tasks and threads
|
5
5
|
class Condition
|
6
6
|
class Waiter
|
7
7
|
def initialize(condition, task, mailbox)
|
@@ -23,11 +23,9 @@ module Celluloid
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
attr_reader :owner
|
27
|
-
|
28
26
|
def initialize
|
29
27
|
@mutex = Mutex.new
|
30
|
-
@
|
28
|
+
@waiters = []
|
31
29
|
end
|
32
30
|
|
33
31
|
# Wait for the given signal and return the associated value
|
@@ -42,7 +40,7 @@ module Celluloid
|
|
42
40
|
waiter = Waiter.new(self, task, Celluloid.mailbox)
|
43
41
|
|
44
42
|
@mutex.synchronize do
|
45
|
-
@
|
43
|
+
@waiters << waiter
|
46
44
|
end
|
47
45
|
|
48
46
|
result = Celluloid.suspend :condwait, waiter
|
@@ -53,7 +51,7 @@ module Celluloid
|
|
53
51
|
# Send a signal to the first task waiting on this condition
|
54
52
|
def signal(value = nil)
|
55
53
|
@mutex.synchronize do
|
56
|
-
if waiter = @
|
54
|
+
if waiter = @waiters.shift
|
57
55
|
waiter << SignalConditionRequest.new(waiter.task, value)
|
58
56
|
else
|
59
57
|
Logger.debug("Celluloid::Condition signaled spuriously")
|
@@ -61,11 +59,11 @@ module Celluloid
|
|
61
59
|
end
|
62
60
|
end
|
63
61
|
|
64
|
-
# Broadcast a value to all waiting tasks
|
62
|
+
# Broadcast a value to all waiting tasks and threads
|
65
63
|
def broadcast(value = nil)
|
66
64
|
@mutex.synchronize do
|
67
|
-
@
|
68
|
-
@
|
65
|
+
@waiters.each { |waiter| waiter << SignalConditionRequest.new(waiter.task, value) }
|
66
|
+
@waiters.clear
|
69
67
|
end
|
70
68
|
end
|
71
69
|
|
@@ -4,7 +4,7 @@ module Celluloid
|
|
4
4
|
module CPUCounter
|
5
5
|
case RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
|
6
6
|
when 'darwin'
|
7
|
-
@cores = Integer(
|
7
|
+
@cores = Integer(`/usr/sbin/sysctl hw.ncpu`[/\d+/])
|
8
8
|
when 'linux'
|
9
9
|
@cores = if File.exists?("/sys/devices/system/cpu/present")
|
10
10
|
File.read("/sys/devices/system/cpu/present").split('-').last.to_i+1
|
@@ -13,26 +13,23 @@ module Celluloid
|
|
13
13
|
def <<(message)
|
14
14
|
@mutex.lock
|
15
15
|
begin
|
16
|
-
if mailbox_full
|
17
|
-
|
16
|
+
if mailbox_full || @dead
|
17
|
+
dead_letter(message)
|
18
18
|
return
|
19
19
|
end
|
20
20
|
if message.is_a?(SystemEvent)
|
21
|
-
# Silently swallow system events sent to dead actors
|
22
|
-
return if @dead
|
23
|
-
|
24
21
|
# SystemEvents are high priority messages so they get added to the
|
25
22
|
# head of our message queue instead of the end
|
26
23
|
@messages.unshift message
|
27
24
|
else
|
28
|
-
raise MailboxError, "dead recipient" if @dead
|
29
25
|
@messages << message
|
30
26
|
end
|
31
27
|
|
32
28
|
current_actor = Thread.current[:celluloid_actor]
|
33
29
|
@reactor.wakeup unless current_actor && current_actor.mailbox == self
|
34
30
|
rescue IOError
|
35
|
-
|
31
|
+
Logger.crash "reactor crashed", $!
|
32
|
+
dead_letter(message)
|
36
33
|
ensure
|
37
34
|
@mutex.unlock rescue nil
|
38
35
|
end
|
@@ -59,7 +56,6 @@ module Celluloid
|
|
59
56
|
|
60
57
|
message
|
61
58
|
rescue IOError
|
62
|
-
shutdown # force shutdown of the mailbox
|
63
59
|
raise MailboxShutdown, "mailbox shutdown called during receive"
|
64
60
|
end
|
65
61
|
|
@@ -75,8 +71,9 @@ module Celluloid
|
|
75
71
|
|
76
72
|
# Cleanup any IO objects this Mailbox may be using
|
77
73
|
def shutdown
|
78
|
-
|
79
|
-
|
74
|
+
super do
|
75
|
+
@reactor.shutdown
|
76
|
+
end
|
80
77
|
end
|
81
78
|
end
|
82
79
|
end
|
data/lib/celluloid/fsm.rb
CHANGED
@@ -13,7 +13,7 @@ module Celluloid
|
|
13
13
|
# #
|
14
14
|
# machine = MyMachine.new(current_actor)
|
15
15
|
module FSM
|
16
|
-
class UnattachedError <
|
16
|
+
class UnattachedError < Celluloid::Error; end # Not attached to an actor
|
17
17
|
|
18
18
|
DEFAULT_STATE = :default # Default state name unless one is explicitly set
|
19
19
|
|
data/lib/celluloid/future.rb
CHANGED
@@ -3,34 +3,76 @@ require 'thread'
|
|
3
3
|
module Celluloid
|
4
4
|
# Maintain a thread pool FOR SPEED!!
|
5
5
|
class InternalPool
|
6
|
-
attr_accessor :
|
6
|
+
attr_accessor :max_idle
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@
|
9
|
+
@group = ThreadGroup.new
|
10
10
|
@mutex = Mutex.new
|
11
|
-
@
|
11
|
+
@threads = []
|
12
12
|
|
13
|
-
reset
|
14
|
-
end
|
15
|
-
|
16
|
-
def reset
|
17
13
|
# TODO: should really adjust this based on usage
|
18
14
|
@max_idle = 16
|
15
|
+
@running = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def busy_size
|
19
|
+
@threads.select(&:busy).size
|
20
|
+
end
|
21
|
+
|
22
|
+
def idle_size
|
23
|
+
@threads.reject(&:busy).size
|
24
|
+
end
|
25
|
+
|
26
|
+
def assert_running
|
27
|
+
unless running?
|
28
|
+
raise Error, "Thread pool is not running"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def assert_inactive
|
33
|
+
if active?
|
34
|
+
message = "Thread pool is still active"
|
35
|
+
if defined?(JRUBY_VERSION)
|
36
|
+
Celluloid.logger.warn message
|
37
|
+
else
|
38
|
+
raise Error, message
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def running?
|
44
|
+
@running
|
45
|
+
end
|
46
|
+
|
47
|
+
def active?
|
48
|
+
to_a.any?
|
49
|
+
end
|
50
|
+
|
51
|
+
def each
|
52
|
+
@threads.each do |thread|
|
53
|
+
yield thread
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def to_a
|
58
|
+
@threads
|
19
59
|
end
|
20
60
|
|
21
61
|
# Get a thread from the pool, running the given block
|
22
62
|
def get(&block)
|
23
63
|
@mutex.synchronize do
|
64
|
+
assert_running
|
65
|
+
|
24
66
|
begin
|
25
|
-
|
67
|
+
idle = @threads.reject(&:busy)
|
68
|
+
if idle.empty?
|
26
69
|
thread = create
|
27
70
|
else
|
28
|
-
thread =
|
29
|
-
@idle_size -= 1
|
71
|
+
thread = idle.first
|
30
72
|
end
|
31
73
|
end until thread.status # handle crashed threads
|
32
74
|
|
33
|
-
|
75
|
+
thread.busy = true
|
34
76
|
thread[:celluloid_queue] << block
|
35
77
|
thread
|
36
78
|
end
|
@@ -39,13 +81,12 @@ module Celluloid
|
|
39
81
|
# Return a thread to the pool
|
40
82
|
def put(thread)
|
41
83
|
@mutex.synchronize do
|
42
|
-
|
84
|
+
thread.busy = false
|
85
|
+
if idle_size >= @max_idle
|
43
86
|
thread[:celluloid_queue] << nil
|
87
|
+
@threads.delete(thread)
|
44
88
|
else
|
45
89
|
clean_thread_locals(thread)
|
46
|
-
@pool << thread
|
47
|
-
@idle_size += 1
|
48
|
-
@busy_size -= 1
|
49
90
|
end
|
50
91
|
end
|
51
92
|
end
|
@@ -66,6 +107,8 @@ module Celluloid
|
|
66
107
|
end
|
67
108
|
|
68
109
|
thread[:celluloid_queue] = queue
|
110
|
+
@threads << thread
|
111
|
+
@group.add(thread)
|
69
112
|
thread
|
70
113
|
end
|
71
114
|
|
@@ -81,13 +124,27 @@ module Celluloid
|
|
81
124
|
|
82
125
|
def shutdown
|
83
126
|
@mutex.synchronize do
|
84
|
-
|
85
|
-
@
|
127
|
+
finalize
|
128
|
+
@threads.each do |thread|
|
86
129
|
thread[:celluloid_queue] << nil
|
87
130
|
end
|
88
131
|
end
|
89
132
|
end
|
90
|
-
end
|
91
133
|
|
92
|
-
|
93
|
-
|
134
|
+
def kill
|
135
|
+
@mutex.synchronize do
|
136
|
+
finalize
|
137
|
+
@running = false
|
138
|
+
|
139
|
+
@threads.shift.kill until @threads.empty?
|
140
|
+
@group.list.each(&:kill)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def finalize
|
147
|
+
@max_idle = 0
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/celluloid/legacy.rb
CHANGED
@@ -1,41 +1,3 @@
|
|
1
|
-
module Celluloid
|
2
|
-
class ActorProxy
|
3
|
-
# method_missing black magic to call bang predicate methods asynchronously
|
4
|
-
def method_missing(meth, *args, &block)
|
5
|
-
# bang methods are async calls
|
6
|
-
if meth.match(/!$/)
|
7
|
-
Logger.deprecate("'bang method'-style async syntax is deprecated and will be removed in Celluloid 1.0." +
|
8
|
-
"Call async methods with 'actor.async.method'.")
|
9
|
-
|
10
|
-
unbanged_meth = meth.to_s
|
11
|
-
unbanged_meth.slice!(-1, 1)
|
12
|
-
async unbanged_meth, *args, &block
|
13
|
-
else
|
14
|
-
super
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module InstanceMethods
|
20
|
-
# Process async calls via method_missing
|
21
|
-
def method_missing(meth, *args, &block)
|
22
|
-
# bang methods are async calls
|
23
|
-
if meth.to_s.match(/!$/)
|
24
|
-
Logger.deprecate("'bang method'-style async syntax is deprecated and will be removed in Celluloid 1.0." +
|
25
|
-
"Call async methods with 'actor.async.method'.")
|
26
|
-
|
27
|
-
unbanged_meth = meth.to_s.sub(/!$/, '')
|
28
|
-
args.unshift unbanged_meth
|
29
|
-
|
30
|
-
async :__send__, *args, &block
|
31
|
-
return
|
32
|
-
end
|
33
|
-
|
34
|
-
super
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
1
|
class Thread
|
40
2
|
def self.mailbox
|
41
3
|
Celluloid.mailbox
|
data/lib/celluloid/mailbox.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'thread'
|
2
2
|
|
3
3
|
module Celluloid
|
4
|
-
class
|
5
|
-
class MailboxShutdown <
|
4
|
+
class MailboxDead < Celluloid::Error; end # you can't receive from the dead
|
5
|
+
class MailboxShutdown < Celluloid::Error; end # raised if the mailbox can no longer be used
|
6
6
|
|
7
7
|
# Actors communicate with asynchronous messages. Messages are buffered in
|
8
8
|
# Mailboxes until Actors can act upon them.
|
@@ -26,19 +26,15 @@ module Celluloid
|
|
26
26
|
def <<(message)
|
27
27
|
@mutex.lock
|
28
28
|
begin
|
29
|
-
if mailbox_full
|
30
|
-
|
29
|
+
if mailbox_full || @dead
|
30
|
+
dead_letter(message)
|
31
31
|
return
|
32
32
|
end
|
33
33
|
if message.is_a?(SystemEvent)
|
34
|
-
# Silently swallow system events sent to dead actors
|
35
|
-
return if @dead
|
36
|
-
|
37
34
|
# SystemEvents are high priority messages so they get added to the
|
38
35
|
# head of our message queue instead of the end
|
39
36
|
@messages.unshift message
|
40
37
|
else
|
41
|
-
raise MailboxError, "dead recipient" if @dead
|
42
38
|
@messages << message
|
43
39
|
end
|
44
40
|
|
@@ -55,7 +51,7 @@ module Celluloid
|
|
55
51
|
|
56
52
|
@mutex.lock
|
57
53
|
begin
|
58
|
-
raise
|
54
|
+
raise MailboxDead, "attempted to receive from a dead mailbox" if @dead
|
59
55
|
|
60
56
|
begin
|
61
57
|
message = next_message(&block)
|
@@ -99,8 +95,11 @@ module Celluloid
|
|
99
95
|
|
100
96
|
# Shut down this mailbox and clean up its contents
|
101
97
|
def shutdown
|
98
|
+
raise MailboxDead, "mailbox already shutdown" if @dead
|
99
|
+
|
102
100
|
@mutex.lock
|
103
101
|
begin
|
102
|
+
yield if block_given?
|
104
103
|
messages = @messages
|
105
104
|
@messages = []
|
106
105
|
@dead = true
|
@@ -108,7 +107,10 @@ module Celluloid
|
|
108
107
|
@mutex.unlock rescue nil
|
109
108
|
end
|
110
109
|
|
111
|
-
messages.each
|
110
|
+
messages.each do |msg|
|
111
|
+
dead_letter msg
|
112
|
+
msg.cleanup if msg.respond_to? :cleanup
|
113
|
+
end
|
112
114
|
true
|
113
115
|
end
|
114
116
|
|
@@ -138,6 +140,11 @@ module Celluloid
|
|
138
140
|
end
|
139
141
|
|
140
142
|
private
|
143
|
+
|
144
|
+
def dead_letter(message)
|
145
|
+
Logger.debug "Discarded message (mailbox is dead): #{message}"
|
146
|
+
end
|
147
|
+
|
141
148
|
def mailbox_full
|
142
149
|
@max_size && @messages.size >= @max_size
|
143
150
|
end
|