celluloid-essentials 0.20.0.pre12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.env-ci +4 -0
  3. data/.env-dev +4 -0
  4. data/.gitignore +10 -0
  5. data/.gitmodules +3 -0
  6. data/.rspec +5 -0
  7. data/.rubocop.yml +2 -0
  8. data/.travis.yml +29 -0
  9. data/CHANGES.md +0 -0
  10. data/Gemfile +29 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +3 -0
  13. data/Rakefile +9 -0
  14. data/celluloid-essentials.gemspec +24 -0
  15. data/lib/celluloid/essentials.rb +33 -0
  16. data/lib/celluloid/internals/call_chain.rb +15 -0
  17. data/lib/celluloid/internals/cpu_counter.rb +36 -0
  18. data/lib/celluloid/internals/handlers.rb +42 -0
  19. data/lib/celluloid/internals/links.rb +38 -0
  20. data/lib/celluloid/internals/logger.rb +98 -0
  21. data/lib/celluloid/internals/method.rb +33 -0
  22. data/lib/celluloid/internals/properties.rb +28 -0
  23. data/lib/celluloid/internals/receivers.rb +64 -0
  24. data/lib/celluloid/internals/registry.rb +104 -0
  25. data/lib/celluloid/internals/responses.rb +45 -0
  26. data/lib/celluloid/internals/signals.rb +24 -0
  27. data/lib/celluloid/internals/stack.rb +76 -0
  28. data/lib/celluloid/internals/stack/dump.rb +14 -0
  29. data/lib/celluloid/internals/stack/states.rb +74 -0
  30. data/lib/celluloid/internals/stack/summary.rb +14 -0
  31. data/lib/celluloid/internals/task_set.rb +51 -0
  32. data/lib/celluloid/internals/thread_handle.rb +52 -0
  33. data/lib/celluloid/internals/uuid.rb +40 -0
  34. data/lib/celluloid/logging/incident.rb +21 -0
  35. data/lib/celluloid/logging/incident_logger.rb +128 -0
  36. data/lib/celluloid/logging/incident_reporter.rb +48 -0
  37. data/lib/celluloid/logging/log_event.rb +20 -0
  38. data/lib/celluloid/logging/ring_buffer.rb +65 -0
  39. data/lib/celluloid/notifications.rb +95 -0
  40. data/lib/celluloid/probe.rb +75 -0
  41. data/tasks/benchmarks.rake +16 -0
  42. data/tasks/rspec.rake +7 -0
  43. data/tasks/rubocop.rake +4 -0
  44. 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,14 @@
1
+ module Celluloid
2
+ module Internals
3
+ class Stack
4
+ class Dump < Stack
5
+
6
+ def initialize(threads)
7
+ super(threads)
8
+ snapshot(true)
9
+ end
10
+
11
+ end
12
+ end
13
+ end
14
+ end
@@ -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