celluloid 0.17.2 → 0.18.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +340 -75
  3. data/CONDUCT.md +13 -0
  4. data/CONTRIBUTING.md +39 -0
  5. data/LICENSE.txt +1 -1
  6. data/README.md +54 -150
  7. data/REFACTOR.md +1 -0
  8. data/architecture.md +120 -0
  9. data/examples/basic_usage.rb +1 -1
  10. data/examples/configurations.rb +78 -0
  11. data/examples/futures.rb +1 -1
  12. data/examples/ring.rb +5 -4
  13. data/examples/simple_pmap.rb +1 -1
  14. data/examples/stack.rb +2 -2
  15. data/examples/supervisors_and_registry.rb +82 -0
  16. data/examples/timers.rb +2 -2
  17. data/lib/celluloid.rb +78 -65
  18. data/lib/celluloid/actor.rb +27 -17
  19. data/lib/celluloid/actor/system.rb +13 -29
  20. data/lib/celluloid/autostart.rb +6 -1
  21. data/lib/celluloid/call/async.rb +2 -0
  22. data/lib/celluloid/call/sync.rb +10 -3
  23. data/lib/celluloid/calls.rb +13 -13
  24. data/lib/celluloid/cell.rb +5 -9
  25. data/lib/celluloid/condition.rb +3 -3
  26. data/lib/celluloid/core_ext.rb +0 -2
  27. data/lib/celluloid/debug.rb +3 -0
  28. data/lib/celluloid/exceptions.rb +2 -2
  29. data/lib/celluloid/future.rb +8 -10
  30. data/lib/celluloid/group.rb +16 -6
  31. data/lib/celluloid/group/pool.rb +1 -3
  32. data/lib/celluloid/group/spawner.rb +2 -6
  33. data/lib/celluloid/internals/call_chain.rb +15 -0
  34. data/lib/celluloid/internals/cpu_counter.rb +62 -0
  35. data/lib/celluloid/internals/handlers.rb +42 -0
  36. data/lib/celluloid/internals/links.rb +38 -0
  37. data/lib/celluloid/internals/logger.rb +104 -0
  38. data/lib/celluloid/internals/method.rb +34 -0
  39. data/lib/celluloid/internals/properties.rb +32 -0
  40. data/lib/celluloid/internals/receivers.rb +64 -0
  41. data/lib/celluloid/internals/registry.rb +102 -0
  42. data/lib/celluloid/internals/responses.rb +46 -0
  43. data/lib/celluloid/internals/signals.rb +24 -0
  44. data/lib/celluloid/internals/stack.rb +74 -0
  45. data/lib/celluloid/internals/stack/dump.rb +12 -0
  46. data/lib/celluloid/internals/stack/states.rb +72 -0
  47. data/lib/celluloid/internals/stack/summary.rb +12 -0
  48. data/lib/celluloid/internals/task_set.rb +51 -0
  49. data/lib/celluloid/internals/thread_handle.rb +52 -0
  50. data/lib/celluloid/internals/uuid.rb +40 -0
  51. data/lib/celluloid/logging/incident.rb +21 -0
  52. data/lib/celluloid/logging/incident_logger.rb +147 -0
  53. data/lib/celluloid/logging/incident_reporter.rb +49 -0
  54. data/lib/celluloid/logging/log_event.rb +20 -0
  55. data/lib/celluloid/logging/ring_buffer.rb +64 -0
  56. data/lib/celluloid/mailbox.rb +22 -9
  57. data/lib/celluloid/mailbox/evented.rb +13 -7
  58. data/lib/celluloid/notifications.rb +95 -0
  59. data/lib/celluloid/pool.rb +6 -0
  60. data/lib/celluloid/probe.rb +81 -0
  61. data/lib/celluloid/proxy/abstract.rb +38 -7
  62. data/lib/celluloid/proxy/actor.rb +0 -5
  63. data/lib/celluloid/proxy/async.rb +2 -18
  64. data/lib/celluloid/proxy/block.rb +2 -1
  65. data/lib/celluloid/proxy/cell.rb +1 -7
  66. data/lib/celluloid/proxy/future.rb +3 -21
  67. data/lib/celluloid/proxy/sync.rb +2 -20
  68. data/lib/celluloid/rspec.rb +22 -34
  69. data/lib/celluloid/supervision.rb +17 -0
  70. data/lib/celluloid/supervision/configuration.rb +169 -0
  71. data/lib/celluloid/supervision/configuration/injections.rb +8 -0
  72. data/lib/celluloid/supervision/configuration/instance.rb +113 -0
  73. data/lib/celluloid/supervision/constants.rb +123 -0
  74. data/lib/celluloid/supervision/container.rb +144 -0
  75. data/lib/celluloid/supervision/container/behavior.rb +89 -0
  76. data/lib/celluloid/supervision/container/behavior/pool.rb +71 -0
  77. data/lib/celluloid/supervision/container/behavior/tree.rb +23 -0
  78. data/lib/celluloid/supervision/container/injections.rb +8 -0
  79. data/lib/celluloid/supervision/container/instance.rb +116 -0
  80. data/lib/celluloid/supervision/container/pool.rb +210 -0
  81. data/lib/celluloid/supervision/service.rb +27 -0
  82. data/lib/celluloid/supervision/supervise.rb +34 -0
  83. data/lib/celluloid/supervision/validation.rb +40 -0
  84. data/lib/celluloid/supervision/version.rb +5 -0
  85. data/lib/celluloid/system_events.rb +10 -3
  86. data/lib/celluloid/task.rb +25 -12
  87. data/lib/celluloid/task/fibered.rb +6 -2
  88. data/lib/celluloid/task/threaded.rb +3 -3
  89. data/lib/celluloid/test.rb +5 -2
  90. data/lib/celluloid/thread.rb +0 -2
  91. data/lib/celluloid/version.rb +1 -1
  92. data/spec/celluloid/block_spec.rb +29 -32
  93. data/spec/celluloid/calls_spec.rb +5 -15
  94. data/spec/celluloid/future_spec.rb +7 -1
  95. data/spec/celluloid/internals/cpu_counter_spec.rb +129 -0
  96. data/spec/celluloid/internals/links_spec.rb +43 -0
  97. data/spec/celluloid/internals/properties_spec.rb +40 -0
  98. data/spec/celluloid/internals/registry_spec.rb +62 -0
  99. data/spec/celluloid/internals/stack/dump_spec.rb +4 -0
  100. data/spec/celluloid/internals/stack/summary_spec.rb +4 -0
  101. data/spec/celluloid/internals/thread_handle_spec.rb +60 -0
  102. data/spec/celluloid/internals/uuid_spec.rb +9 -0
  103. data/spec/celluloid/logging/ring_buffer_spec.rb +36 -0
  104. data/spec/celluloid/mailbox/evented_spec.rb +21 -19
  105. data/spec/celluloid/misc/leak_spec.rb +3 -4
  106. data/spec/celluloid/notifications_spec.rb +140 -0
  107. data/spec/celluloid/probe_spec.rb +102 -0
  108. data/spec/celluloid/proxy_spec.rb +33 -0
  109. data/spec/celluloid/supervision/behavior_spec.rb +74 -0
  110. data/spec/celluloid/supervision/configuration_spec.rb +181 -0
  111. data/spec/celluloid/supervision/container_spec.rb +72 -0
  112. data/spec/celluloid/supervision/instance_spec.rb +13 -0
  113. data/spec/celluloid/supervision/root_spec.rb +28 -0
  114. data/spec/celluloid/supervision/supervisor_spec.rb +93 -0
  115. data/spec/celluloid/task/fibered_spec.rb +1 -3
  116. data/spec/celluloid/task/threaded_spec.rb +1 -3
  117. data/spec/shared/actor_examples.rb +65 -29
  118. data/spec/shared/group_examples.rb +2 -2
  119. data/spec/shared/mailbox_examples.rb +1 -1
  120. data/spec/shared/stack_examples.rb +87 -0
  121. data/spec/shared/task_examples.rb +2 -3
  122. data/spec/spec_helper.rb +2 -4
  123. data/spec/support/configure_rspec.rb +3 -4
  124. data/spec/support/coverage.rb +2 -4
  125. data/spec/support/crash_checking.rb +2 -2
  126. data/spec/support/examples/actor_class.rb +3 -8
  127. data/spec/support/examples/call_class.rb +2 -2
  128. data/spec/support/examples/container_class.rb +35 -0
  129. data/spec/support/examples/evented_mailbox_class.rb +1 -2
  130. data/spec/support/examples/stack_classes.rb +58 -0
  131. data/spec/support/examples/stack_methods.rb +23 -0
  132. data/spec/support/examples/subordinate_class.rb +19 -0
  133. data/spec/support/logging.rb +3 -34
  134. data/spec/support/loose_threads.rb +3 -24
  135. data/spec/support/reset_class_variables.rb +5 -1
  136. data/spec/support/stubbing.rb +1 -1
  137. metadata +93 -291
  138. data/culture/CONDUCT.md +0 -28
  139. data/culture/Gemfile +0 -9
  140. data/culture/LICENSE.txt +0 -22
  141. data/culture/README.md +0 -22
  142. data/culture/Rakefile +0 -5
  143. data/culture/SYNC.md +0 -70
  144. data/culture/celluloid-culture.gemspec +0 -18
  145. data/culture/gems/README.md +0 -39
  146. data/culture/gems/dependencies.yml +0 -85
  147. data/culture/gems/loader.rb +0 -101
  148. data/culture/rubocop/README.md +0 -38
  149. data/culture/rubocop/lint.yml +0 -8
  150. data/culture/rubocop/metrics.yml +0 -15
  151. data/culture/rubocop/perf.yml +0 -0
  152. data/culture/rubocop/rubocop.yml +0 -5
  153. data/culture/rubocop/style.yml +0 -57
  154. data/culture/spec/gems_spec.rb +0 -2
  155. data/culture/spec/spec_helper.rb +0 -0
  156. data/culture/spec/sync_spec.rb +0 -2
  157. data/culture/sync.rb +0 -56
  158. data/culture/tasks/rspec.rake +0 -5
  159. data/culture/tasks/rubocop.rake +0 -2
  160. data/lib/celluloid/actor/manager.rb +0 -7
  161. data/lib/celluloid/backported.rb +0 -2
  162. data/lib/celluloid/current.rb +0 -2
  163. data/lib/celluloid/deprecate.rb +0 -21
  164. data/lib/celluloid/fiber.rb +0 -32
  165. data/lib/celluloid/managed.rb +0 -3
  166. data/lib/celluloid/notices.rb +0 -15
  167. data/spec/celluloid/actor/manager_spec.rb +0 -0
  168. data/spec/deprecate/actor_system_spec.rb +0 -72
  169. data/spec/deprecate/block_spec.rb +0 -52
  170. data/spec/deprecate/calls_spec.rb +0 -39
  171. data/spec/deprecate/evented_mailbox_spec.rb +0 -34
  172. data/spec/deprecate/future_spec.rb +0 -32
  173. data/spec/deprecate/internal_pool_spec.rb +0 -4
  174. data/spec/support/env.rb +0 -21
