celluloid 0.12.1.pre → 0.12.1.pre2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/celluloid.rb +22 -3
- data/lib/celluloid/actor.rb +15 -12
- data/lib/celluloid/boot.rb +4 -2
- data/lib/celluloid/fsm.rb +49 -16
- data/lib/celluloid/logging.rb +5 -0
- data/lib/celluloid/logging/incident.rb +21 -0
- data/lib/celluloid/logging/incident_logger.rb +129 -0
- data/lib/celluloid/logging/incident_reporter.rb +48 -0
- data/lib/celluloid/logging/log_event.rb +20 -0
- data/lib/celluloid/logging/ring_buffer.rb +65 -0
- data/lib/celluloid/stack_dumper.rb +45 -0
- data/lib/celluloid/task.rb +4 -1
- data/lib/celluloid/thread_handle.rb +5 -0
- data/lib/celluloid/version.rb +1 -1
- data/spec/support/actor_examples.rb +8 -0
- metadata +52 -35
- data/lib/celluloid/cpu_counter.rb +0 -18
data/lib/celluloid.rb
CHANGED
@@ -2,6 +2,7 @@ require 'logger'
|
|
2
2
|
require 'thread'
|
3
3
|
require 'timeout'
|
4
4
|
require 'set'
|
5
|
+
require 'facter'
|
5
6
|
|
6
7
|
module Celluloid
|
7
8
|
extend self # expose all instance methods as singleton methods
|
@@ -33,11 +34,18 @@ module Celluloid
|
|
33
34
|
|
34
35
|
# Obtain the number of CPUs in the system
|
35
36
|
def cores
|
36
|
-
|
37
|
+
core_count = Facter.fact(:processorcount).value
|
38
|
+
Integer(core_count)
|
37
39
|
end
|
38
40
|
alias_method :cpus, :cores
|
39
41
|
alias_method :ncpus, :cores
|
40
42
|
|
43
|
+
# Perform a stack dump of all actors to the given output object
|
44
|
+
def stack_dump(output = STDERR)
|
45
|
+
Celluloid::StackDumper.dump(output)
|
46
|
+
end
|
47
|
+
alias_method :dump, :stack_dump
|
48
|
+
|
41
49
|
# Define an exception handler for actor crashes
|
42
50
|
def exception_handler(&block)
|
43
51
|
Logger.exception_handler(&block)
|
@@ -73,7 +81,17 @@ module Celluloid
|
|
73
81
|
end
|
74
82
|
|
75
83
|
# Terminate all actors at exit
|
76
|
-
at_exit
|
84
|
+
at_exit do
|
85
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION >= "1.9"
|
86
|
+
# workaround for MRI bug losing exit status in at_exit block
|
87
|
+
# http://bugs.ruby-lang.org/issues/5218
|
88
|
+
exit_status = $!.status if $!.is_a?(SystemExit)
|
89
|
+
shutdown
|
90
|
+
exit exit_status if exit_status
|
91
|
+
else
|
92
|
+
shutdown
|
93
|
+
end
|
94
|
+
end
|
77
95
|
|
78
96
|
# Class methods added to classes which include Celluloid
|
79
97
|
module ClassMethods
|
@@ -402,7 +420,6 @@ require 'celluloid/version'
|
|
402
420
|
|
403
421
|
require 'celluloid/calls'
|
404
422
|
require 'celluloid/core_ext'
|
405
|
-
require 'celluloid/cpu_counter'
|
406
423
|
require 'celluloid/fiber'
|
407
424
|
require 'celluloid/fsm'
|
408
425
|
require 'celluloid/internal_pool'
|
@@ -414,6 +431,7 @@ require 'celluloid/receivers'
|
|
414
431
|
require 'celluloid/registry'
|
415
432
|
require 'celluloid/responses'
|
416
433
|
require 'celluloid/signals'
|
434
|
+
require 'celluloid/stack_dumper'
|
417
435
|
require 'celluloid/system_events'
|
418
436
|
require 'celluloid/task'
|
419
437
|
require 'celluloid/thread_handle'
|
@@ -430,5 +448,6 @@ require 'celluloid/pool_manager'
|
|
430
448
|
require 'celluloid/supervision_group'
|
431
449
|
require 'celluloid/supervisor'
|
432
450
|
require 'celluloid/notifications'
|
451
|
+
require 'celluloid/logging'
|
433
452
|
|
434
453
|
require 'celluloid/boot'
|
data/lib/celluloid/actor.rb
CHANGED
@@ -65,11 +65,9 @@ module Celluloid
|
|
65
65
|
raise DeadActorError, "attempted to call a dead actor"
|
66
66
|
end
|
67
67
|
|
68
|
-
if
|
69
|
-
# The current task will be automatically resumed when we get a response
|
68
|
+
if Thread.current[:task]
|
70
69
|
Task.suspend(:callwait).value
|
71
70
|
else
|
72
|
-
# Otherwise we're inside a normal thread or exclusive, so block
|
73
71
|
response = loop do
|
74
72
|
message = Thread.mailbox.receive do |msg|
|
75
73
|
msg.respond_to?(:call) and msg.call == call
|
@@ -221,10 +219,16 @@ module Celluloid
|
|
221
219
|
|
222
220
|
# Execute a code block in exclusive mode.
|
223
221
|
def exclusive
|
224
|
-
@exclusive
|
225
|
-
|
226
|
-
|
227
|
-
|
222
|
+
if @exclusive
|
223
|
+
yield
|
224
|
+
else
|
225
|
+
begin
|
226
|
+
@exclusive = true
|
227
|
+
yield
|
228
|
+
ensure
|
229
|
+
@exclusive = false
|
230
|
+
end
|
231
|
+
end
|
228
232
|
end
|
229
233
|
|
230
234
|
# Perform a linking request with another actor
|
@@ -302,12 +306,11 @@ module Celluloid
|
|
302
306
|
|
303
307
|
# Sleep for the given amount of time
|
304
308
|
def sleep(interval)
|
305
|
-
if
|
306
|
-
Kernel.sleep(interval)
|
307
|
-
else
|
308
|
-
task = Task.current
|
309
|
+
if task = Thread.current[:task]
|
309
310
|
@timers.after(interval) { task.resume }
|
310
311
|
Task.suspend :sleeping
|
312
|
+
else
|
313
|
+
Kernel.sleep(interval)
|
311
314
|
end
|
312
315
|
end
|
313
316
|
|
@@ -389,7 +392,7 @@ module Celluloid
|
|
389
392
|
if @exclusives && (@exclusives == :all || @exclusives.include?(method_name))
|
390
393
|
exclusive { block.call }
|
391
394
|
else
|
392
|
-
@task_class.new(
|
395
|
+
@task_class.new(task_type, &block).resume
|
393
396
|
end
|
394
397
|
end
|
395
398
|
end
|
data/lib/celluloid/boot.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
# Things to run after Celluloid is fully loaded
|
2
2
|
|
3
3
|
# Configure default systemwide settings
|
4
|
-
Celluloid.logger = Logger.new STDERR
|
5
4
|
Celluloid.task_class = Celluloid::TaskFiber
|
6
5
|
|
7
6
|
# Launch the notifications fanout actor
|
8
7
|
# FIXME: We should set up the supervision hierarchy here
|
9
|
-
Celluloid::Notifications::Fanout.supervise_as :notifications_fanout
|
8
|
+
Celluloid::Notifications::Fanout.supervise_as :notifications_fanout
|
9
|
+
|
10
|
+
Celluloid.logger = Celluloid::IncidentLogger.new
|
11
|
+
Celluloid::IncidentReporter.supervise_as :default_incident_reporter, STDERR
|
data/lib/celluloid/fsm.rb
CHANGED
@@ -84,30 +84,71 @@ module Celluloid
|
|
84
84
|
#
|
85
85
|
# Note: making additional state transitions will cancel delayed transitions
|
86
86
|
def transition(state_name, options = {})
|
87
|
+
new_state = validate_and_sanitize_new_state(state_name)
|
88
|
+
return unless new_state
|
89
|
+
|
90
|
+
if handle_delayed_transitions(new_state, options[:delay])
|
91
|
+
return @delayed_transition
|
92
|
+
end
|
93
|
+
|
94
|
+
transition_with_callbacks!(new_state)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Immediate state transition with no sanity checks, or callbacks. "Dangerous!"
|
98
|
+
def transition!(state_name)
|
99
|
+
@state = state_name
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def validate_and_sanitize_new_state(state_name)
|
87
105
|
state_name = state_name.to_sym
|
88
|
-
current_state = self.class.states[@state]
|
89
106
|
|
90
|
-
return if
|
107
|
+
return if current_state_name == state_name
|
91
108
|
|
92
109
|
if current_state and not current_state.valid_transition? state_name
|
93
110
|
valid = current_state.transitions.map(&:to_s).join(", ")
|
94
111
|
raise ArgumentError, "#{self.class} can't change state from '#{@state}' to '#{state_name}', only to: #{valid}"
|
95
112
|
end
|
96
113
|
|
97
|
-
new_state =
|
114
|
+
new_state = states[state_name]
|
98
115
|
|
99
116
|
unless new_state
|
100
|
-
return if state_name ==
|
117
|
+
return if state_name == default_state
|
101
118
|
raise ArgumentError, "invalid state for #{self.class}: #{state_name}"
|
102
119
|
end
|
103
120
|
|
104
|
-
|
121
|
+
new_state
|
122
|
+
end
|
123
|
+
|
124
|
+
def transition_with_callbacks!(state_name)
|
125
|
+
transition! state_name.name
|
126
|
+
state_name.call(self)
|
127
|
+
end
|
128
|
+
|
129
|
+
def states
|
130
|
+
self.class.states
|
131
|
+
end
|
132
|
+
|
133
|
+
def default_state
|
134
|
+
self.class.default_state
|
135
|
+
end
|
136
|
+
|
137
|
+
def current_state
|
138
|
+
states[@state]
|
139
|
+
end
|
140
|
+
|
141
|
+
def current_state_name
|
142
|
+
current_state && current_state.name || ''
|
143
|
+
end
|
144
|
+
|
145
|
+
def handle_delayed_transitions(new_state, delay)
|
146
|
+
if delay
|
105
147
|
raise UnattachedError, "can't delay unless attached" unless @actor
|
106
148
|
@delayed_transition.cancel if @delayed_transition
|
107
149
|
|
108
|
-
@delayed_transition = @actor.after(
|
109
|
-
|
110
|
-
new_state.call(self)
|
150
|
+
@delayed_transition = @actor.after(delay) do
|
151
|
+
transition_with_callbacks!(new_state)
|
111
152
|
end
|
112
153
|
|
113
154
|
return @delayed_transition
|
@@ -117,14 +158,6 @@ module Celluloid
|
|
117
158
|
@delayed_transition.cancel
|
118
159
|
@delayed_transition = nil
|
119
160
|
end
|
120
|
-
|
121
|
-
transition! new_state.name
|
122
|
-
new_state.call(self)
|
123
|
-
end
|
124
|
-
|
125
|
-
# Immediate state transition with no sanity checks. "Dangerous!"
|
126
|
-
def transition!(state_name)
|
127
|
-
@state = state_name
|
128
161
|
end
|
129
162
|
|
130
163
|
# FSM states as declared by Celluloid::FSM.state
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# Wraps all events and context for a single incident.
|
3
|
+
class Incident
|
4
|
+
attr_accessor :pid
|
5
|
+
attr_accessor :events, :triggering_event
|
6
|
+
|
7
|
+
def initialize(events, triggering_event=nil)
|
8
|
+
@events = events
|
9
|
+
@triggering_event = triggering_event
|
10
|
+
@pid = $$
|
11
|
+
end
|
12
|
+
|
13
|
+
# Merge two incidents together. This may be useful if two incidents occur at the same time.
|
14
|
+
def merge(*other_incidents)
|
15
|
+
merged_events = other_incidents.flatten.inject(events) do |events, incident|
|
16
|
+
events += incident.events
|
17
|
+
end
|
18
|
+
Incident.new(merged_events.sort, triggering_event)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Celluloid
|
3
|
+
# A logger that holds all messages in circular buffers, then flushes the buffers
|
4
|
+
# when an event occurs at a configurable severity threshold.
|
5
|
+
#
|
6
|
+
# Unlike ruby's Logger, this class only supports a single progname.
|
7
|
+
class IncidentLogger
|
8
|
+
module Severity
|
9
|
+
include ::Logger::Severity
|
10
|
+
|
11
|
+
TRACE = -1
|
12
|
+
|
13
|
+
def severity_to_string(severity)
|
14
|
+
case severity
|
15
|
+
when TRACE then 'TRACE'
|
16
|
+
when DEBUG then 'DEBUG'
|
17
|
+
when INFO then 'INFO'
|
18
|
+
when WARN then 'WARN'
|
19
|
+
when ERROR then 'ERROR'
|
20
|
+
when FATAL then 'FATAL'
|
21
|
+
when UNKNOWN then 'UNKNOWN'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
include Severity
|
27
|
+
|
28
|
+
# The progname (facility) for this instance.
|
29
|
+
attr_accessor :progname
|
30
|
+
|
31
|
+
# The logging level. Messages below this severity will not be logged at all.
|
32
|
+
attr_accessor :level
|
33
|
+
|
34
|
+
# The incident threshold. Messages at or above this severity will generate an
|
35
|
+
# incident and be published to incident reporters.
|
36
|
+
attr_accessor :threshold
|
37
|
+
|
38
|
+
# The buffer size limit. Each log level will retain this number of messages
|
39
|
+
# at maximum.
|
40
|
+
attr_accessor :sizelimit
|
41
|
+
|
42
|
+
attr_accessor :buffers
|
43
|
+
|
44
|
+
# Create a new IncidentLogger.
|
45
|
+
def initialize(progname=nil, options={})
|
46
|
+
@progname = progname || "default"
|
47
|
+
@level = options[:level] || DEBUG
|
48
|
+
@threshold = options[:threshold] || ERROR
|
49
|
+
@sizelimit = options[:sizelimit] || 100
|
50
|
+
|
51
|
+
@buffer_mutex = Mutex.new
|
52
|
+
@buffers = Hash.new do |progname_hash, progname|
|
53
|
+
@buffer_mutex.synchronize do
|
54
|
+
progname_hash[progname] = Hash.new do |severity_hash, severity|
|
55
|
+
severity_hash[severity] = RingBuffer.new(@sizelimit)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# When the IncidentLogger itself encounters an error, it falls back to logging to stderr
|
61
|
+
@fallback_logger = ::Logger.new(STDERR)
|
62
|
+
@fallback_logger.progname = "FALLBACK"
|
63
|
+
end
|
64
|
+
|
65
|
+
# add an event.
|
66
|
+
def add(severity, message=nil, progname=nil, &block)
|
67
|
+
progname ||= @progname
|
68
|
+
severity ||= UNKNOWN
|
69
|
+
|
70
|
+
if severity < @level
|
71
|
+
return event.id
|
72
|
+
end
|
73
|
+
|
74
|
+
if message.nil? && !block_given?
|
75
|
+
message = progname
|
76
|
+
progname = @progname
|
77
|
+
end
|
78
|
+
|
79
|
+
event = LogEvent.new(severity, message, progname, &block)
|
80
|
+
|
81
|
+
@buffers[progname][severity] << event
|
82
|
+
|
83
|
+
if severity >= @threshold
|
84
|
+
begin
|
85
|
+
Celluloid::Notifications.notifier.async.publish(incident_topic, create_incident(event))
|
86
|
+
rescue => ex
|
87
|
+
@fallback_logger.error(ex)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
event.id
|
91
|
+
end
|
92
|
+
alias :log :add
|
93
|
+
|
94
|
+
# See docs for Logger#info
|
95
|
+
def trace (progname=nil, &block); add(TRACE, nil, progname, &block); end
|
96
|
+
def debug (progname=nil, &block); add(DEBUG, nil, progname, &block); end
|
97
|
+
def info (progname=nil, &block); add(INFO, nil, progname, &block); end
|
98
|
+
def warn (progname=nil, &block); add(WARN, nil, progname, &block); end
|
99
|
+
def error (progname=nil, &block); add(ERROR, nil, progname, &block); end
|
100
|
+
def fatal (progname=nil, &block); add(FATAL, nil, progname, &block); end
|
101
|
+
def unknown (progname=nil, &block); add(UNKNOWN, nil, progname, &block); end
|
102
|
+
|
103
|
+
def flush
|
104
|
+
messages = []
|
105
|
+
@buffer_mutex.synchronize do
|
106
|
+
@buffers.each do |progname, severities|
|
107
|
+
severities.each do |severity, buffer|
|
108
|
+
messages += buffer.flush
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
messages.sort
|
113
|
+
end
|
114
|
+
|
115
|
+
def clear
|
116
|
+
@buffer_mutex.synchronize do
|
117
|
+
@buffers.each { |buffer| buffer.clear }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def create_incident(event=nil)
|
122
|
+
Incident.new(flush, event)
|
123
|
+
end
|
124
|
+
|
125
|
+
def incident_topic
|
126
|
+
"log.incident.#{@progname}"
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'logger'
|
2
|
+
module Celluloid
|
3
|
+
# Subscribes to log incident topics to report on them.
|
4
|
+
class IncidentReporter
|
5
|
+
include Celluloid
|
6
|
+
include Celluloid::Notifications
|
7
|
+
|
8
|
+
# get the time from the event
|
9
|
+
class Formatter < ::Logger::Formatter
|
10
|
+
def call(severity, time, progname, msg)
|
11
|
+
super(severity, msg.time, progname, msg.message)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(*args)
|
16
|
+
subscribe(/log\.incident/, :report)
|
17
|
+
@logger = ::Logger.new(*args)
|
18
|
+
@logger.formatter = Formatter.new
|
19
|
+
@silenced = false
|
20
|
+
end
|
21
|
+
|
22
|
+
def report(topic, incident)
|
23
|
+
return if @silenced
|
24
|
+
|
25
|
+
header = "INCIDENT"
|
26
|
+
header << " AT #{incident.triggering_event.time}" if incident.triggering_event
|
27
|
+
@logger << header
|
28
|
+
@logger << "\n"
|
29
|
+
@logger << "====================\n"
|
30
|
+
incident.events.each do |event|
|
31
|
+
@logger.add(event.severity, event, event.progname)
|
32
|
+
end
|
33
|
+
@logger << "====================\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
def silence
|
37
|
+
@silenced = true
|
38
|
+
end
|
39
|
+
|
40
|
+
def unsilence
|
41
|
+
@silenced = false
|
42
|
+
end
|
43
|
+
|
44
|
+
def silenced?
|
45
|
+
@silenced
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Celluloid
|
2
|
+
# Wraps a single log event.
|
3
|
+
class LogEvent
|
4
|
+
attr_accessor :id, :severity, :message, :progname, :time
|
5
|
+
|
6
|
+
def initialize(severity, message, progname, time=Time.now, &block)
|
7
|
+
# This id should be ordered. For now relies on Celluloid::UUID to be ordered.
|
8
|
+
# May want to use a generation/counter strategy for independence of uuid.
|
9
|
+
@id = Celluloid::UUID.generate
|
10
|
+
@severity = severity
|
11
|
+
@message = block_given? ? yield : message
|
12
|
+
@progname = progname
|
13
|
+
@time = time
|
14
|
+
end
|
15
|
+
|
16
|
+
def <=>(other)
|
17
|
+
@id <=> other.id
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Celluloid
|
2
|
+
class RingBuffer
|
3
|
+
def initialize(size)
|
4
|
+
@size = size
|
5
|
+
@start = 0
|
6
|
+
@count = 0
|
7
|
+
@buffer = Array.new(size)
|
8
|
+
@mutex = Mutex.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def full?
|
12
|
+
@count == @size
|
13
|
+
end
|
14
|
+
|
15
|
+
def empty?
|
16
|
+
@count == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def push(value)
|
20
|
+
@mutex.synchronize do
|
21
|
+
stop = (@start + @count) % @size
|
22
|
+
@buffer[stop] = value
|
23
|
+
if full?
|
24
|
+
@start = (@start + 1) % @size
|
25
|
+
else
|
26
|
+
@count += 1
|
27
|
+
end
|
28
|
+
value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
alias :<< :push
|
32
|
+
|
33
|
+
def shift
|
34
|
+
@mutex.synchronize do
|
35
|
+
remove_element
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def flush
|
40
|
+
values = []
|
41
|
+
@mutex.synchronize do
|
42
|
+
while !empty?
|
43
|
+
values << remove_element
|
44
|
+
end
|
45
|
+
end
|
46
|
+
values
|
47
|
+
end
|
48
|
+
|
49
|
+
def clear
|
50
|
+
@buffer = Array.new(@size)
|
51
|
+
@start = 0
|
52
|
+
@count = 0
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def remove_element
|
58
|
+
return nil if empty?
|
59
|
+
value, @buffer[@start] = @buffer[@start], nil
|
60
|
+
@start = (@start + 1) % @size
|
61
|
+
@count -= 1
|
62
|
+
value
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module StackDumper
|
3
|
+
def self.dump(output = STDERR)
|
4
|
+
actors = {}
|
5
|
+
threads = []
|
6
|
+
|
7
|
+
Thread.list.each do |thread|
|
8
|
+
if actor = thread[:actor]
|
9
|
+
actors[actor.subject.object_id] = actor
|
10
|
+
else
|
11
|
+
threads << thread
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
actors.each do |_, actor|
|
16
|
+
output << "Celluloid::Actor 0x#{actor.subject.object_id.to_s(16)}: #{actor.subject.class}"
|
17
|
+
output << " [#{actor.name}]" if actor.name
|
18
|
+
output << "\n"
|
19
|
+
|
20
|
+
tasks = actor.tasks
|
21
|
+
if tasks.empty?
|
22
|
+
output << "State: Idle (waiting for messages)\n"
|
23
|
+
else
|
24
|
+
output << "State: Running (executing tasks)\n"
|
25
|
+
output << "Tasks:\n"
|
26
|
+
|
27
|
+
tasks.each_with_index do |task, i|
|
28
|
+
output << " #{i+1}) #{task.class}: #{task.status}\n"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
display_backtrace actor.thread, output
|
33
|
+
end
|
34
|
+
|
35
|
+
threads.each do |thread|
|
36
|
+
output << "Thread 0x#{object_id.to_s(16)}:\n"
|
37
|
+
display_backtrace thread, output
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.display_backtrace(thread, output)
|
42
|
+
output << "\t" << thread.backtrace.join("\n\t") << "\n\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/celluloid/task.rb
CHANGED
@@ -2,6 +2,9 @@ require 'celluloid/tasks/task_fiber'
|
|
2
2
|
require 'celluloid/tasks/task_thread'
|
3
3
|
|
4
4
|
module Celluloid
|
5
|
+
# Asked to do task-related things outside a task
|
6
|
+
class NotTaskError < StandardError; end
|
7
|
+
|
5
8
|
# Trying to resume a dead task
|
6
9
|
class DeadTaskError < StandardError; end
|
7
10
|
|
@@ -11,7 +14,7 @@ module Celluloid
|
|
11
14
|
|
12
15
|
# Obtain the current task
|
13
16
|
def self.current
|
14
|
-
Thread.current[:task] or raise "not within a task context"
|
17
|
+
Thread.current[:task] or raise NotTaskError, "not within a task context"
|
15
18
|
end
|
16
19
|
|
17
20
|
# Suspend the running task, deferring to the scheduler
|
@@ -1,8 +1,13 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
module Celluloid
|
2
4
|
# An abstraction around threads from the InternalPool which ensures we don't
|
3
5
|
# accidentally do things to threads which have been returned to the pool,
|
4
6
|
# such as, say, killing them
|
5
7
|
class ThreadHandle
|
8
|
+
extend Forwardable
|
9
|
+
def_delegators :@thread, :backtrace
|
10
|
+
|
6
11
|
def initialize
|
7
12
|
@mutex = Mutex.new
|
8
13
|
@join = ConditionVariable.new
|
data/lib/celluloid/version.rb
CHANGED
@@ -422,12 +422,20 @@ shared_context "a Celluloid Actor" do |included_module|
|
|
422
422
|
exclusive?
|
423
423
|
end
|
424
424
|
exclusive :exclusive_example
|
425
|
+
|
426
|
+
def nested_exclusive_example
|
427
|
+
exclusive { exclusive { nil }; Celluloid.exclusive? }
|
428
|
+
end
|
425
429
|
end.new
|
426
430
|
end
|
427
431
|
|
428
432
|
it "supports exclusive methods" do
|
429
433
|
subject.exclusive_example.should be_true
|
430
434
|
end
|
435
|
+
|
436
|
+
it "remains in exclusive mode inside nested blocks" do
|
437
|
+
subject.nested_exclusive_example.should be_true
|
438
|
+
end
|
431
439
|
end
|
432
440
|
|
433
441
|
context "exclusive classes" do
|
metadata
CHANGED
@@ -1,62 +1,72 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: celluloid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.12.1.pre
|
5
4
|
prerelease: 7
|
5
|
+
version: 0.12.1.pre2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Tony Arcieri
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-10-10 00:00:00.000000000Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: timers
|
16
|
-
|
17
|
-
none: false
|
16
|
+
version_requirements: &2056 !ruby/object:Gem::Requirement
|
18
17
|
requirements:
|
19
18
|
- - ! '>='
|
20
19
|
- !ruby/object:Gem::Version
|
21
20
|
version: 1.0.0
|
21
|
+
none: false
|
22
|
+
requirement: *2056
|
23
|
+
prerelease: false
|
22
24
|
type: :runtime
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: facter
|
27
|
+
version_requirements: &2074 !ruby/object:Gem::Requirement
|
28
|
+
requirements:
|
29
|
+
- - ! '>='
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: 1.6.12
|
32
|
+
none: false
|
33
|
+
requirement: *2074
|
23
34
|
prerelease: false
|
24
|
-
|
35
|
+
type: :runtime
|
25
36
|
- !ruby/object:Gem::Dependency
|
26
37
|
name: rake
|
27
|
-
|
28
|
-
none: false
|
38
|
+
version_requirements: &2090 !ruby/object:Gem::Requirement
|
29
39
|
requirements:
|
30
40
|
- - ! '>='
|
31
41
|
- !ruby/object:Gem::Version
|
32
42
|
version: '0'
|
33
|
-
|
43
|
+
none: false
|
44
|
+
requirement: *2090
|
34
45
|
prerelease: false
|
35
|
-
|
46
|
+
type: :development
|
36
47
|
- !ruby/object:Gem::Dependency
|
37
48
|
name: rspec
|
38
|
-
|
39
|
-
none: false
|
49
|
+
version_requirements: &2108 !ruby/object:Gem::Requirement
|
40
50
|
requirements:
|
41
51
|
- - ! '>='
|
42
52
|
- !ruby/object:Gem::Version
|
43
53
|
version: '0'
|
44
|
-
|
54
|
+
none: false
|
55
|
+
requirement: *2108
|
45
56
|
prerelease: false
|
46
|
-
|
57
|
+
type: :development
|
47
58
|
- !ruby/object:Gem::Dependency
|
48
59
|
name: benchmark_suite
|
49
|
-
|
50
|
-
none: false
|
60
|
+
version_requirements: &2124 !ruby/object:Gem::Requirement
|
51
61
|
requirements:
|
52
62
|
- - ! '>='
|
53
63
|
- !ruby/object:Gem::Version
|
54
64
|
version: '0'
|
55
|
-
|
65
|
+
none: false
|
66
|
+
requirement: *2124
|
56
67
|
prerelease: false
|
57
|
-
|
58
|
-
description: Celluloid enables people to build concurrent programs out of concurrent
|
59
|
-
objects just as easily as they build sequential programs out of sequential objects
|
68
|
+
type: :development
|
69
|
+
description: Celluloid enables people to build concurrent programs out of concurrent objects just as easily as they build sequential programs out of sequential objects
|
60
70
|
email:
|
61
71
|
- tony.arcieri@gmail.com
|
62
72
|
executables: []
|
@@ -64,40 +74,46 @@ extensions: []
|
|
64
74
|
extra_rdoc_files: []
|
65
75
|
files:
|
66
76
|
- README.md
|
77
|
+
- lib/celluloid.rb
|
67
78
|
- lib/celluloid/actor.rb
|
68
79
|
- lib/celluloid/boot.rb
|
69
80
|
- lib/celluloid/calls.rb
|
70
81
|
- lib/celluloid/core_ext.rb
|
71
|
-
- lib/celluloid/cpu_counter.rb
|
72
82
|
- lib/celluloid/fiber.rb
|
73
83
|
- lib/celluloid/fsm.rb
|
74
84
|
- lib/celluloid/future.rb
|
75
85
|
- lib/celluloid/internal_pool.rb
|
76
86
|
- lib/celluloid/links.rb
|
77
87
|
- lib/celluloid/logger.rb
|
88
|
+
- lib/celluloid/logging.rb
|
78
89
|
- lib/celluloid/mailbox.rb
|
79
90
|
- lib/celluloid/method.rb
|
80
91
|
- lib/celluloid/notifications.rb
|
81
92
|
- lib/celluloid/pool_manager.rb
|
82
|
-
- lib/celluloid/proxies/abstract_proxy.rb
|
83
|
-
- lib/celluloid/proxies/actor_proxy.rb
|
84
|
-
- lib/celluloid/proxies/async_proxy.rb
|
85
|
-
- lib/celluloid/proxies/future_proxy.rb
|
86
93
|
- lib/celluloid/receivers.rb
|
87
94
|
- lib/celluloid/registry.rb
|
88
95
|
- lib/celluloid/responses.rb
|
89
96
|
- lib/celluloid/rspec.rb
|
90
97
|
- lib/celluloid/signals.rb
|
98
|
+
- lib/celluloid/stack_dumper.rb
|
91
99
|
- lib/celluloid/supervision_group.rb
|
92
100
|
- lib/celluloid/supervisor.rb
|
93
101
|
- lib/celluloid/system_events.rb
|
94
102
|
- lib/celluloid/task.rb
|
95
|
-
- lib/celluloid/tasks/task_fiber.rb
|
96
|
-
- lib/celluloid/tasks/task_thread.rb
|
97
103
|
- lib/celluloid/thread_handle.rb
|
98
104
|
- lib/celluloid/uuid.rb
|
99
105
|
- lib/celluloid/version.rb
|
100
|
-
- lib/celluloid.rb
|
106
|
+
- lib/celluloid/logging/incident.rb
|
107
|
+
- lib/celluloid/logging/incident_logger.rb
|
108
|
+
- lib/celluloid/logging/incident_reporter.rb
|
109
|
+
- lib/celluloid/logging/log_event.rb
|
110
|
+
- lib/celluloid/logging/ring_buffer.rb
|
111
|
+
- lib/celluloid/proxies/abstract_proxy.rb
|
112
|
+
- lib/celluloid/proxies/actor_proxy.rb
|
113
|
+
- lib/celluloid/proxies/async_proxy.rb
|
114
|
+
- lib/celluloid/proxies/future_proxy.rb
|
115
|
+
- lib/celluloid/tasks/task_fiber.rb
|
116
|
+
- lib/celluloid/tasks/task_thread.rb
|
101
117
|
- spec/support/actor_examples.rb
|
102
118
|
- spec/support/example_actor_class.rb
|
103
119
|
- spec/support/mailbox_examples.rb
|
@@ -105,27 +121,28 @@ files:
|
|
105
121
|
homepage: https://github.com/celluloid/celluloid
|
106
122
|
licenses:
|
107
123
|
- MIT
|
108
|
-
post_install_message:
|
124
|
+
post_install_message:
|
109
125
|
rdoc_options: []
|
110
126
|
require_paths:
|
111
127
|
- lib
|
112
128
|
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
129
|
requirements:
|
115
130
|
- - ! '>='
|
116
131
|
- !ruby/object:Gem::Version
|
117
132
|
version: 1.9.2
|
118
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
133
|
none: false
|
134
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
135
|
requirements:
|
121
136
|
- - ! '>='
|
122
137
|
- !ruby/object:Gem::Version
|
123
138
|
version: 1.3.6
|
139
|
+
none: false
|
124
140
|
requirements: []
|
125
|
-
rubyforge_project:
|
126
|
-
rubygems_version: 1.8.
|
127
|
-
signing_key:
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 1.8.15
|
143
|
+
signing_key:
|
128
144
|
specification_version: 3
|
129
145
|
summary: Actor-based concurrent object framework for Ruby
|
130
146
|
test_files: []
|
131
|
-
has_rdoc:
|
147
|
+
has_rdoc:
|
148
|
+
...
|
@@ -1,18 +0,0 @@
|
|
1
|
-
require 'rbconfig'
|
2
|
-
|
3
|
-
module Celluloid
|
4
|
-
module CPUCounter
|
5
|
-
case RbConfig::CONFIG['host_os'][/^[A-Za-z]+/]
|
6
|
-
when 'darwin'
|
7
|
-
@cores = Integer(`sysctl hw.ncpu`[/\d+/])
|
8
|
-
when 'linux'
|
9
|
-
@cores = File.read("/proc/cpuinfo").scan(/core id\s+: \d+/).uniq.size
|
10
|
-
when 'mingw', 'mswin'
|
11
|
-
@cores = Integer(`SET NUMBER_OF_PROCESSORS`[/\d+/])
|
12
|
-
else
|
13
|
-
@cores = nil
|
14
|
-
end
|
15
|
-
|
16
|
-
def self.cores; @cores; end
|
17
|
-
end
|
18
|
-
end
|