celluloid 0.17.4 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
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,71 @@
1
+ require "set"
2
+
3
+ module Celluloid
4
+ module ClassMethods
5
+ extend Forwardable
6
+ def_delegators :"Celluloid::Supervision::Container::Pool", :pooling_options
7
+ # Create a new pool of workers. Accepts the following options:
8
+ #
9
+ # * size: how many workers to create. Default is worker per CPU core
10
+ # * args: array of arguments to pass when creating a worker
11
+ #
12
+ def pool(config = {}, &block)
13
+ _ = Celluloid.supervise(pooling_options(config, block: block, actors: self))
14
+ _.actors.last
15
+ end
16
+
17
+ # Same as pool, but links to the pool manager
18
+ def pool_link(klass, config = {}, &block)
19
+ Supervision::Container::Pool.new_link(pooling_options(config, block: block, actors: klass))
20
+ end
21
+ end
22
+
23
+ module Supervision
24
+ class Container
25
+ extend Forwardable
26
+ def_delegators :"Celluloid::Supervision::Container::Pool", :pooling_options
27
+
28
+ def pool(klass, config = {}, &block)
29
+ _ = supervise(pooling_options(config, block: block, actors: klass))
30
+ _.actors.last
31
+ end
32
+
33
+ class Instance
34
+ attr_accessor :pool, :pool_size
35
+ end
36
+
37
+ class << self
38
+ # Register a pool of actors to be launched on group startup
39
+ def pool(klass, config, &block)
40
+ blocks << lambda do |container|
41
+ container.pool(klass, config, &block)
42
+ end
43
+ end
44
+ end
45
+
46
+ class Pool
47
+ include Behavior
48
+
49
+ class << self
50
+ def pooling_options(config = {}, mixins = {})
51
+ combined = { type: Celluloid::Supervision::Container::Pool }.merge(config).merge(mixins)
52
+ combined[:args] = [%i[block actors size args].each_with_object({}) do |p, e|
53
+ e[p] = combined.delete(p) if combined[p]
54
+ end]
55
+ combined
56
+ end
57
+ end
58
+
59
+ identifier! :size, :pool
60
+
61
+ configuration do
62
+ @supervisor = Container::Pool
63
+ @method = "pool_link"
64
+ @pool = true
65
+ @pool_size = @configuration[:size]
66
+ @configuration
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,23 @@
1
+ module Celluloid
2
+ module Supervision
3
+ class Container
4
+ class Tree
5
+ include Behavior
6
+
7
+ identifier! :supervises, :supervise
8
+
9
+ configuration do
10
+ if @configuration[:supervise].is_a? Array
11
+ @supervisor = @configuration.dup
12
+ @branch = @configuration.fetch(:branch, @configuration[:as])
13
+ @configuration.delete(Behavior.parameter(:supervise, @configuration))
14
+ elsif @configuration[:supervise].is_a?(Celluloid::Supervision::Configuration)
15
+ @configuration
16
+ else
17
+ raise ArgumentError, "No actors given to Tree to supervise."
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,89 @@
1
+ module Celluloid
2
+ module Supervision
3
+ class Container
4
+ module Behavior
5
+ @@injections = {} # Hash of Class => Hash of Injections
6
+ @@behaviors = {} # Hash of identifying symbol parameter => Class
7
+
8
+ module Error
9
+ class Mutant < Celluloid::Error; end
10
+ end
11
+
12
+ class << self
13
+ def included(klass)
14
+ klass.send :extend, ClassMethods
15
+ end
16
+
17
+ def injections
18
+ @@injections
19
+ end
20
+
21
+ def [](identifier)
22
+ @@behaviors[identifier]
23
+ end
24
+
25
+ def []=(identifier, behavior)
26
+ @@behaviors[identifier] = behavior
27
+ end
28
+
29
+ def parameter(identifier, options)
30
+ found = nil
31
+ p = Configuration.aliases.each_with_object([identifier]) { |(a, i), invoke| invoke << a if i == identifier; }
32
+ case p.count { |parameter| found = parameter; options.key?(parameter) }
33
+ when 1
34
+ found
35
+ when 0
36
+
37
+ else
38
+ raise Error::Mutant, "More than one kind of identifiable behavior parameter."
39
+ end
40
+ end
41
+
42
+ # Beware of order. There may be multiple behavior injections, but their order is not determined ( yet )
43
+ # Right now, something like a pool-coordinator-tree supervisor mutant are absolutely expected to crash.
44
+ # Therefore, sorry Professor X -- we kill every Mutant. On sight, no questions asked. Zero mutant love.
45
+ def configure(options)
46
+ behavior = nil
47
+ injection = nil
48
+ @@behaviors.map do |identifier, injector|
49
+ if identifier = parameter(identifier, options)
50
+ if behavior
51
+ raise Error::Mutant, "More than one type of behavior expected."
52
+ else
53
+ if @@injections[injector].include?(:configuration)
54
+ injection = @@injections[behavior = injector][:configuration]
55
+ options[:behavior] ||= behavior
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ options[:type] ||= behavior
62
+ injection || proc { @configuration }
63
+ end
64
+
65
+ module ClassMethods
66
+ def identifier!(identifier, *aliases)
67
+ Behavior[identifier] = self
68
+ Configuration.parameter! :plugins, identifier
69
+ aliases.each do |aliased|
70
+ Configuration.alias! aliased, identifier
71
+ end
72
+ Configuration.save_defaults
73
+ end
74
+
75
+ def behavior_injections
76
+ Behavior.injections[self] ||= {}
77
+ end
78
+
79
+ Configuration::INJECTIONS.each do |point|
80
+ define_method(point) do |&injector|
81
+ behavior_injections[point] = injector
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,8 @@
1
+ module Celluloid
2
+ module Supervision
3
+ class Container
4
+ class Injection
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,116 @@
1
+ module Celluloid
2
+ # Supervise collections of actors as a group
3
+ module Supervision
4
+ class Container
5
+ class Instance
6
+ attr_reader :name, :actor
7
+
8
+ # @option options [#call, Object] :args ([]) arguments array for the
9
+ # actor's constructor (lazy evaluation if it responds to #call)
10
+ def initialize(configuration = {})
11
+ @type = configuration.delete(:type)
12
+ @registry = configuration.delete(:registry)
13
+ @branch = configuration.delete(:branch) || :services
14
+ @configuration = configuration
15
+
16
+ # allows injections inside initialize, start, and restart
17
+ @injections = configuration.delete(:injections) || {}
18
+ invoke_injection(:before_initialize)
19
+
20
+ # Stringify keys :/
21
+ # de @configuration = configuration.each_with_object({}) { |(k,v), h| h[k.to_s] = v }
22
+
23
+ @name = @configuration[:as]
24
+ @block = @configuration[:block]
25
+ @args = prepare_args(@configuration[:args])
26
+ @method = @configuration[:method] || "new_link"
27
+ add_accessors
28
+ invoke_injection(:after_initialize)
29
+ start
30
+ end
31
+
32
+ def start
33
+ invoke_injection(:before_start)
34
+ @actor = @type.send(@method, *@args, &@block)
35
+ @registry.add(@name, @actor, @branch) if @name
36
+ invoke_injection(:after_start)
37
+ rescue Celluloid::TaskTimeout => ex
38
+ Internals::Logger.error("TaskTimeout at start of supervised instance of #{@type}")
39
+ raise ex
40
+ # TODO: Implement timeout/retry(?)
41
+ rescue => ex
42
+ Internals::Logger.error("Error ( #{ex.class} ) at start of supervised instance of #{@type}")
43
+ raise ex
44
+ end
45
+
46
+ def restart
47
+ # no need to reset @actor, as this is called in an `exclusive {}` block
48
+ # @actor = nil
49
+ # cleanup
50
+ invoke_injection(:before_restart)
51
+ start
52
+ invoke_injection(:after_restart)
53
+ end
54
+
55
+ def terminate
56
+ @actor.terminate if @actor
57
+ cleanup
58
+ rescue DeadActorError
59
+ end
60
+
61
+ def cleanup
62
+ @registry.delete(@name) if @name
63
+ end
64
+
65
+ private
66
+
67
+ def add_accessors
68
+ remove_accessors
69
+ if @configuration[:accessors].is_a? Array
70
+ # TODO: Decide which level to keep, and only keep that.
71
+ # Do we provide access by Celluloid.accessor
72
+ # Do we provide access by Celluloid.actor_system.accessor
73
+ @configuration[:accessors].each do |name|
74
+ Celluloid.instance_exec(@configuration[:as], name) do |actor, _where|
75
+ define_method(name) do
76
+ Celluloid.actor_system[actor]
77
+ end
78
+ end
79
+ Celluloid::Actor::System.instance_exec(@configuration[:as], name) do |actor, _where|
80
+ define_method(name) do
81
+ Celluloid.actor_system[actor]
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+
88
+ def remove_accessors
89
+ if @configuration[:accessors].is_a? Array
90
+ @configuration[:accessors].each do |name|
91
+ Celluloid.instance_eval do
92
+ remove_method(name) rescue nil # avoid warnings in tests
93
+ end
94
+ Celluloid::Actor::System.instance_eval do
95
+ remove_method(name) rescue nil # avoid warnings in tests
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ def invoke_injection(name)
102
+ return unless @injections
103
+ block = @injections[name]
104
+ instance_eval(&block) if block.is_a? Proc
105
+ end
106
+
107
+ # Executes args if it has the method #call, and converts the return
108
+ # value to an Array. Otherwise, it just converts it to an Array.
109
+ def prepare_args(args)
110
+ args = args.call if args.respond_to?(:call)
111
+ Array(args)
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,210 @@
1
+ module Celluloid
2
+ module Supervision
3
+ class Container
4
+ # Manages a fixed-size pool of actors
5
+ # Delegates work (i.e. methods) and supervises actors
6
+ # Don't use this class directly. Instead use MyKlass.pool
7
+ class Pool
8
+ include Celluloid
9
+
10
+ trap_exit :__crash_handler__
11
+ finalizer :__shutdown__
12
+
13
+ attr_reader :size, :actors
14
+
15
+ def initialize(options = {})
16
+ @idle = []
17
+ @busy = []
18
+ @klass = options[:actors]
19
+ @actors = Set.new
20
+ @mutex = Mutex.new
21
+
22
+ @size = options[:size] || [Celluloid.cores || 2, 2].max
23
+ @args = options[:args] ? Array(options[:args]) : []
24
+
25
+ # Do this last since it can suspend and/or crash
26
+ @idle = @size.times.map { __spawn_actor__ }
27
+ end
28
+
29
+ def __shutdown__
30
+ return unless defined?(@actors) && @actors
31
+ # TODO: these can be nil if initializer crashes
32
+ terminators = @actors.map do |actor|
33
+ begin
34
+ actor.future(:terminate)
35
+ rescue DeadActorError
36
+ end
37
+ end
38
+
39
+ terminators.compact.each { |terminator| terminator.value rescue nil }
40
+ end
41
+
42
+ def _send_(method, *args, &block)
43
+ actor = __provision_actor__
44
+ begin
45
+ actor._send_ method, *args, &block
46
+ rescue DeadActorError # if we get a dead actor out of the pool
47
+ wait :respawn_complete
48
+ actor = __provision_actor__
49
+ retry
50
+ rescue ::Exception => ex
51
+ abort ex
52
+ ensure
53
+ if actor.alive?
54
+ @idle << actor
55
+ @busy.delete actor
56
+
57
+ # Broadcast that actor is done processing and
58
+ # waiting idle
59
+ signal :actor_idle
60
+ end
61
+ end
62
+ end
63
+
64
+ def name
65
+ _send_ @mailbox, :name
66
+ end
67
+
68
+ def is_a?(klass)
69
+ _send_ :is_a?, klass
70
+ end
71
+
72
+ def kind_of?(klass)
73
+ _send_ :kind_of?, klass
74
+ end
75
+
76
+ def methods(include_ancestors = true)
77
+ _send_ :methods, include_ancestors
78
+ end
79
+
80
+ def to_s
81
+ _send_ :to_s
82
+ end
83
+
84
+ def inspect
85
+ _send_ :inspect
86
+ end
87
+
88
+ def size=(new_size)
89
+ new_size = [0, new_size].max
90
+ if new_size > size
91
+ delta = new_size - size
92
+ delta.times { @idle << __spawn_actor__ }
93
+ else
94
+ (size - new_size).times do
95
+ actor = __provision_actor__
96
+ unlink actor
97
+ @busy.delete actor
98
+ @actors.delete actor
99
+ actor.terminate
100
+ end
101
+ end
102
+ @size = new_size
103
+ end
104
+
105
+ def busy_size
106
+ @mutex.synchronize { @busy.length }
107
+ end
108
+
109
+ def idle_size
110
+ @mutex.synchronize { @idle.length }
111
+ end
112
+
113
+ def __idle?(actor)
114
+ @mutex.synchronize { @idle.include? actor }
115
+ end
116
+
117
+ def __busy?(actor)
118
+ @mutex.synchronize { @busy.include? actor }
119
+ end
120
+
121
+ def __busy
122
+ @mutex.synchronize { @busy }
123
+ end
124
+
125
+ def __idle
126
+ @mutex.synchronize { @idle }
127
+ end
128
+
129
+ def __state(actor)
130
+ return :busy if __busy?(actor)
131
+ return :idle if __idle?(actor)
132
+ :missing
133
+ end
134
+
135
+ # Instantiate an actor, add it to the actor Set, and return it
136
+ def __spawn_actor__
137
+ actor = @klass.new_link(*@args)
138
+ @mutex.synchronize { @actors.add(actor) }
139
+ @actors.add(actor)
140
+ actor
141
+ end
142
+
143
+ # Provision a new actor ( take it out of idle, move it into busy, and avail it )
144
+ def __provision_actor__
145
+ Task.current.guard_warnings = true
146
+ @mutex.synchronize do
147
+ while @idle.empty?
148
+ # Wait for responses from one of the busy actors
149
+ response = exclusive { receive { |msg| msg.is_a?(Internals::Response) } }
150
+ Thread.current[:celluloid_actor].handle_message(response)
151
+ end
152
+
153
+ actor = @idle.shift
154
+ @busy << actor
155
+ actor
156
+ end
157
+ end
158
+
159
+ # Spawn a new worker for every crashed one
160
+ def __crash_handler__(actor, reason)
161
+ @busy.delete actor
162
+ @idle.delete actor
163
+ @actors.delete actor
164
+ return unless reason
165
+ @idle << __spawn_actor__
166
+ signal :respawn_complete
167
+ end
168
+
169
+ def respond_to?(meth, include_private = false)
170
+ # NOTE: use method() here since this class
171
+ # shouldn't be used directly, and method() is less
172
+ # likely to be "reimplemented" inconsistently
173
+ # with other Object.*method* methods.
174
+
175
+ found = method(meth)
176
+ if include_private
177
+ found ? true : false
178
+ else
179
+ if found.is_a?(UnboundMethod)
180
+ found.owner.public_instance_methods.include?(meth) ||
181
+ found.owner.protected_instance_methods.include?(meth)
182
+ else
183
+ found.receiver.public_methods.include?(meth) ||
184
+ found.receiver.protected_methods.include?(meth)
185
+ end
186
+ end
187
+ rescue NameError
188
+ false
189
+ end
190
+
191
+ def method_missing(method, *args, &block)
192
+ if respond_to?(method)
193
+ _send_ method, *args, &block
194
+ else
195
+ super
196
+ end
197
+ end
198
+
199
+ # Since Pool allocates worker objects only just before calling them,
200
+ # we can still help Celluloid::Call detect passing invalid parameters to
201
+ # async methods by checking for those methods on the worker class
202
+ def method(meth)
203
+ super
204
+ rescue NameError
205
+ @klass.instance_method(meth.to_sym)
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,144 @@
1
+ module Celluloid
2
+ # Supervise actor instances in a container.
3
+ module Supervision
4
+ class Container
5
+ include Celluloid
6
+
7
+ trap_exit :restart_actor
8
+
9
+ class << self
10
+ def define(options)
11
+ Configuration.define(top(options))
12
+ end
13
+
14
+ def deploy(options)
15
+ Configuration.deploy(top(options))
16
+ end
17
+
18
+ def top(options)
19
+ {
20
+ as: options.delete(:as),
21
+ type: (options.delete(:type) || self),
22
+ branch: (options.delete(:branch) || :services),
23
+ supervise: options.delete(:supervise) || []
24
+ }
25
+ end
26
+
27
+ # Actors or sub-applications to be supervised
28
+ def blocks
29
+ @blocks ||= []
30
+ end
31
+
32
+ # Start this application (and watch it with a supervisor)
33
+ def run!(options = {})
34
+ container = new(options) do |g|
35
+ blocks.each do |block|
36
+ block.call(g)
37
+ end
38
+ end
39
+ container
40
+ end
41
+
42
+ # Run the application in the foreground with a simple watchdog
43
+ def run(options = {})
44
+ loop do
45
+ supervisor = run!(options)
46
+
47
+ # Take five, toplevel supervisor
48
+ sleep 5 while supervisor.alive? # Why 5?
49
+
50
+ Internals::Logger.error "!!! Celluloid::Supervision::Container #{self} crashed. Restarting..."
51
+ end
52
+ end
53
+ end
54
+
55
+ finalizer :finalize
56
+
57
+ attr_accessor :registry
58
+
59
+ # Start the container.
60
+ def initialize(options = {})
61
+ options = { registry: options } if options.is_a? Internals::Registry
62
+ @state = :initializing
63
+ @actors = [] # instances in the container
64
+ @registry = options.delete(:registry) || Celluloid.actor_system.registry
65
+ @branch = options.delete(:branch) || :services
66
+ yield current_actor if block_given?
67
+ end
68
+
69
+ execute_block_on_receiver :initialize, :supervise, :supervise_as
70
+
71
+ def add(configuration)
72
+ Configuration.valid?(configuration, true)
73
+ @actors << Instance.new(configuration.merge(registry: @registry, branch: @branch))
74
+ @state = :running
75
+ add_accessors configuration
76
+ Actor.current
77
+ end
78
+
79
+ def add_accessors(configuration)
80
+ if configuration[:as]
81
+ unless methods.include? configuration[:as]
82
+ self.class.instance_eval do
83
+ define_method(configuration[:as]) do
84
+ @registry[configuration[:as]]
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def remove_accessors; end
92
+
93
+ def remove(actor)
94
+ actor = Celluloid::Actor[actor] if actor.is_a? Symbol
95
+ instance = find(actor)
96
+ instance.terminate if instance
97
+ end
98
+
99
+ def actors
100
+ @actors.map(&:actor)
101
+ end
102
+
103
+ def find(actor)
104
+ @actors.find do |instance|
105
+ instance.actor == actor
106
+ end
107
+ end
108
+
109
+ def [](actor_name)
110
+ @registry[actor_name]
111
+ end
112
+
113
+ # Restart a crashed actor
114
+ def restart_actor(actor, reason)
115
+ return if @state == :shutdown
116
+ instance = find(actor)
117
+ raise "a container instance went missing. This shouldn't be!" unless instance
118
+
119
+ if reason
120
+ exclusive { instance.restart }
121
+ else
122
+ instance.cleanup
123
+ @actors.delete(instance)
124
+ end
125
+ end
126
+
127
+ def shutdown
128
+ @state = :shutdown
129
+ finalize
130
+ end
131
+
132
+ private
133
+
134
+ def finalize
135
+ if @actors
136
+ @actors.reverse_each do |instance|
137
+ instance.terminate
138
+ @actors.delete(instance)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,27 @@
1
+ module Celluloid
2
+ module Supervision
3
+ module Service
4
+ class Root < Container
5
+ class << self
6
+ def define
7
+ super({
8
+ supervise: Celluloid.actor_system.root_configuration,
9
+ as: :root_supervisor,
10
+ accessors: [:root],
11
+ branch: :root,
12
+ type: self
13
+ })
14
+ end
15
+
16
+ def deploy(instances)
17
+ super(supervise: instances, branch: :root, as: :root, type: self)
18
+ end
19
+ end
20
+ def provider
21
+ Celluloid.root_services
22
+ end
23
+ end
24
+ class Public < Container; end
25
+ end
26
+ end
27
+ end