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,51 @@
1
+ require "set"
2
+ require "forwardable"
3
+
4
+ module Celluloid
5
+ module Internals
6
+ if RUBY_PLATFORM == "java"
7
+ require "jruby/synchronized"
8
+
9
+ class TaskSet
10
+ extend Forwardable
11
+ include JRuby::Synchronized
12
+
13
+ def_delegators :@tasks, :<<, :delete, :first, :empty?, :to_a
14
+
15
+ def initialize
16
+ @tasks = Set.new
17
+ end
18
+ end
19
+ elsif RUBY_ENGINE == "rbx"
20
+ class TaskSet
21
+ def initialize
22
+ @tasks = Set.new
23
+ end
24
+
25
+ def <<(task)
26
+ Rubinius.synchronize(self) { @tasks << task }
27
+ end
28
+
29
+ def delete(task)
30
+ Rubinius.synchronize(self) { @tasks.delete task }
31
+ end
32
+
33
+ def first
34
+ Rubinius.synchronize(self) { @tasks.first }
35
+ end
36
+
37
+ def empty?
38
+ Rubinius.synchronize(self) { @tasks.empty? }
39
+ end
40
+
41
+ def to_a
42
+ Rubinius.synchronize(self) { @tasks.to_a }
43
+ end
44
+ end
45
+ else
46
+ # Assume we're on MRI, where we have the GIL. But what about IronRuby?
47
+ # Or MacRuby. Do people care? This will break Celluloid::Internals::StackDumps
48
+ TaskSet = Set
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,52 @@
1
+ module Celluloid
2
+ module Internals
3
+ # An abstraction around threads from the InternalPool which ensures we don't
4
+ # accidentally do things to threads which have been returned to the pool,
5
+ # such as, say, killing them
6
+ class ThreadHandle
7
+ def initialize(actor_system, role = nil)
8
+ @mutex = Mutex.new
9
+ @join = ConditionVariable.new
10
+
11
+ @thread = actor_system.get_thread do
12
+ Thread.current.role = role
13
+ begin
14
+ yield
15
+ ensure
16
+ @mutex.synchronize do
17
+ @thread = nil
18
+ @join.broadcast
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ # Is the thread running?
25
+ def alive?
26
+ @mutex.synchronize { @thread && @thread.alive? }
27
+ end
28
+
29
+ # Forcibly kill the thread
30
+ def kill
31
+ @mutex.synchronize { @thread && @thread.kill }
32
+ self
33
+ end
34
+
35
+ # Join to a running thread, blocking until it terminates
36
+ def join(limit = nil)
37
+ raise ThreadError, "Target thread must not be current thread" if @thread == Thread.current
38
+ @mutex.synchronize { @join.wait(@mutex, limit) if @thread }
39
+ self
40
+ end
41
+
42
+ # Obtain the backtrace for this thread
43
+ def backtrace
44
+ @thread.backtrace
45
+ rescue NoMethodError
46
+ # undefined method `backtrace' for nil:NilClass
47
+ # Swallow this in case this ThreadHandle was terminated and @thread was
48
+ # set to nil
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,40 @@
1
+ require "securerandom"
2
+
3
+ module Celluloid
4
+ module Internals
5
+ # Clearly Ruby doesn't have enough UUID libraries
6
+ # This one aims to be fast and simple with good support for multiple threads
7
+ # If there's a better UUID library I can use with similar multithreaded
8
+ # performance, I certainly wouldn't mind using a gem for this!
9
+ module UUID
10
+ values = SecureRandom.hex(9).match(/(.{8})(.{4})(.{3})(.{3})/)
11
+ PREFIX = "#{values[1]}-#{values[2]}-4#{values[3]}-8#{values[4]}".freeze
12
+ BLOCK_SIZE = 0x10000
13
+
14
+ @counter = 0
15
+ @counter_mutex = Mutex.new
16
+
17
+ def self.generate
18
+ thread = Thread.current
19
+
20
+ unless thread.uuid_limit
21
+ @counter_mutex.synchronize do
22
+ block_base = @counter
23
+ @counter += BLOCK_SIZE
24
+ thread.uuid_counter = block_base
25
+ thread.uuid_limit = @counter - 1
26
+ end
27
+ end
28
+
29
+ counter = thread.uuid_counter
30
+ if thread.uuid_counter >= thread.uuid_limit
31
+ thread.uuid_counter = thread.uuid_limit = nil
32
+ else
33
+ thread.uuid_counter += 1
34
+ end
35
+
36
+ "#{PREFIX}-#{format('%012x', counter)}".freeze
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,21 @@
1
+ module Celluloid
2
+ # Wraps all events and context for a single incident.
3
+ class Incident
4
+ attr_accessor :pid
5
+ attr_accessor :events, :triggering_event
6
+
7
+ def initialize(events, triggering_event = nil)
8
+ @events = events
9
+ @triggering_event = triggering_event
10
+ @pid = $PROCESS_ID
11
+ end
12
+
13
+ # Merge two incidents together. This may be useful if two incidents occur at the same time.
14
+ def merge(*other_incidents)
15
+ merged_events = other_incidents.flatten.inject(events) do |events, incident|
16
+ events += incident.events
17
+ end
18
+ Incident.new(merged_events.sort, triggering_event)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,147 @@
1
+ require "logger"
2
+
3
+ module Celluloid
4
+ # A logger that holds all messages in circular buffers, then flushes the buffers
5
+ # when an event occurs at a configurable severity threshold.
6
+ #
7
+ # Unlike ruby's Logger, this class only supports a single progname.
8
+ class IncidentLogger
9
+ module Severity
10
+ include ::Logger::Severity
11
+
12
+ TRACE = -1
13
+
14
+ def severity_to_string(severity)
15
+ case severity
16
+ when TRACE then "TRACE"
17
+ when DEBUG then "DEBUG"
18
+ when INFO then "INFO"
19
+ when WARN then "WARN"
20
+ when ERROR then "ERROR"
21
+ when FATAL then "FATAL"
22
+ when UNKNOWN then "UNKNOWN"
23
+ end
24
+ end
25
+ end
26
+ include Severity
27
+
28
+ # The progname (facility) for this instance.
29
+ attr_accessor :progname
30
+
31
+ # The logging level. Messages below this severity will not be logged at all.
32
+ attr_accessor :level
33
+
34
+ # The incident threshold. Messages at or above this severity will generate an
35
+ # incident and be published to incident reporters.
36
+ attr_accessor :threshold
37
+
38
+ # The buffer size limit. Each log level will retain this number of messages
39
+ # at maximum.
40
+ attr_accessor :sizelimit
41
+
42
+ attr_accessor :buffers
43
+
44
+ # Create a new IncidentLogger.
45
+ def initialize(progname = nil, options = {})
46
+ @progname = progname || "default"
47
+ @level = options[:level] || DEBUG
48
+ @threshold = options[:threshold] || ERROR
49
+ @sizelimit = options[:sizelimit] || 100
50
+
51
+ @buffer_mutex = Mutex.new
52
+ @buffers = Hash.new do |progname_hash, pn|
53
+ @buffer_mutex.synchronize do
54
+ progname_hash[pn] = Hash.new do |severity_hash, severity|
55
+ severity_hash[severity] = RingBuffer.new(@sizelimit)
56
+ end
57
+ end
58
+ end
59
+
60
+ # When the IncidentLogger itself encounters an error, it falls back to logging to stderr
61
+ @fallback_logger = ::Logger.new(STDERR)
62
+ @fallback_logger.progname = "FALLBACK"
63
+ end
64
+
65
+ # add an event.
66
+ def add(severity, message = nil, progname = nil, &block)
67
+ progname ||= @progname
68
+ severity ||= UNKNOWN
69
+
70
+ return event.id if severity < @level
71
+
72
+ if message.nil? && !block_given?
73
+ message = progname
74
+ progname = @progname
75
+ end
76
+
77
+ event = LogEvent.new(severity, message, progname, &block)
78
+
79
+ @buffers[progname][severity] << event
80
+
81
+ if severity >= @threshold
82
+ begin
83
+ Celluloid::Notifications.notifier.async.publish(incident_topic, create_incident(event))
84
+ rescue => ex
85
+ @fallback_logger.error(ex)
86
+ end
87
+ end
88
+ event.id
89
+ end
90
+ alias log add
91
+
92
+ # See docs for Logger#info
93
+ def trace(progname = nil, &block)
94
+ add(TRACE, nil, progname, &block)
95
+ end
96
+
97
+ def debug(progname = nil, &block)
98
+ add(DEBUG, nil, progname, &block)
99
+ end
100
+
101
+ def info(progname = nil, &block)
102
+ add(INFO, nil, progname, &block)
103
+ end
104
+
105
+ def warn(progname = nil, &block)
106
+ add(WARN, nil, progname, &block)
107
+ end
108
+
109
+ def error(progname = nil, &block)
110
+ add(ERROR, nil, progname, &block)
111
+ end
112
+
113
+ def fatal(progname = nil, &block)
114
+ add(FATAL, nil, progname, &block)
115
+ end
116
+
117
+ def unknown(progname = nil, &block)
118
+ add(UNKNOWN, nil, progname, &block)
119
+ end
120
+
121
+ def flush
122
+ messages = []
123
+ @buffer_mutex.synchronize do
124
+ @buffers.each do |_progname, severities|
125
+ severities.each do |_severity, buffer|
126
+ messages += buffer.flush
127
+ end
128
+ end
129
+ end
130
+ messages.sort
131
+ end
132
+
133
+ def clear
134
+ @buffer_mutex.synchronize do
135
+ @buffers.each(&:clear)
136
+ end
137
+ end
138
+
139
+ def create_incident(event = nil)
140
+ Incident.new(flush, event)
141
+ end
142
+
143
+ def incident_topic
144
+ "log.incident.#{@progname}"
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,49 @@
1
+ require "logger"
2
+
3
+ module Celluloid
4
+ # Subscribes to log incident topics to report on them.
5
+ class IncidentReporter
6
+ include Celluloid
7
+ include Celluloid::Notifications
8
+
9
+ # get the time from the event
10
+ class Formatter < ::Logger::Formatter
11
+ def call(severity, _time, progname, msg)
12
+ super(severity, msg.time, progname, msg.message)
13
+ end
14
+ end
15
+
16
+ def initialize(*args)
17
+ subscribe(/log\.incident/, :report)
18
+ @logger = ::Logger.new(*args)
19
+ @logger.formatter = Formatter.new
20
+ @silenced = false
21
+ end
22
+
23
+ def report(_topic, incident)
24
+ return if @silenced
25
+
26
+ header = "INCIDENT"
27
+ header << " AT #{incident.triggering_event.time}" if incident.triggering_event
28
+ @logger << header
29
+ @logger << "\n"
30
+ @logger << "====================\n"
31
+ incident.events.each do |event|
32
+ @logger.add(event.severity, event, event.progname)
33
+ end
34
+ @logger << "====================\n"
35
+ end
36
+
37
+ def silence
38
+ @silenced = true
39
+ end
40
+
41
+ def unsilence
42
+ @silenced = false
43
+ end
44
+
45
+ def silenced?
46
+ @silenced
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ module Celluloid
2
+ # Wraps a single log event.
3
+ class LogEvent
4
+ attr_accessor :id, :severity, :message, :progname, :time
5
+
6
+ def initialize(severity, message, progname, time = Time.now, &_block)
7
+ # This id should be ordered. For now relies on Celluloid::UUID to be ordered.
8
+ # May want to use a generation/counter strategy for independence of uuid.
9
+ @id = Internals::UUID.generate
10
+ @severity = severity
11
+ @message = block_given? ? yield : message
12
+ @progname = progname
13
+ @time = time
14
+ end
15
+
16
+ def <=>(other)
17
+ @id <=> other.id
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,64 @@
1
+ module Celluloid
2
+ class RingBuffer
3
+ def initialize(size)
4
+ @size = size
5
+ @start = 0
6
+ @count = 0
7
+ @buffer = Array.new(size)
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def full?
12
+ @count == @size
13
+ end
14
+
15
+ def empty?
16
+ @count == 0
17
+ end
18
+
19
+ def push(value)
20
+ @mutex.synchronize do
21
+ stop = (@start + @count) % @size
22
+ @buffer[stop] = value
23
+ if full?
24
+ @start = (@start + 1) % @size
25
+ else
26
+ @count += 1
27
+ end
28
+ value
29
+ end
30
+ end
31
+ alias << push
32
+
33
+ def shift
34
+ @mutex.synchronize do
35
+ remove_element
36
+ end
37
+ end
38
+
39
+ def flush
40
+ values = []
41
+ @mutex.synchronize do
42
+ values << remove_element until empty?
43
+ end
44
+ values
45
+ end
46
+
47
+ def clear
48
+ @buffer = Array.new(@size)
49
+ @start = 0
50
+ @count = 0
51
+ end
52
+
53
+ private
54
+
55
+ def remove_element
56
+ return nil if empty?
57
+ value = @buffer[@start]
58
+ @buffer[@start] = nil
59
+ @start = (@start + 1) % @size
60
+ @count -= 1
61
+ value
62
+ end
63
+ end
64
+ end
@@ -26,13 +26,17 @@ module Celluloid
26
26
  @messages << message
