celluloid 0.17.4 → 0.18.0

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 (171) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +300 -81
  3. data/CONDUCT.md +13 -0
  4. data/CONTRIBUTING.md +39 -0
  5. data/README.md +54 -155
  6. data/REFACTOR.md +1 -0
  7. data/architecture.md +120 -0
  8. data/examples/basic_usage.rb +1 -1
  9. data/examples/configurations.rb +78 -0
  10. data/examples/futures.rb +1 -1
  11. data/examples/ring.rb +5 -4
  12. data/examples/simple_pmap.rb +1 -1
  13. data/examples/stack.rb +2 -2
  14. data/examples/supervisors_and_registry.rb +82 -0
  15. data/examples/timers.rb +2 -2
  16. data/lib/celluloid/actor/system.rb +13 -29
  17. data/lib/celluloid/actor.rb +27 -17
  18. data/lib/celluloid/autostart.rb +6 -1
  19. data/lib/celluloid/call/async.rb +2 -0
  20. data/lib/celluloid/call/sync.rb +10 -3
  21. data/lib/celluloid/calls.rb +13 -12
  22. data/lib/celluloid/cell.rb +5 -9
  23. data/lib/celluloid/condition.rb +3 -3
  24. data/lib/celluloid/core_ext.rb +0 -2
  25. data/lib/celluloid/debug.rb +3 -0
  26. data/lib/celluloid/exceptions.rb +2 -2
  27. data/lib/celluloid/future.rb +8 -10
  28. data/lib/celluloid/group/pool.rb +1 -3
  29. data/lib/celluloid/group/spawner.rb +2 -6
  30. data/lib/celluloid/group.rb +12 -8
  31. data/lib/celluloid/internals/call_chain.rb +15 -0
  32. data/lib/celluloid/internals/cpu_counter.rb +62 -0
  33. data/lib/celluloid/internals/handlers.rb +42 -0
  34. data/lib/celluloid/internals/links.rb +38 -0
  35. data/lib/celluloid/internals/logger.rb +104 -0
  36. data/lib/celluloid/internals/method.rb +34 -0
  37. data/lib/celluloid/internals/properties.rb +32 -0
  38. data/lib/celluloid/internals/receivers.rb +64 -0
  39. data/lib/celluloid/internals/registry.rb +102 -0
  40. data/lib/celluloid/internals/responses.rb +46 -0
  41. data/lib/celluloid/internals/signals.rb +24 -0
  42. data/lib/celluloid/internals/stack/dump.rb +12 -0
  43. data/lib/celluloid/internals/stack/states.rb +72 -0
  44. data/lib/celluloid/internals/stack/summary.rb +12 -0
  45. data/lib/celluloid/internals/stack.rb +74 -0
  46. data/lib/celluloid/internals/task_set.rb +51 -0
  47. data/lib/celluloid/internals/thread_handle.rb +52 -0
  48. data/lib/celluloid/internals/uuid.rb +40 -0
  49. data/lib/celluloid/logging/incident.rb +21 -0
  50. data/lib/celluloid/logging/incident_logger.rb +147 -0
  51. data/lib/celluloid/logging/incident_reporter.rb +49 -0
  52. data/lib/celluloid/logging/log_event.rb +20 -0
  53. data/lib/celluloid/logging/ring_buffer.rb +64 -0
  54. data/lib/celluloid/mailbox/evented.rb +13 -5
  55. data/lib/celluloid/mailbox.rb +22 -9
  56. data/lib/celluloid/notifications.rb +95 -0
  57. data/lib/celluloid/pool.rb +6 -0
  58. data/lib/celluloid/probe.rb +81 -0
  59. data/lib/celluloid/proxy/abstract.rb +9 -9
  60. data/lib/celluloid/proxy/async.rb +1 -1
  61. data/lib/celluloid/proxy/block.rb +2 -2
  62. data/lib/celluloid/proxy/cell.rb +1 -1
  63. data/lib/celluloid/proxy/future.rb +2 -4
  64. data/lib/celluloid/proxy/sync.rb +1 -3
  65. data/lib/celluloid/rspec.rb +22 -33
  66. data/lib/celluloid/supervision/configuration/injections.rb +8 -0
  67. data/lib/celluloid/supervision/configuration/instance.rb +113 -0
  68. data/lib/celluloid/supervision/configuration.rb +169 -0
  69. data/lib/celluloid/supervision/constants.rb +123 -0
  70. data/lib/celluloid/supervision/container/behavior/pool.rb +71 -0
  71. data/lib/celluloid/supervision/container/behavior/tree.rb +23 -0
  72. data/lib/celluloid/supervision/container/behavior.rb +89 -0
  73. data/lib/celluloid/supervision/container/injections.rb +8 -0
  74. data/lib/celluloid/supervision/container/instance.rb +116 -0
  75. data/lib/celluloid/supervision/container/pool.rb +210 -0
  76. data/lib/celluloid/supervision/container.rb +144 -0
  77. data/lib/celluloid/supervision/service.rb +27 -0
  78. data/lib/celluloid/supervision/supervise.rb +34 -0
  79. data/lib/celluloid/supervision/validation.rb +40 -0
  80. data/lib/celluloid/supervision/version.rb +5 -0
  81. data/lib/celluloid/supervision.rb +17 -0
  82. data/lib/celluloid/system_events.rb +11 -6
  83. data/lib/celluloid/task/fibered.rb +6 -2
  84. data/lib/celluloid/task/threaded.rb +3 -3
  85. data/lib/celluloid/task.rb +25 -12
  86. data/lib/celluloid/test.rb +5 -2
  87. data/lib/celluloid/thread.rb +0 -2
  88. data/lib/celluloid/version.rb +1 -1
  89. data/lib/celluloid.rb +74 -64
  90. data/spec/celluloid/block_spec.rb +29 -32
  91. data/spec/celluloid/calls_spec.rb +5 -15
  92. data/spec/celluloid/future_spec.rb +7 -1
  93. data/spec/celluloid/internals/cpu_counter_spec.rb +129 -0
  94. data/spec/celluloid/internals/links_spec.rb +43 -0
  95. data/spec/celluloid/internals/properties_spec.rb +40 -0
  96. data/spec/celluloid/internals/registry_spec.rb +62 -0
  97. data/spec/celluloid/internals/stack/dump_spec.rb +4 -0
  98. data/spec/celluloid/internals/stack/summary_spec.rb +4 -0
  99. data/spec/celluloid/internals/thread_handle_spec.rb +60 -0
  100. data/spec/celluloid/internals/uuid_spec.rb +9 -0
  101. data/spec/celluloid/logging/ring_buffer_spec.rb +36 -0
  102. data/spec/celluloid/mailbox/evented_spec.rb +11 -22
  103. data/spec/celluloid/misc/leak_spec.rb +3 -4
  104. data/spec/celluloid/notifications_spec.rb +140 -0
  105. data/spec/celluloid/probe_spec.rb +102 -0
  106. data/spec/celluloid/proxy_spec.rb +30 -30
  107. data/spec/celluloid/supervision/behavior_spec.rb +74 -0
  108. data/spec/celluloid/supervision/configuration_spec.rb +181 -0
  109. data/spec/celluloid/supervision/container_spec.rb +72 -0
  110. data/spec/celluloid/supervision/instance_spec.rb +13 -0
  111. data/spec/celluloid/supervision/root_spec.rb +28 -0
  112. data/spec/celluloid/supervision/supervisor_spec.rb +93 -0
  113. data/spec/celluloid/task/fibered_spec.rb +1 -3
  114. data/spec/celluloid/task/threaded_spec.rb +1 -3
  115. data/spec/shared/actor_examples.rb +58 -33
  116. data/spec/shared/group_examples.rb +2 -2
  117. data/spec/shared/mailbox_examples.rb +1 -1
  118. data/spec/shared/stack_examples.rb +87 -0
  119. data/spec/shared/task_examples.rb +2 -3
  120. data/spec/spec_helper.rb +2 -4
  121. data/spec/support/configure_rspec.rb +2 -3
  122. data/spec/support/coverage.rb +2 -4
  123. data/spec/support/crash_checking.rb +2 -2
  124. data/spec/support/examples/actor_class.rb +3 -8
  125. data/spec/support/examples/call_class.rb +2 -2
  126. data/spec/support/examples/container_class.rb +35 -0
  127. data/spec/support/examples/evented_mailbox_class.rb +1 -2
  128. data/spec/support/examples/stack_classes.rb +58 -0
  129. data/spec/support/examples/stack_methods.rb +23 -0
  130. data/spec/support/examples/subordinate_class.rb +19 -0
  131. data/spec/support/logging.rb +3 -34
  132. data/spec/support/loose_threads.rb +3 -16
  133. data/spec/support/reset_class_variables.rb +5 -1
  134. data/spec/support/stubbing.rb +1 -1
  135. metadata +91 -289
  136. data/culture/CONDUCT.md +0 -28
  137. data/culture/Gemfile +0 -9
  138. data/culture/LICENSE.txt +0 -22
  139. data/culture/README.md +0 -22
  140. data/culture/Rakefile +0 -5
  141. data/culture/SYNC.md +0 -70
  142. data/culture/celluloid-culture.gemspec +0 -18
  143. data/culture/gems/README.md +0 -39
  144. data/culture/gems/dependencies.yml +0 -85
  145. data/culture/gems/loader.rb +0 -101
  146. data/culture/rubocop/README.md +0 -38
  147. data/culture/rubocop/lint.yml +0 -8
  148. data/culture/rubocop/metrics.yml +0 -15
  149. data/culture/rubocop/perf.yml +0 -0
  150. data/culture/rubocop/rubocop.yml +0 -5
  151. data/culture/rubocop/style.yml +0 -57
  152. data/culture/spec/gems_spec.rb +0 -2
  153. data/culture/spec/spec_helper.rb +0 -0
  154. data/culture/spec/sync_spec.rb +0 -2
  155. data/culture/sync.rb +0 -56
  156. data/culture/tasks/rspec.rake +0 -5
  157. data/culture/tasks/rubocop.rake +0 -2
  158. data/lib/celluloid/actor/manager.rb +0 -7
  159. data/lib/celluloid/backported.rb +0 -2
  160. data/lib/celluloid/current.rb +0 -2
  161. data/lib/celluloid/deprecate.rb +0 -21
  162. data/lib/celluloid/fiber.rb +0 -32
  163. data/lib/celluloid/managed.rb +0 -3
  164. data/lib/celluloid/notices.rb +0 -15
  165. data/spec/deprecate/actor_system_spec.rb +0 -72
  166. data/spec/deprecate/block_spec.rb +0 -52
  167. data/spec/deprecate/calls_spec.rb +0 -39
  168. data/spec/deprecate/evented_mailbox_spec.rb +0 -34
  169. data/spec/deprecate/future_spec.rb +0 -32
  170. data/spec/deprecate/internal_pool_spec.rb +0 -4
  171. data/spec/support/env.rb +0 -21
