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
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
|