27
27
  end
28
28
  ensure
29
- @mutex.unlock rescue nil
29
+ begin
30
+ @mutex.unlock
31
+ rescue
32
+ nil
33
+ end
30
34
  end
31
35
  begin
32
36
  current_actor = Thread.current[:celluloid_actor]
33
37
  @reactor.wakeup unless current_actor && current_actor.mailbox == self
34
- rescue
35
- Internals::Logger.crash "reactor crashed", $ERROR_INFO
38
+ rescue => ex
39
+ Internals::Logger.crash "reactor crashed", ex
36
40
  dead_letter(message)
37
41
  end
38
42
  nil
@@ -50,7 +54,7 @@ module Celluloid
50
54
  @reactor.run_once(timeout)
51
55
 
52
56
  # No message was received:
53
- return nil
57
+ nil
54
58
  end
55
59
 
56
60
  # Obtain the next message from the mailbox that matches the given block
@@ -59,7 +63,11 @@ module Celluloid
59
63
  begin
60
64
  super(&block)
61
65
  ensure
62
- @mutex.unlock rescue nil
66
+ begin
67
+ @mutex.unlock
68
+ rescue
69
+ nil
70
+ end
63
71
  end
64
72
  end
65
73
 
@@ -1,5 +1,3 @@
1
- require "thread"
2
-
3
1
  module Celluloid