@@ -3,13 +3,14 @@ module Celluloid
3
3
  attr_accessor :group
4
4
 
5
5
  def initialize
6
+ @pid = $PROCESS_ID
6
7
  @mutex = Mutex.new
7
8
  @group = []
8
9
  @running = true
9
10
  end
10
11
 
11
12
  def assert_active
12
- fail Celluloid::NotActive unless active?
13
+ raise Celluloid::NotActive unless active?
13
14
  end
14
15
 
15
16
  def assert_inactive
@@ -17,7 +18,7 @@ module Celluloid
17
18
  if RUBY_PLATFORM == "java"
18
19
  Celluloid.logger.warn "Group is still active"
19
20
  else
20
- fail Celluloid::StillActive
21
+ raise Celluloid::StillActive
21
22
  end
22
23
  end
23
24
 
@@ -25,7 +26,12 @@ module Celluloid
25
26
  to_a.each { |thread| yield thread }
26
27
  end
27
28
 
29
+ def forked?
30
+ @pid != $PROCESS_ID
31
+ end
32
+
28
33
  def to_a
34
+ return [] if forked?
29
35
  res = nil
30
36
  @mutex.synchronize { res = @group.dup }
31
37
  res
@@ -34,7 +40,11 @@ module Celluloid
34
40
  def purge(thread)
