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