4
2
  class MailboxDead < Celluloid::Error; end # you can't receive from the dead
5
3
  class MailboxShutdown < Celluloid::Error; end # raised if the mailbox can no longer be used
@@ -41,7 +39,11 @@ module Celluloid
41
39
  @condition.signal
42
40
  nil
43
41
  ensure
44
- @mutex.unlock rescue nil
42
+ begin
43
+ @mutex.unlock
44
+ rescue
45
+ nil
46
+ end
45
47
  end
46
48
  end
47
49
 
@@ -52,7 +54,7 @@ module Celluloid
52
54
 
53
55
  @mutex.lock
54
56
  begin
55
- fail MailboxDead, "attempted to receive from a dead mailbox" if @dead
57
+ raise MailboxDead, "attempted to receive from a dead mailbox" if @dead
56
58
 
57
59
  message = nil
58
60
  Timers::Wait.for(timeout) do |remaining|
@@ -63,7 +65,11 @@ module Celluloid
63
65
  @condition.wait(@mutex, remaining)
64
66
  end
65
67
  ensure
66
- @mutex.unlock rescue nil
68
+ begin
69
+ @mutex.unlock
70
+ rescue
71
+ nil
72
+ end
67
73
  end
68
74
 
69
75
  message
@@ -73,17 +79,17 @@ module Celluloid
73
79
  # timeout is exceeded, raise a TaskTimeout.