35
41
  @mutex.synchronize do
36
42
  @group.delete(thread)
37
- thread.kill rescue nil
43
+ begin
44
+ thread.kill
45
+ rescue
46
+ nil
47
+ end
38
48
  end
39
49
  end
40
50
 
@@ -47,15 +57,15 @@ module Celluloid
47
57
  end
48
58
 
49
59
  def get
50
- fail NotImplementedError
60
+ raise NotImplementedError
51
61
  end
52
62
 
53
63
  def create
54
- fail NotImplementedError
64
+ raise NotImplementedError
55
65
  end
56
66
 
57
67
  def shutdown
58
- fail NotImplementedError
68
+ raise NotImplementedError
59
69
  end
60
70
  end
61
71
  end
@@ -1,5 +1,3 @@
1
- require "thread"
2
-
3
1
  module Celluloid
4
2
  class Group
5
3
  class Pool < Group
@@ -12,7 +10,7 @@ module Celluloid
12
10
  super
13
11
  @mutex = Mutex.new
14
12
  @idle_threads = []
15
- @group = []
13
+ @group = []
16
14
  @busy_size = 0
17
15
  @idle_size = 0
18
16
 
@@ -1,5 +1,3 @@
1
- require "thread"
2
-
3
1
  module Celluloid
4
2
  class Group
