celluloid 0.18.0.pre → 0.18.0.pre2

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 (179) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +114 -39
  3. data/CONDUCT.md +13 -0
  4. data/CONTRIBUTING.md +39 -0
  5. data/README.md +59 -55
  6. data/architecture.md +120 -0
  7. data/examples/basic_usage.rb +1 -1
  8. data/examples/configurations.rb +78 -0
  9. data/examples/futures.rb +1 -1
  10. data/examples/ring.rb +5 -4
  11. data/examples/simple_pmap.rb +1 -1
  12. data/examples/stack.rb +2 -2
  13. data/examples/supervisors_and_registry.rb +82 -0
  14. data/examples/timers.rb +1 -1
  15. data/lib/celluloid.rb +72 -47
  16. data/lib/celluloid/actor.rb +27 -17
  17. data/lib/celluloid/actor/system.rb +13 -29
  18. data/lib/celluloid/autostart.rb +5 -5
  19. data/lib/celluloid/backported.rb +6 -2
  20. data/lib/celluloid/call/async.rb +2 -0
  21. data/lib/celluloid/call/sync.rb +10 -3
  22. data/lib/celluloid/calls.rb +5 -12
  23. data/lib/celluloid/cell.rb +5 -9
  24. data/lib/celluloid/condition.rb +3 -3
  25. data/lib/celluloid/core_ext.rb +0 -2
  26. data/lib/celluloid/current.rb +3 -1
  27. data/lib/celluloid/debug.rb +3 -0
  28. data/lib/celluloid/exceptions.rb +2 -2
  29. data/lib/celluloid/future.rb +7 -9
  30. data/lib/celluloid/group.rb +12 -8
  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 -5
  58. data/lib/celluloid/managed.rb +6 -3
  59. data/lib/celluloid/notifications.rb +95 -0
  60. data/lib/celluloid/pool.rb +6 -0
  61. data/lib/celluloid/probe.rb +81 -0
  62. data/lib/celluloid/proxy/abstract.rb +9 -9
  63. data/lib/celluloid/proxy/async.rb +1 -1
  64. data/lib/celluloid/proxy/block.rb +2 -2
  65. data/lib/celluloid/proxy/cell.rb +1 -1
  66. data/lib/celluloid/proxy/future.rb +2 -4
  67. data/lib/celluloid/proxy/sync.rb +1 -3
  68. data/lib/celluloid/rspec.rb +22 -33
  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/{culture/rubocop/perf.yml → lib/celluloid/supervision/container/tree.rb} +0 -0
  82. data/lib/celluloid/supervision/deprecate.rb +9 -0
  83. data/lib/celluloid/supervision/deprecate/supervise.rb +105 -0
  84. data/lib/celluloid/supervision/deprecate/validation.rb +54 -0
  85. data/lib/celluloid/supervision/service.rb +27 -0
  86. data/lib/celluloid/supervision/supervise.rb +34 -0
  87. data/lib/celluloid/supervision/validation.rb +40 -0
  88. data/lib/celluloid/supervision/version.rb +5 -0
  89. data/lib/celluloid/system_events.rb +11 -6
  90. data/lib/celluloid/task.rb +25 -12
  91. data/lib/celluloid/task/fibered.rb +2 -0
  92. data/lib/celluloid/task/threaded.rb +3 -3
  93. data/lib/celluloid/test.rb +5 -2
  94. data/lib/celluloid/thread.rb +0 -2
  95. data/lib/celluloid/version.rb +1 -1
  96. data/spec/celluloid/block_spec.rb +29 -32
  97. data/spec/celluloid/calls_spec.rb +5 -15
  98. data/spec/celluloid/future_spec.rb +2 -2
  99. data/spec/celluloid/internals/cpu_counter_spec.rb +129 -0
  100. data/spec/celluloid/internals/links_spec.rb +43 -0
  101. data/spec/celluloid/internals/properties_spec.rb +40 -0
  102. data/spec/celluloid/internals/registry_spec.rb +62 -0
  103. data/spec/celluloid/internals/stack/dump_spec.rb +4 -0
  104. data/spec/celluloid/internals/stack/summary_spec.rb +4 -0
  105. data/spec/celluloid/internals/thread_handle_spec.rb +60 -0
  106. data/spec/celluloid/internals/uuid_spec.rb +9 -0
  107. data/spec/celluloid/logging/ring_buffer_spec.rb +36 -0
  108. data/spec/celluloid/mailbox/evented_spec.rb +11 -22
  109. data/spec/celluloid/misc/leak_spec.rb +3 -4
  110. data/spec/celluloid/notifications_spec.rb +140 -0
  111. data/spec/celluloid/probe_spec.rb +102 -0
  112. data/spec/celluloid/proxy_spec.rb +30 -30
  113. data/spec/celluloid/supervision/behavior_spec.rb +74 -0
  114. data/spec/celluloid/supervision/configuration_spec.rb +181 -0
  115. data/spec/celluloid/supervision/container_spec.rb +72 -0
  116. data/spec/celluloid/supervision/instance_spec.rb +13 -0
  117. data/spec/celluloid/supervision/root_spec.rb +28 -0
  118. data/spec/celluloid/supervision/supervisor_spec.rb +93 -0
  119. data/spec/celluloid/task/fibered_spec.rb +1 -3
  120. data/spec/celluloid/task/threaded_spec.rb +1 -3
  121. data/spec/shared/actor_examples.rb +58 -33
  122. data/spec/shared/group_examples.rb +2 -2
  123. data/spec/shared/mailbox_examples.rb +1 -1
  124. data/spec/shared/stack_examples.rb +87 -0
  125. data/spec/shared/task_examples.rb +2 -3
  126. data/spec/spec_helper.rb +2 -4
  127. data/spec/support/configure_rspec.rb +2 -3
  128. data/spec/support/coverage.rb +2 -4
  129. data/spec/support/crash_checking.rb +2 -2
  130. data/spec/support/examples/actor_class.rb +3 -8
  131. data/spec/support/examples/call_class.rb +2 -2
  132. data/spec/support/examples/container_class.rb +35 -0
  133. data/spec/support/examples/evented_mailbox_class.rb +1 -2
  134. data/spec/support/examples/stack_classes.rb +58 -0
  135. data/spec/support/examples/stack_methods.rb +23 -0
  136. data/spec/support/examples/subordinate_class.rb +19 -0
  137. data/spec/support/logging.rb +2 -34
  138. data/spec/support/loose_threads.rb +3 -16
  139. data/spec/support/reset_class_variables.rb +5 -1
  140. data/spec/support/stubbing.rb +1 -1
  141. metadata +91 -316
  142. data/culture/CONDUCT.md +0 -38
  143. data/culture/GSoC/1010-why_we_will_participate.md +0 -17
  144. data/culture/GSoC/1020-how_mentors_stay_engaged.md +0 -7
  145. data/culture/GSoC/1030-keeping_students_on_schedule.md +0 -9
  146. data/culture/GSoC/1040-getting_students_involved.md +0 -5
  147. data/culture/GSoC/1050-student_involvement_after.md +0 -5
  148. data/culture/GSoC/README.md +0 -16
  149. data/culture/Gemfile +0 -9
  150. data/culture/LICENSE.txt +0 -22
  151. data/culture/README.md +0 -22
  152. data/culture/Rakefile +0 -5
  153. data/culture/SYNC.md +0 -70
  154. data/culture/celluloid-culture.gemspec +0 -18
  155. data/culture/gems/README.md +0 -39
  156. data/culture/gems/dependencies.yml +0 -93
  157. data/culture/gems/loader.rb +0 -101
  158. data/culture/rubocop/README.md +0 -38
  159. data/culture/rubocop/lint.yml +0 -8
  160. data/culture/rubocop/metrics.yml +0 -15
  161. data/culture/rubocop/rubocop.yml +0 -5
  162. data/culture/rubocop/style.yml +0 -61
  163. data/culture/spec/gems_spec.rb +0 -2
  164. data/culture/spec/spec_helper.rb +0 -0
  165. data/culture/spec/sync_spec.rb +0 -2
  166. data/culture/sync.rb +0 -56
  167. data/culture/tasks/rspec.rake +0 -5
  168. data/culture/tasks/rubocop.rake +0 -2
  169. data/lib/celluloid/actor/manager.rb +0 -7
  170. data/lib/celluloid/deprecate.rb +0 -34
  171. data/lib/celluloid/fiber.rb +0 -32
  172. data/lib/celluloid/notices.rb +0 -15
  173. data/spec/deprecate/actor_system_spec.rb +0 -72
  174. data/spec/deprecate/block_spec.rb +0 -52
  175. data/spec/deprecate/calls_spec.rb +0 -39
  176. data/spec/deprecate/evented_mailbox_spec.rb +0 -34
  177. data/spec/deprecate/future_spec.rb +0 -32
  178. data/spec/deprecate/internal_pool_spec.rb +0 -4
  179. data/spec/support/env.rb +0 -21
@@ -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,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"
@@ -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,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