74
80
  def receive(timeout = nil, &block)
75
81
  message = nil
76
- Timers::Wait.for(timeout) do |remaining|
82
+ Timers::Wait.for(timeout) do |_remaining|
77
83
  message = check(timeout, &block)
78
84
  break if message
79
85
  end
80
86
  return message if message
81
- fail TaskTimeout.new("receive timeout exceeded")
87
+ raise TaskTimeout, "receive timeout exceeded"
82
88
  end
83
89
 
84
90
  # Shut down this mailbox and clean up its contents
85
91
  def shutdown
86
- fail MailboxDead, "mailbox already shutdown" if @dead
92
+ raise MailboxDead, "mailbox already shutdown" if @dead
87
93
 
88
94
  @mutex.lock
89
95
  begin
@@ -92,7 +98,11 @@ module Celluloid
92
98
  @messages = []
93
99
  @dead = true
94
100
  ensure
95
- @mutex.unlock rescue nil
101
+ begin
102
+ @mutex.unlock
103
+ rescue
104
+ nil
105
+ end
96
106
  end
97
107
 
98
108
  messages.each do |msg|
@@ -147,7 +157,10 @@ module Celluloid
147
157
  end
148
158
 
149
159
  def dead_letter(message)
160
+ # !!! DO NOT INTRODUCE ADDITIONAL GLOBAL VARIABLES !!!
161
+ # rubocop:disable Style/GlobalVars
150
162
  Internals::Logger.debug "Discarded message (mailbox is dead): #{message}" if $CELLULOID_DEBUG