5
3
  class Spawner < Group
@@ -11,7 +9,7 @@ module Celluloid
11
9
 
12
10
  def get(&block)
13
11
  assert_active
14
- fail ArgumentError.new("No block sent to Spawner.get()") unless block_given?
12
+ raise ArgumentError, "No block sent to Spawner.get()" unless block_given?
15
13
  instantiate block
16
14
  end
17
15
 
@@ -52,9 +50,7 @@ module Celluloid
52
50
  Internals::Logger.crash("thread crashed", ex)
53
51
  Thread.current[:celluloid_thread_state] = :error
54
52
  ensure
55
- unless Thread.current[:celluloid_thread_state] == :error
56
- Thread.current[:celluloid_thread_state] = :finished
57
- end
53
+ Thread.current[:celluloid_thread_state] = :finished unless Thread.current[:celluloid_thread_state] == :error
58
54
  @mutex.synchronize { @group.delete Thread.current }
59
55
  Thread.exit
60
56
  end
@@ -0,0 +1,15 @@
1
+ module Celluloid
2
+ module Internals
3
+ class CallChain
4
+ def self.current_id=(value)
5
+ Thread.current[:celluloid_chain_id] = value
6
+ task = Thread.current[:celluloid_task]
7
+ task.chain_id = value if task
8
+ end
9
+
10
+ def self.current_id
11
+ Thread.current[:celluloid_chain_id]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,62 @@
1
+ module Celluloid
2
+ module Internals
3
+ module CPUCounter
4
+ class << self
5
+ def cores
6
+ @cores ||= count_cores
7
+ end
8
+
9
+ def count_cores
10
+ from_result(from_env || from_sysdev || from_java || from_proc || from_win32ole || from_sysctl) || 1
11
+ end
12
+
13
+ def from_env
14
+ result = ENV["NUMBER_OF_PROCESSORS"]
15
+ result if result && !result.empty?
16
+ rescue
17
+ end
18
+
19
+ def from_sysdev
20
+ ::IO.read("/sys/devices/system/cpu/present").split("-").last.to_i + 1
21
+ rescue Errno::ENOENT
22
+ begin
23
+ result = Dir["/sys/devices/system/cpu/cpu*"].count { |n| n =~ /cpu\d+/ }
24
+ result unless result.zero?
25
+ rescue
26
+ end
27
+ rescue
28
+ end
29
+
30
+ def from_java
31
+ Java::Java.lang.Runtime.getRuntime.availableProcessors if defined? Java::Java
32
+ rescue
33
+ end
34
+
35
+ def from_proc
36
+ File.read("/proc/cpuinfo").scan(/^processor\s*:/).size if File.exist?("/proc/cpuinfo")
37
+ rescue
38
+ end
39
+
40
+ def from_win32ole
41
+ require "win32ole"
42
+ WIN32OLE.connect("winmgmts://").ExecQuery("select * from Win32_ComputerSystem").NumberOfProcessors
43
+ rescue LoadError
44
+ rescue
45
+ end
46
+
47
+ def from_sysctl
48
+ Integer `sysctl -n hw.ncpu 2>/dev/null`
49
+ rescue
50
+ end
51
+
52
+ def from_result(result)
53
+ if result
54
+ i = Integer(result.to_s[/\d+/], 10)
55
+ return i if i > 0
56
+ end
57
+ rescue
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -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