@@ -0,0 +1,42 @@
1
+ require "set"
2
+
3
+ module Celluloid
4
+ module Internals
5
+ class Handlers
6
+ def initialize
7
+ @handlers = Set.new
8
+ end
9
+
10
+ def handle(*patterns, &block)
11
+ patterns.each do |pattern|
12
+ handler = Handler.new pattern, block
13
+ @handlers << handler
14
+ end
15
+ end
16
+
17
+ # Handle incoming messages
18
+ def handle_message(message)
19
+ handler = @handlers.find { |h| h.match(message) }
20
+ handler.call(message) if handler
21
+ handler
22
+ end
23
+ end
24
+
25
+ # Methods blocking on a call to receive
26
+ class Handler
27
+ def initialize(pattern, block)
28
+ @pattern = pattern
29
+ @block = block
30
+ end
31
+
32
+ # Match a message with this receiver's block
33
+ def match(message)
34
+ @pattern === message
35
+ end
36
+
37
+ def call(message)
38
+ @block.call message
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,38 @@
1
+ module Celluloid
2
+ module Internals
3
+ # Linked actors send each other system events
4
+ class Links
5
+ include Enumerable
6
+
7
+ def initialize
8
+ @links = {}
9
+ end
10
+
11
+ # Add an actor to the current links
12
+ def <<(actor)
13
+ @links[actor.mailbox.address] = actor
14
+ end
15
+
16
+ # Do links include the given actor?
17
+ def include?(actor)
18
+ @links.key? actor.mailbox.address
19
+ end
20
+
21
+ # Remove an actor from the links
22
+ def delete(actor)
23
+ @links.delete actor.mailbox.address
24
+ end
25
+
26
+ # Iterate through all links
27
+ def each
28
+ @links.each { |_, actor| yield(actor) }
29
+ end
30
+
31
+ # Generate a string representation
32
+ def inspect
33
+ links = map(&:inspect).join(",")
34
+ "#<#{self.class}[#{links}]>"
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,104 @@
1
+ module Celluloid
2
+ module Internals
3
+ module Logger
4
+ class WithBacktrace
5
+ def initialize(backtrace)
6
+ @backtrace = backtrace
7
+ end
8
+
9
+ def debug(string)
10
+ # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!!
11
+ # rubocop:disable Style/GlobalVars
12
+ Celluloid.logger.debug(decorate(string)) if $CELLULOID_DEBUG
13
+ # rubocop:enable Style/GlobalVars
14
+ end
15
+
16
+ def info(string)
17
+ Celluloid.logger.info(decorate(string))
18
+ end
19
+
20
+ def warn(string)
21
+ Celluloid.logger.warn(decorate(string))
22
+ end
23
+
24
+ def error(string)
25
+ Celluloid.logger.error(decorate(string))
26
+ end
27
+
28
+ def decorate(string)
29
+ [string, @backtrace].join("\n\t")
30
+ end
31
+ end
32
+
33
+ @exception_handlers = []
34
+
35
+ module_function
36
+
37
+ def with_backtrace(backtrace)
38
+ yield WithBacktrace.new(backtrace) if Celluloid.logger
39
+ end
40
+
41
+ # Send a debug message
42
+ def debug(string)
43
+ # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!!
44
+ # rubocop:disable Style/GlobalVars
45
+ Celluloid.logger.debug(string) if Celluloid.logger && $CELLULOID_DEBUG
46
+ # rubocop:enable Style/GlobalVars
47
+ end
48
+
49
+ # Send a info message
50
+ def info(string)
51
+ Celluloid.logger.info(string) if Celluloid.logger
52
+ end
53
+
54
+ # Send a warning message
55
+ def warn(string)
56
+ Celluloid.logger.warn(string) if Celluloid.logger
57
+ end
58
+
59
+ # Send an error message
60
+ def error(string)
61
+ Celluloid.logger.error(string) if Celluloid.logger
62
+ end
63
+
64
+ # Handle a crash
65
+ def crash(string, exception)
66
+ if Celluloid.log_actor_crashes
67
+ string << "\n" << format_exception(exception)
68
+ error string
69
+ end
70
+
71
+ @exception_handlers.each do |handler|
72
+ begin
73
+ handler.call(exception)
74
+ rescue => ex
75
+ error "EXCEPTION HANDLER CRASHED:\n" << format_exception(ex)
76
+ end
77
+ end
78
+ end
79
+
80
+ # Note a deprecation
81
+ def deprecate(message)
82
+ trace = caller.join("\n\t")
83
+ warn "DEPRECATION WARNING: #{message}\n\t#{trace}"
84
+ end
85
+
86
+ # Define an exception handler
87
+ # NOTE: These should be defined at application start time
88
+ def exception_handler(&block)
89
+ @exception_handlers << block
90
+ nil
91
+ end
92
+
93
+ # Format an exception message
94
+ def format_exception(exception)
95
+ str = "#{exception.class}: #{exception}\n\t"
96
+ str << if exception.backtrace
97
+ exception.backtrace.join("\n\t")
98
+ else
99
+ "EMPTY BACKTRACE\n\t"
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,34 @@
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 = proxy
9
+ @name = name
10
+ @klass = @proxy.class
11
+ end
12
+
13
+ def arity
14
+ @proxy.method_missing(:method, @name).arity
15
+ end
16
+
17
+ def name
18
+ @proxy.method_missing(:method, @name).name
19
+ end
20
+
21
+ def parameters
22
+ @proxy.method_missing(:method, @name).parameters
23
+ end
24
+
25
+ def call(*args, &block)
26
+ @proxy.__send__(@name, *args, &block)
27
+ end
28
+
29
+ def inspect
30
+ "#<Celluloid::Internals::Method #{@klass}##{@name}>"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,32 @@
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
+ begin
13
+ singleton.send(:remove_method, name)
14
+ rescue
15
+ nil
16
+ end
17
+ singleton.send(:define_method, name) do |value = nil, *extra|
18
+ if value
19
+ value = value ? [value, *send(name), *extra].uniq : [] if multi
20
+ instance_variable_set(ivar_name, value)
21
+ elsif instance_variables.include?(ivar_name)
22
+ instance_variable_get(ivar_name)
23
+ elsif superclass.respond_to? name
24
+ superclass.send(name)
25
+ else
26
+ default
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ 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,102 @@
1
+ module Celluloid
2
+ module Internals
3
+ # The Registry allows us to refer to specific actors by human-meaningful names
4
+ class Registry
5
+ def initialize
6
+ @root = nil # keep root out of the standard list of registered names
7
+ @actors = {} # hash of name => actor
8
+ @index = {} # hash of name => branch
9
+ @branches = {} # hash of branch => [ actors ]
10
+ @registry = Mutex.new
11
+ end
12
+
13
+ # Register an Actor
14
+ def []=(name, actor)
15
+ if name == :root
16
+ @registry.synchronize do
17
+ @root = actor
18
+ end
19
+ else
20
+ actor_singleton = class << actor; self; end
21
+ raise TypeError, "not an actor" unless actor_singleton.ancestors.include? Proxy::Abstract
22
+
23
+ # if actor.class.ancestors.include? Supervision::Container
24
+ # puts "Supervisor: #{actor.links.inspect}"
25
+ # end
26
+ @registry.synchronize do
27
+ @actors[name.to_sym] = actor
28
+ end
29
+ actor.mailbox << NamingRequest.new(name.to_sym)
30
+ end
31
+ end
32
+
33
+ def add(name, actor, branch = :services)
34
+ set(name, actor)
35
+ @registry.synchronize do
36
+ unless @branches.key? branch
37
+ @branches[branch] = []
38
+ self.class.instance_eval do
39
+ begin
40
+ remove_method(branch)
41
+ rescue
42
+ nil
43
+ end
44
+ define_method(branch) { @branches[branch] }
45
+ end
46
+ @branches[branch] << name
47
+ end
48
+ @index[name.to_sym] = branch
49
+ end
50
+ end
51
+
52
+ # Retrieve an actor by name
53
+ def [](name)
54
+ return @root if name == :root
55
+ @registry.synchronize do
56
+ @actors[name.to_sym]
57
+ end
58
+ end
59
+
60
+ def branch(name)
61
+ @registry.synchronize do
62
+ @index.select { |_a, b| b == name }
63
+ end
64
+ end
65
+
66
+ alias get []
67
+ alias set []=
68
+
69
+ def delete(name)
70
+ @registry.synchronize do
71
+ @index.delete name.to_sym
72
+ @actors.delete name.to_sym
73
+ end
74
+ end
75
+
76
+ def include?(name)
77
+ names.include? name
78
+ end
79
+
80
+ # List all registered actors by name
81
+ def names
82
+ @registry.synchronize { @actors.keys }
83
+ end
84
+
85
+ def index
86
+ @registry.synchronize { @index }
87
+ end
88
+
89
+ # removes and returns all registered actors as a hash of `name => actor`
90
+ # can be used in testing to clear the registry
91
+ def clear
92
+ hash = nil
93
+ @registry.synchronize do
94
+ hash = @actors.dup
95
+ @actors.clear
96
+ @index.clear
97
+ end
98
+ hash
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,46 @@
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 = call
9
+ @value = value
10
+ end
11
+
12
+ def dispatch
13
+ @call.task.resume self
14
+ end
15
+
16
+ # Call completed successfully
17
+ class Success < Response; end
18
+
19
+ # Call was aborted due to sender error
20
+ class Error < Response
21
+ def value
22
+ ex = super
23
+ ex = ex.cause if ex.is_a? Celluloid::AbortError
24
+
25
+ if ex.backtrace
26
+ ex.backtrace << "(celluloid):0:in `remote procedure call'"
27
+ ex.backtrace.concat(caller)
28
+ end
29
+
30
+ raise ex
31
+ end
32
+ end
33
+
34
+ class Block
35
+ def initialize(call, result)
36
+ @call = call
37
+ @result = result
38
+ end
39
+
40
+ def dispatch
41
+ @call.task.resume(@result)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ 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,12 @@
1
+ module Celluloid
2
+ module Internals
3
+ class Stack
4
+ class Dump < Stack
5
+ def initialize(threads)
6
+ super(threads)
7
+ snapshot(true)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,72 @@
1
+ module Celluloid
2
+ module Internals
3
+ class Stack
4
+ module DisplayBacktrace
5
+ def display_backtrace(backtrace, output, indent = nil)
6
+ backtrace ||= ["EMPTY BACKTRACE"]
7
+ backtrace.each do |line|
8
+ output << indent if indent
9
+ output << "\t" << line << "\n"
10
+ end
11
+ output << "\n\n"
12
+ end
13
+ end
14
+
15
+ class TaskState < Struct.new(:task_class, :type, :meta, :status, :backtrace); end
16
+
17
+ class CellState < Struct.new(:subject_id, :subject_class)
18
+ def dump
19
+ "Celluloid::Cell 0x#{subject_id.to_s(16)}: #{subject_class}"
20
+ end
21
+ end
22
+
23
+ class ThreadState < Struct.new(:thread_id, :backtrace, :role)
24
+ include DisplayBacktrace
25
+ def dump
26
+ string = ""
27
+ string << "Thread 0x#{thread_id.to_s(16)} (#{role}):\n"
28
+ display_backtrace backtrace, string if backtrace
29
+ string
30
+ end
31
+ end
32
+
33
+ class ActorState
34
+ include DisplayBacktrace
35
+ attr_accessor :name, :id, :cell
36
+ attr_accessor :status, :tasks
37
+ attr_accessor :backtrace
38
+
39
+ def dump
40
+ string = ""
41
+ string << "Celluloid::Actor 0x#{id.to_s(16)}"
42
+ string << " [#{name}]" if name
43
+ string << "\n"
44
+
45
+ if cell
46
+ string << cell.dump
47
+ string << "\n"
48
+ end
49
+
50
+ if status == :idle
51
+ string << "State: Idle (waiting for messages)\n"
52
+ display_backtrace backtrace, string if backtrace
53
+ else
54
+ string << "State: Running (executing tasks)\n"
55
+ display_backtrace backtrace, string if backtrace
56
+ string << "\tTasks:\n"
57
+
58
+ tasks.each_with_index do |task, i|
59
+ string << "\t #{i + 1}) #{task.task_class}[#{task.type}]: #{task.status}\n"
60
+ if task.backtrace
61
+ string << "\t #{task.meta.inspect}\n"
62
+ display_backtrace task.backtrace, string, "\t"
63
+ end
64
+ end
65
+ end
66
+ string << "\n" unless backtrace
67
+ string
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,12 @@
1
+ module Celluloid
2
+ module Internals
3
+ class Stack
4
+ class Summary < Stack
5
+ def initialize(threads)
6
+ super(threads)
7
+ snapshot
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,74 @@
1
+ module Celluloid
2
+ module Internals
3
+ class Stack
4
+ attr_accessor :actors, :threads
5
+
6
+ def initialize(threads)
7
+ @group = threads
8
+ @actors = []
9
+ @threads = []
10
+ end
11
+
12
+ def snapshot(backtrace = nil)
13
+ @group.each do |thread|
14
+ if thread.role == :actor
15
+ @actors << snapshot_actor(thread.actor, backtrace) if thread.actor
16
+ else
17
+ @threads << snapshot_thread(thread, backtrace)
18
+ end
19
+ end
20
+ end
21
+
22
+ def snapshot_actor(actor, backtrace = nil)
23
+ state = ActorState.new
24
+ state.id = actor.object_id
25
+
26
+ # TODO: delegate to the behavior
27
+ state.cell = snapshot_cell(actor.behavior) if actor.behavior.is_a?(Cell)
28
+
29
+ tasks = actor.tasks
30
+ if tasks.empty?
31
+ state.status = :idle
32
+ else
33
+ state.status = :running
34
+ state.tasks = tasks.to_a.map { |t| TaskState.new(t.class, t.type, t.meta, t.status, t.backtrace) }
35
+ end
36
+
37
+ state.backtrace = actor.thread.backtrace if backtrace && actor.thread
38
+ state
39
+ end
40
+
41
+ def snapshot_cell(behavior)
42
+ state = CellState.new
43
+ state.subject_id = behavior.subject.object_id
44
+ state.subject_class = behavior.subject.class
45
+ state
46
+ end
47
+
48
+ def snapshot_thread(thread, backtrace = nil)
49
+ if backtrace
50
+ backtrace = begin
51
+ thread.backtrace
52
+ rescue NoMethodError # for Rubinius < 2.5.2.c145
53
+ []
54
+ end
55
+ end
56
+ ThreadState.new(thread.object_id, backtrace, thread.role)
57
+ end
58
+
59
+ def print(output = STDERR)
60
+ @actors.each do |actor|
61
+ output.print actor.dump
62
+ end
63
+
64
+ @threads.each do |thread|
65
+ output.print thread.dump
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ require "celluloid/internals/stack/states"
73
+ require "celluloid/internals/stack/dump"
74
+ require "celluloid/internals/stack/summary"