163
+ # rubocop:enable Style/GlobalVars
151
164
  end
152
165
 
153
166
  def mailbox_full
@@ -0,0 +1,95 @@
1
+ module Celluloid
2
+ module Notifications
3
+ def self.notifier
4
+ Actor[:notifications_fanout] || raise(DeadActorError, "notifications fanout actor not running")
5
+ end
6
+
7
+ def publish(pattern, *args)
8
+ Celluloid::Notifications.notifier.publish(pattern, *args)
9
+ rescue DeadActorError
10
+ # Bad shutdown logic. Oh well....
11
+ # TODO: needs a tests
12
+ end
13
+
14
+ module_function :publish
15
+
16
+ def subscribe(pattern, method)
17
+ Celluloid::Notifications.notifier.subscribe(Actor.current, pattern, method)
18
+ end
19
+
20
+ def unsubscribe(*args)
21
+ Celluloid::Notifications.notifier.unsubscribe(*args)
22
+ end
23
+
24
+ class Fanout
25
+ include Celluloid
26
+ trap_exit :prune
27
+
28
+ def initialize
29
+ @subscribers = []
30
+ @listeners_for = {}
31
+ end
32
+
33
+ def subscribe(actor, pattern, method)
34
+ subscriber = Subscriber.new(actor, pattern, method).tap do |s|
35
+ @subscribers << s
36
+ end
37
+ link actor
38
+ @listeners_for.clear
39
+ subscriber
40
+ end
41
+
42
+ def unsubscribe(subscriber)
43
+ @subscribers.reject! { |s| s.matches?(subscriber) }
44
+ @listeners_for.clear
45
+ end
46
+
47
+ def publish(pattern, *args)
48
+ listeners_for(pattern).each { |s| s.publish(pattern, *args) }
49
+ end
50
+
51
+ def listeners_for(pattern)
52
+ @listeners_for[pattern] ||= @subscribers.select { |s| s.subscribed_to?(pattern) }
53
+ end
54
+
55
+ def listening?(pattern)
56
+ listeners_for(pattern).any?
57
+ end
58
+
59
+ def prune(actor, _reason = nil)
60
+ @subscribers.reject! { |s| s.actor == actor }
61
+ @listeners_for.clear
62
+ end
63
+ end
64
+
65
+ class Subscriber
66
+ attr_accessor :actor, :pattern, :method
67
+
68
+ def initialize(actor, pattern, method)
69
+ @actor = actor
70
+ @pattern = pattern
71
+ @method = method
72
+ end
73
+
74
+ def publish(pattern, *args)
75
+ actor.async method, pattern, *args
76
+ rescue DeadActorError
77
+ # TODO: needs a tests
78
+ # Bad shutdown logic. Oh well....
79
+ end
80
+
81
+ def subscribed_to?(pattern)
82
+ !pattern || @pattern === pattern.to_s || @pattern === pattern
83
+ end
84
+
85
+ def matches?(subscriber_or_pattern)
86
+ self === subscriber_or_pattern ||
87
+ @pattern && @pattern === subscriber_or_pattern
88
+ end
89
+ end
90
+ end
91
+
92
+ def self.publish(*args)
93
+ Notifications.publish(*args)
94
+ end
95
+ end
@@ -0,0 +1,6 @@
1
+ require "celluloid" unless defined? Celluloid
2
+
3
+ require "celluloid/supervision"
4
+
5
+ require "celluloid/supervision/container/pool"
6
+ require "celluloid/supervision/container/behavior/pool"