celluloid-essentials 0.20.0.pre12
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/.env-ci +4 -0
- data/.env-dev +4 -0
- data/.gitignore +10 -0
- data/.gitmodules +3 -0
- data/.rspec +5 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +29 -0
- data/CHANGES.md +0 -0
- data/Gemfile +29 -0
- data/LICENSE.txt +22 -0
- data/README.md +3 -0
- data/Rakefile +9 -0
- data/celluloid-essentials.gemspec +24 -0
- data/lib/celluloid/essentials.rb +33 -0
- data/lib/celluloid/internals/call_chain.rb +15 -0
- data/lib/celluloid/internals/cpu_counter.rb +36 -0
- data/lib/celluloid/internals/handlers.rb +42 -0
- data/lib/celluloid/internals/links.rb +38 -0
- data/lib/celluloid/internals/logger.rb +98 -0
- data/lib/celluloid/internals/method.rb +33 -0
- data/lib/celluloid/internals/properties.rb +28 -0
- data/lib/celluloid/internals/receivers.rb +64 -0
- data/lib/celluloid/internals/registry.rb +104 -0
- data/lib/celluloid/internals/responses.rb +45 -0
- data/lib/celluloid/internals/signals.rb +24 -0
- data/lib/celluloid/internals/stack.rb +76 -0
- data/lib/celluloid/internals/stack/dump.rb +14 -0
- data/lib/celluloid/internals/stack/states.rb +74 -0
- data/lib/celluloid/internals/stack/summary.rb +14 -0
- data/lib/celluloid/internals/task_set.rb +51 -0
- data/lib/celluloid/internals/thread_handle.rb +52 -0
- data/lib/celluloid/internals/uuid.rb +40 -0
- data/lib/celluloid/logging/incident.rb +21 -0
- data/lib/celluloid/logging/incident_logger.rb +128 -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/notifications.rb +95 -0
- data/lib/celluloid/probe.rb +75 -0
- data/tasks/benchmarks.rake +16 -0
- data/tasks/rspec.rake +7 -0
- data/tasks/rubocop.rake +4 -0
- metadata +117 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# Method handles that route through an actor proxy
|
4
|
+
class Method
|
5
|
+
def initialize(proxy, name)
|
6
|
+
raise NoMethodError, "undefined method `#{name}'" unless proxy.respond_to? name
|
7
|
+
|
8
|
+
@proxy, @name = proxy, name
|
9
|
+
@klass = @proxy.class
|
10
|
+
end
|
11
|
+
|
12
|
+
def arity
|
13
|
+
@proxy.method_missing(:method, @name).arity
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
@proxy.method_missing(:method, @name).name
|
18
|
+
end
|
19
|
+
|
20
|
+
def parameters
|
21
|
+
@proxy.method_missing(:method, @name).parameters
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(*args, &block)
|
25
|
+
@proxy.__send__(@name, *args, &block)
|
26
|
+
end
|
27
|
+
|
28
|
+
def inspect
|
29
|
+
"#<Celluloid::Internals::Method #{@klass}##{@name}>"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# Properties define inheritable attributes of classes, somewhat similar to
|
4
|
+
# Rails cattr_*/mattr_* or class_attribute
|
5
|
+
module Properties
|
6
|
+
def property(name, opts = {})
|
7
|
+
default = opts.fetch(:default, nil)
|
8
|
+
multi = opts.fetch(:multi, false)
|
9
|
+
ivar_name = "@#{name}".to_sym
|
10
|
+
|
11
|
+
singleton = class << ancestors.first; self; end
|
12
|
+
singleton.send(:remove_method, name) rescue nil
|
13
|
+
singleton.send(:define_method, name) do |value = nil, *extra|
|
14
|
+
if value
|
15
|
+
value = value ? [value, *send(name), *extra].uniq : [] if multi
|
16
|
+
instance_variable_set(ivar_name, value)
|
17
|
+
elsif instance_variables.include?(ivar_name)
|
18
|
+
instance_variable_get(ivar_name)
|
19
|
+
elsif superclass.respond_to? name
|
20
|
+
superclass.send(name)
|
21
|
+
else
|
22
|
+
default
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'timers'
|
3
|
+
|
4
|
+
module Celluloid
|
5
|
+
module Internals
|
6
|
+
# Allow methods to directly interact with the actor protocol
|
7
|
+
class Receivers
|
8
|
+
def initialize(timers)
|
9
|
+
@receivers = Set.new
|
10
|
+
@timers = timers
|
11
|
+
end
|
12
|
+
|
13
|
+
# Receive an asynchronous message
|
14
|
+
def receive(timeout = nil, &block)
|
15
|
+
if Celluloid.exclusive?
|
16
|
+
Celluloid.mailbox.receive(timeout, &block)
|
17
|
+
else
|
18
|
+
receiver = Receiver.new block
|
19
|
+
|
20
|
+
if timeout
|
21
|
+
receiver.timer = @timers.after(timeout) do
|
22
|
+
@receivers.delete receiver
|
23
|
+
receiver.resume
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
@receivers << receiver
|
28
|
+
Task.suspend :receiving
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Handle incoming messages
|
33
|
+
def handle_message(message)
|
34
|
+
receiver = @receivers.find { |r| r.match(message) }
|
35
|
+
return unless receiver
|
36
|
+
|
37
|
+
@receivers.delete receiver
|
38
|
+
receiver.timer.cancel if receiver.timer
|
39
|
+
receiver.resume message
|
40
|
+
message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Methods blocking on a call to receive
|
45
|
+
class Receiver
|
46
|
+
attr_accessor :timer
|
47
|
+
|
48
|
+
def initialize(block)
|
49
|
+
@block = block
|
50
|
+
@task = Task.current
|
51
|
+
@timer = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Match a message with this receiver's block
|
55
|
+
def match(message)
|
56
|
+
@block ? @block.call(message) : true
|
57
|
+
end
|
58
|
+
|
59
|
+
def resume(message = nil)
|
60
|
+
@task.resume message
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Celluloid
|
4
|
+
module Internals
|
5
|
+
# The Registry allows us to refer to specific actors by human-meaningful names
|
6
|
+
class Registry
|
7
|
+
def initialize
|
8
|
+
@root = nil # keep root out of the standard list of registered names
|
9
|
+
@actors = {} # hash of name => actor
|
10
|
+
@index = {} # hash of name => branch
|
11
|
+
@branches = {} # hash of branch => [ actors ]
|
12
|
+
@registry = Mutex.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# Register an Actor
|
16
|
+
def []=(name, actor)
|
17
|
+
if name == :root
|
18
|
+
@registry.synchronize {
|
19
|
+
@root = actor
|
20
|
+
}
|
21
|
+
else
|
22
|
+
actor_singleton = class << actor; self; end
|
23
|
+
unless actor_singleton.ancestors.include? Proxy::Abstract
|
24
|
+
raise TypeError, "not an actor"
|
25
|
+
end
|
26
|
+
|
27
|
+
=begin
|
28
|
+
if actor.class.ancestors.include? Supervision::Container
|
29
|
+
puts "Supervisor: #{actor.links.inspect}"
|
30
|
+
end
|
31
|
+
=end
|
32
|
+
@registry.synchronize do
|
33
|
+
@actors[name.to_sym] = actor
|
34
|
+
end
|
35
|
+
actor.mailbox << NamingRequest.new(name.to_sym)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def add(name,actor,branch=:services)
|
40
|
+
set(name, actor)
|
41
|
+
@registry.synchronize {
|
42
|
+
unless @branches.key? branch
|
43
|
+
@branches[branch] = []
|
44
|
+
self.class.instance_eval {
|
45
|
+
remove_method(branch) rescue nil
|
46
|
+
define_method(branch) { @branches[branch] }
|
47
|
+
}
|
48
|
+
@branches[branch] << name
|
49
|
+
end
|
50
|
+
@index[name.to_sym] = branch
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
# Retrieve an actor by name
|
55
|
+
def [](name)
|
56
|
+
return @root if name == :root
|
57
|
+
@registry.synchronize {
|
58
|
+
@actors[name.to_sym]
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
def branch(name)
|
63
|
+
@registry.synchronize {
|
64
|
+
@index.select { |a,b| b == name }
|
65
|
+
}
|
66
|
+
end
|
67
|
+
|
68
|
+
alias_method :get, :[]
|
69
|
+
alias_method :set, :[]=
|
70
|
+
|
71
|
+
def delete(name)
|
72
|
+
@registry.synchronize {
|
73
|
+
@index.delete name.to_sym
|
74
|
+
@actors.delete name.to_sym
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
def include? name
|
79
|
+
names.include? name
|
80
|
+
end
|
81
|
+
|
82
|
+
# List all registered actors by name
|
83
|
+
def names
|
84
|
+
@registry.synchronize { @actors.keys }
|
85
|
+
end
|
86
|
+
|
87
|
+
def index
|
88
|
+
@registry.synchronize { @index }
|
89
|
+
end
|
90
|
+
|
91
|
+
# removes and returns all registered actors as a hash of `name => actor`
|
92
|
+
# can be used in testing to clear the registry
|
93
|
+
def clear
|
94
|
+
hash = nil
|
95
|
+
@registry.synchronize {
|
96
|
+
hash = @actors.dup
|
97
|
+
@actors.clear
|
98
|
+
@index.clear
|
99
|
+
}
|
100
|
+
hash
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# Responses to calls
|
4
|
+
class Response
|
5
|
+
attr_reader :call, :value
|
6
|
+
|
7
|
+
def initialize(call, value)
|
8
|
+
@call, @value = call, value
|
9
|
+
end
|
10
|
+
|
11
|
+
def dispatch
|
12
|
+
@call.task.resume self
|
13
|
+
end
|
14
|
+
|
15
|
+
# Call completed successfully
|
16
|
+
class Success < Response; end
|
17
|
+
|
18
|
+
# Call was aborted due to sender error
|
19
|
+
class Error < Response
|
20
|
+
def value
|
21
|
+
ex = super
|
22
|
+
ex = ex.cause if ex.is_a? Celluloid::AbortError
|
23
|
+
|
24
|
+
if ex.backtrace
|
25
|
+
ex.backtrace << "(celluloid):0:in `remote procedure call'"
|
26
|
+
ex.backtrace.concat(caller)
|
27
|
+
end
|
28
|
+
|
29
|
+
raise ex
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Block
|
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
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
# Event signaling between methods of the same object
|
4
|
+
class Signals
|
5
|
+
def initialize
|
6
|
+
@conditions = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
# Wait for the given signal and return the associated value
|
10
|
+
def wait(name)
|
11
|
+
raise "cannot wait for signals while exclusive" if Celluloid.exclusive?
|
12
|
+
|
13
|
+
@conditions[name] ||= Condition.new
|
14
|
+
@conditions[name].wait
|
15
|
+
end
|
16
|
+
|
17
|
+
# Send a signal to all method calls waiting for the given name
|
18
|
+
def broadcast(name, value = nil)
|
19
|
+
condition = @conditions.delete(name)
|
20
|
+
condition.broadcast(value) if condition
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
class Stack
|
4
|
+
|
5
|
+
attr_accessor :actors, :threads
|
6
|
+
|
7
|
+
def initialize(threads)
|
8
|
+
@group = threads
|
9
|
+
@actors = []
|
10
|
+
@threads = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def snapshot(backtrace=nil)
|
14
|
+
@group.each do |thread|
|
15
|
+
if thread.role == :actor
|
16
|
+
@actors << snapshot_actor(thread.actor,backtrace) if thread.actor
|
17
|
+
else
|
18
|
+
@threads << snapshot_thread(thread,backtrace)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def snapshot_actor(actor,backtrace=nil)
|
24
|
+
state = ActorState.new
|
25
|
+
state.id = actor.object_id
|
26
|
+
|
27
|
+
# TODO: delegate to the behavior
|
28
|
+
if actor.behavior.is_a?(Cell)
|
29
|
+
state.cell = snapshot_cell(actor.behavior)
|
30
|
+
end
|
31
|
+
|
32
|
+
tasks = actor.tasks
|
33
|
+
if tasks.empty?
|
34
|
+
state.status = :idle
|
35
|
+
else
|
36
|
+
state.status = :running
|
37
|
+
state.tasks = tasks.to_a.map { |t| TaskState.new(t.class, t.type, t.meta, t.status, t.backtrace) }
|
38
|
+
end
|
39
|
+
|
40
|
+
state.backtrace = actor.thread.backtrace if backtrace and actor.thread
|
41
|
+
state
|
42
|
+
end
|
43
|
+
|
44
|
+
def snapshot_cell(behavior)
|
45
|
+
state = CellState.new
|
46
|
+
state.subject_id = behavior.subject.object_id
|
47
|
+
state.subject_class = behavior.subject.class
|
48
|
+
state
|
49
|
+
end
|
50
|
+
|
51
|
+
def snapshot_thread(thread,backtrace=nil)
|
52
|
+
backtrace = begin
|
53
|
+
thread.backtrace
|
54
|
+
rescue NoMethodError # for Rubinius < 2.5.2.c145
|
55
|
+
[]
|
56
|
+
end if backtrace
|
57
|
+
ThreadState.new(thread.object_id, backtrace, thread.role)
|
58
|
+
end
|
59
|
+
|
60
|
+
def print(output = STDERR)
|
61
|
+
@actors.each do |actor|
|
62
|
+
output.print actor.dump
|
63
|
+
end
|
64
|
+
|
65
|
+
@threads.each do |thread|
|
66
|
+
output.print thread.dump
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
require 'celluloid/internals/stack/states'
|
75
|
+
require 'celluloid/internals/stack/dump'
|
76
|
+
require 'celluloid/internals/stack/summary'
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Celluloid
|
2
|
+
module Internals
|
3
|
+
class Stack
|
4
|
+
|
5
|
+
module DisplayBacktrace
|
6
|
+
def display_backtrace(backtrace, output, indent = nil)
|
7
|
+
backtrace ||= ["EMPTY BACKTRACE"]
|
8
|
+
backtrace.each do |line|
|
9
|
+
output << indent if indent
|
10
|
+
output << "\t" << line << "\n"
|
11
|
+
end
|
12
|
+
output << "\n\n"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class TaskState < Struct.new(:task_class, :type, :meta, :status, :backtrace); end
|
17
|
+
|
18
|
+
class CellState < Struct.new(:subject_id, :subject_class)
|
19
|
+
def dump
|
20
|
+
"Celluloid::Cell 0x#{subject_id.to_s(16)}: #{subject_class}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class ThreadState < Struct.new(:thread_id, :backtrace, :role)
|
25
|
+
include DisplayBacktrace
|
26
|
+
def dump
|
27
|
+
string = ""
|
28
|
+
string << "Thread 0x#{thread_id.to_s(16)} (#{role}):\n"
|
29
|
+
display_backtrace backtrace, string if backtrace
|
30
|
+
string
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ActorState
|
35
|
+
include DisplayBacktrace
|
36
|
+
attr_accessor :name, :id, :cell
|
37
|
+
attr_accessor :status, :tasks
|
38
|
+
attr_accessor :backtrace
|
39
|
+
|
40
|
+
def dump
|
41
|
+
string = ""
|
42
|
+
string << "Celluloid::Actor 0x#{id.to_s(16)}"
|
43
|
+
string << " [#{name}]" if name
|
44
|
+
string << "\n"
|
45
|
+
|
46
|
+
if cell
|
47
|
+
string << cell.dump
|
48
|
+
string << "\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
if status == :idle
|
52
|
+
string << "State: Idle (waiting for messages)\n"
|
53
|
+
display_backtrace backtrace, string if backtrace
|
54
|
+
else
|
55
|
+
string << "State: Running (executing tasks)\n"
|
56
|
+
display_backtrace backtrace, string if backtrace
|
57
|
+
string << "\tTasks:\n"
|
58
|
+
|
59
|
+
tasks.each_with_index do |task, i|
|
60
|
+
string << "\t #{i+1}) #{task.task_class}[#{task.type}]: #{task.status}\n"
|
61
|
+
if task.backtrace
|
62
|
+
string << "\t #{task.meta.inspect}\n"
|
63
|
+
display_backtrace task.backtrace, string, "\t"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
string << "\n" unless backtrace
|
68
|
+
string
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|