celluloid 0.12.0 → 0.13.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.
- data/README.md +36 -23
- data/lib/celluloid/actor.rb +74 -47
- data/lib/celluloid/autostart.rb +16 -0
- data/lib/celluloid/calls.rb +52 -52
- data/lib/celluloid/condition.rb +70 -0
- data/lib/celluloid/core_ext.rb +1 -1
- data/lib/celluloid/cpu_counter.rb +7 -1
- data/lib/celluloid/fsm.rb +49 -16
- data/lib/celluloid/future.rb +5 -2
- data/lib/celluloid/internal_pool.rb +62 -44
- data/lib/celluloid/legacy.rb +46 -0
- data/lib/celluloid/links.rb +0 -5
- data/lib/celluloid/logger.rb +14 -4
- data/lib/celluloid/logging/incident.rb +21 -0
- data/lib/celluloid/logging/incident_logger.rb +129 -0
- data/lib/celluloid/logging/incident_reporter.rb +48 -0
- data/lib/celluloid/logging/log_event.rb +20 -0
- data/lib/celluloid/logging/ring_buffer.rb +65 -0
- data/lib/celluloid/logging.rb +5 -0
- data/lib/celluloid/mailbox.rb +2 -1
- data/lib/celluloid/method.rb +5 -0
- data/lib/celluloid/pool_manager.rb +21 -8
- data/lib/celluloid/proxies/actor_proxy.rb +11 -11
- data/lib/celluloid/proxies/async_proxy.rb +5 -1
- data/lib/celluloid/responses.rb +8 -6
- data/lib/celluloid/stack_dump.rb +94 -0
- data/lib/celluloid/supervision_group.rb +5 -3
- data/lib/celluloid/system_events.rb +11 -0
- data/lib/celluloid/tasks/task_fiber.rb +17 -9
- data/lib/celluloid/tasks/task_thread.rb +24 -14
- data/lib/celluloid/tasks.rb +59 -0
- data/lib/celluloid/thread_handle.rb +10 -1
- data/lib/celluloid/version.rb +1 -1
- data/lib/celluloid.rb +146 -87
- data/spec/support/actor_examples.rb +242 -114
- data/spec/support/example_actor_class.rb +17 -5
- data/spec/support/task_examples.rb +11 -2
- metadata +58 -14
- data/lib/celluloid/boot.rb +0 -9
- data/lib/celluloid/task.rb +0 -22
|
@@ -6,7 +6,8 @@ module Celluloid
|
|
|
6
6
|
# Don't use this class directly. Instead use MyKlass.pool
|
|
7
7
|
class PoolManager
|
|
8
8
|
include Celluloid
|
|
9
|
-
trap_exit :
|
|
9
|
+
trap_exit :__crash_handler__
|
|
10
|
+
finalizer :__shutdown__
|
|
10
11
|
|
|
11
12
|
def initialize(worker_class, options = {})
|
|
12
13
|
@size = options[:size] || [Celluloid.cores, 2].max
|
|
@@ -22,7 +23,7 @@ module Celluloid
|
|
|
22
23
|
@busy = []
|
|
23
24
|
end
|
|
24
25
|
|
|
25
|
-
def
|
|
26
|
+
def __shutdown__
|
|
26
27
|
terminators = (@idle + @busy).each do |actor|
|
|
27
28
|
begin
|
|
28
29
|
actor.future(:terminate)
|
|
@@ -34,13 +35,13 @@ module Celluloid
|
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def _send_(method, *args, &block)
|
|
37
|
-
worker =
|
|
38
|
+
worker = __provision_worker__
|
|
38
39
|
|
|
39
40
|
begin
|
|
40
41
|
worker._send_ method, *args, &block
|
|
41
42
|
rescue DeadActorError # if we get a dead actor out of the pool
|
|
42
43
|
wait :respawn_complete
|
|
43
|
-
worker =
|
|
44
|
+
worker = __provision_worker__
|
|
44
45
|
retry
|
|
45
46
|
rescue Exception => ex
|
|
46
47
|
abort ex
|
|
@@ -76,12 +77,24 @@ module Celluloid
|
|
|
76
77
|
_send_ :inspect
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
def size
|
|
81
|
+
@size
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def busy_size
|
|
85
|
+
@busy.length
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def idle_size
|
|
89
|
+
@idle.length
|
|
90
|
+
end
|
|
91
|
+
|
|
79
92
|
# Provision a new worker
|
|
80
|
-
def
|
|
93
|
+
def __provision_worker__
|
|
81
94
|
while @idle.empty?
|
|
82
95
|
# Wait for responses from one of the busy workers
|
|
83
96
|
response = exclusive { receive { |msg| msg.is_a?(Response) } }
|
|
84
|
-
Thread.current[:
|
|
97
|
+
Thread.current[:celluloid_actor].handle_message(response)
|
|
85
98
|
end
|
|
86
99
|
|
|
87
100
|
worker = @idle.shift
|
|
@@ -91,7 +104,7 @@ module Celluloid
|
|
|
91
104
|
end
|
|
92
105
|
|
|
93
106
|
# Spawn a new worker for every crashed one
|
|
94
|
-
def
|
|
107
|
+
def __crash_handler__(actor, reason)
|
|
95
108
|
@busy.delete actor
|
|
96
109
|
@idle.delete actor
|
|
97
110
|
return unless reason
|
|
@@ -100,7 +113,7 @@ module Celluloid
|
|
|
100
113
|
signal :respawn_complete
|
|
101
114
|
end
|
|
102
115
|
|
|
103
|
-
def respond_to?(method)
|
|
116
|
+
def respond_to?(method, include_private = false)
|
|
104
117
|
super || @worker_class.instance_methods.include?(method.to_sym)
|
|
105
118
|
end
|
|
106
119
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Celluloid
|
|
2
2
|
# A proxy object returned from Celluloid::Actor.new/new_link which converts
|
|
3
3
|
# the normal Ruby method protocol into an inter-actor message protocol
|
|
4
|
-
class ActorProxy
|
|
4
|
+
class ActorProxy
|
|
5
5
|
attr_reader :mailbox, :thread
|
|
6
6
|
|
|
7
7
|
def initialize(actor)
|
|
@@ -10,17 +10,24 @@ module Celluloid
|
|
|
10
10
|
@async_proxy = AsyncProxy.new(actor)
|
|
11
11
|
@future_proxy = FutureProxy.new(actor)
|
|
12
12
|
end
|
|
13
|
-
|
|
13
|
+
|
|
14
|
+
# allow querying the real class
|
|
15
|
+
alias :__class__ :class
|
|
16
|
+
|
|
14
17
|
def class
|
|
15
18
|
Actor.call @mailbox, :__send__, :class
|
|
16
19
|
end
|
|
17
20
|
|
|
21
|
+
def send(meth, *args, &block)
|
|
22
|
+
Actor.call @mailbox, :send, meth, *args, &block
|
|
23
|
+
end
|
|
24
|
+
|
|
18
25
|
def _send_(meth, *args, &block)
|
|
19
26
|
Actor.call @mailbox, :__send__, meth, *args, &block
|
|
20
27
|
end
|
|
21
28
|
|
|
22
29
|
def inspect
|
|
23
|
-
Actor.call
|
|
30
|
+
Actor.call(@mailbox, :inspect)
|
|
24
31
|
rescue DeadActorError
|
|
25
32
|
"#<Celluloid::Actor(#{@klass}) dead>"
|
|
26
33
|
end
|
|
@@ -90,14 +97,7 @@ module Celluloid
|
|
|
90
97
|
|
|
91
98
|
# method_missing black magic to call bang predicate methods asynchronously
|
|
92
99
|
def method_missing(meth, *args, &block)
|
|
93
|
-
|
|
94
|
-
if meth.match(/!$/)
|
|
95
|
-
unbanged_meth = meth.to_s
|
|
96
|
-
unbanged_meth.slice!(-1, 1)
|
|
97
|
-
Actor.async @mailbox, unbanged_meth, *args, &block
|
|
98
|
-
else
|
|
99
|
-
Actor.call @mailbox, meth, *args, &block
|
|
100
|
-
end
|
|
100
|
+
Actor.call @mailbox, meth, *args, &block
|
|
101
101
|
end
|
|
102
102
|
end
|
|
103
103
|
end
|
|
@@ -13,7 +13,11 @@ module Celluloid
|
|
|
13
13
|
|
|
14
14
|
# method_missing black magic to call bang predicate methods asynchronously
|
|
15
15
|
def method_missing(meth, *args, &block)
|
|
16
|
-
|
|
16
|
+
if @mailbox == ::Thread.current[:celluloid_mailbox]
|
|
17
|
+
Actor.async @mailbox, :__send__, meth, *args, &block
|
|
18
|
+
else
|
|
19
|
+
Actor.async @mailbox, meth, *args, &block
|
|
20
|
+
end
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
end
|
data/lib/celluloid/responses.rb
CHANGED
|
@@ -18,13 +18,15 @@ module Celluloid
|
|
|
18
18
|
# Call was aborted due to caller error
|
|
19
19
|
class ErrorResponse < Response
|
|
20
20
|
def value
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
ex = super
|
|
22
|
+
ex = ex.cause if ex.is_a? AbortError
|
|
23
|
+
|
|
24
|
+
if ex.backtrace
|
|
25
|
+
ex.backtrace << "(celluloid):0:in `remote procedure call'"
|
|
26
|
+
ex.backtrace.concat(caller)
|
|
27
27
|
end
|
|
28
|
+
|
|
29
|
+
raise ex
|
|
28
30
|
end
|
|
29
31
|
end
|
|
30
32
|
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
class StackDump
|
|
3
|
+
|
|
4
|
+
class TaskState < Struct.new(:task_class, :status)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class ActorState
|
|
8
|
+
attr_accessor :subject_id, :subject_class, :name
|
|
9
|
+
attr_accessor :status, :tasks
|
|
10
|
+
attr_accessor :backtrace
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class ThreadState < Struct.new(:thread_id, :backtrace)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_accessor :actors, :threads
|
|
17
|
+
|
|
18
|
+
def initialize
|
|
19
|
+
@actors = []
|
|
20
|
+
@threads = []
|
|
21
|
+
|
|
22
|
+
snapshot
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def snapshot
|
|
26
|
+
Thread.list.each do |thread|
|
|
27
|
+
if actor = thread[:celluloid_actor]
|
|
28
|
+
@actors << snapshot_actor(actor)
|
|
29
|
+
else
|
|
30
|
+
@threads << snapshot_thread(thread)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def snapshot_actor(actor)
|
|
36
|
+
state = ActorState.new
|
|
37
|
+
state.subject_id = actor.subject.object_id
|
|
38
|
+
state.subject_class = actor.subject.class
|
|
39
|
+
|
|
40
|
+
tasks = actor.tasks
|
|
41
|
+
if tasks.empty?
|
|
42
|
+
state.status = :idle
|
|
43
|
+
else
|
|
44
|
+
state.status = :running
|
|
45
|
+
state.tasks = tasks.collect { |t| TaskState.new(t.class, t.status) }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
state.backtrace = actor.thread.backtrace if actor.thread
|
|
49
|
+
state
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def snapshot_thread(thread)
|
|
53
|
+
ThreadState.new(thread.object_id, thread.backtrace)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def dump(output = STDERR)
|
|
57
|
+
@actors.each do |actor|
|
|
58
|
+
string = ""
|
|
59
|
+
string << "Celluloid::Actor 0x#{actor.subject_id.to_s(16)}: #{actor.subject_class}"
|
|
60
|
+
string << " [#{actor.name}]" if actor.name
|
|
61
|
+
string << "\n"
|
|
62
|
+
|
|
63
|
+
if actor.status == :idle
|
|
64
|
+
string << "State: Idle (waiting for messages)\n"
|
|
65
|
+
else
|
|
66
|
+
string << "State: Running (executing tasks)\n"
|
|
67
|
+
string << "Tasks:\n"
|
|
68
|
+
|
|
69
|
+
actor.tasks.each_with_index do |task, i|
|
|
70
|
+
string << " #{i+1}) #{task.task_class}: #{task.status}\n"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
display_backtrace actor.backtrace, string
|
|
75
|
+
output.print string
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@threads.each do |thread|
|
|
79
|
+
string = ""
|
|
80
|
+
string << "Thread 0x#{thread.thread_id.to_s(16)}:\n"
|
|
81
|
+
display_backtrace thread.backtrace, string
|
|
82
|
+
output.print string
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def display_backtrace(backtrace, output)
|
|
87
|
+
if backtrace
|
|
88
|
+
output << "\t" << backtrace.join("\n\t") << "\n\n"
|
|
89
|
+
else
|
|
90
|
+
output << "EMPTY BACKTRACE\n\n"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -60,7 +60,7 @@ module Celluloid
|
|
|
60
60
|
@members = []
|
|
61
61
|
@registry = registry || Registry.root
|
|
62
62
|
|
|
63
|
-
yield
|
|
63
|
+
yield current_actor if block_given?
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
def supervise(klass, *args, &block)
|
|
@@ -86,9 +86,11 @@ module Celluloid
|
|
|
86
86
|
@members.map(&:actor)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
finalizer :finalize
|
|
90
|
+
|
|
89
91
|
# Terminate the group
|
|
90
92
|
def finalize
|
|
91
|
-
@members.
|
|
93
|
+
@members.reverse_each(&:terminate)
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
# Restart a crashed actor
|
|
@@ -147,7 +149,7 @@ module Celluloid
|
|
|
147
149
|
|
|
148
150
|
def terminate
|
|
149
151
|
@actor.terminate if @actor
|
|
150
|
-
rescue DeadActorError
|
|
152
|
+
rescue DeadActorError, MailboxError
|
|
151
153
|
end
|
|
152
154
|
end
|
|
153
155
|
end
|
|
@@ -51,4 +51,15 @@ module Celluloid
|
|
|
51
51
|
|
|
52
52
|
# Request for an actor to terminate
|
|
53
53
|
class TerminationRequest < SystemEvent; end
|
|
54
|
+
|
|
55
|
+
# Signal a condition
|
|
56
|
+
class SignalConditionRequest < SystemEvent
|
|
57
|
+
def initialize(task, value)
|
|
58
|
+
@task, @value = task, value
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def call
|
|
62
|
+
@task.resume(@value)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
54
65
|
end
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
module Celluloid
|
|
2
|
+
class FiberStackError < StandardError; end
|
|
3
|
+
|
|
2
4
|
# Tasks with a Fiber backend
|
|
3
|
-
class TaskFiber
|
|
5
|
+
class TaskFiber < Task
|
|
4
6
|
attr_reader :type, :status
|
|
5
7
|
|
|
6
8
|
# Run the given block within a task
|
|
7
9
|
def initialize(type)
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
super
|
|
11
|
+
|
|
12
|
+
actor = Thread.current[:celluloid_actor]
|
|
13
|
+
mailbox = Thread.current[:celluloid_mailbox]
|
|
14
|
+
chain_id = Thread.current[:celluloid_chain_id]
|
|
10
15
|
|
|
11
|
-
actor, mailbox = Thread.current[:actor], Thread.current[:mailbox]
|
|
12
16
|
raise NotActorError, "can't create tasks outside of actors" unless actor
|
|
13
17
|
|
|
14
18
|
@fiber = Fiber.new do
|
|
15
19
|
@status = :running
|
|
16
|
-
Thread.current[:
|
|
17
|
-
Thread.current[:
|
|
18
|
-
Thread.current[:
|
|
20
|
+
Thread.current[:celluloid_actor] = actor
|
|
21
|
+
Thread.current[:celluloid_mailbox] = mailbox
|
|
22
|
+
Thread.current[:celluloid_task] = self
|
|
23
|
+
Thread.current[:celluloid_chain_id] = chain_id
|
|
24
|
+
|
|
19
25
|
actor.tasks << self
|
|
20
26
|
|
|
21
27
|
begin
|
|
@@ -43,8 +49,10 @@ module Celluloid
|
|
|
43
49
|
def resume(value = nil)
|
|
44
50
|
@fiber.resume value
|
|
45
51
|
nil
|
|
46
|
-
rescue
|
|
47
|
-
raise
|
|
52
|
+
rescue SystemStackError => ex
|
|
53
|
+
raise FiberStackError, "#{ex} (please see https://github.com/celluloid/celluloid/wiki/Fiber-stack-errors)"
|
|
54
|
+
rescue FiberError => ex
|
|
55
|
+
raise DeadTaskError, "cannot resume a dead task (#{ex})"
|
|
48
56
|
end
|
|
49
57
|
|
|
50
58
|
# Terminate this task
|
|
@@ -1,33 +1,40 @@
|
|
|
1
1
|
module Celluloid
|
|
2
2
|
# Tasks with a Thread backend
|
|
3
|
-
class TaskThread
|
|
3
|
+
class TaskThread < Task
|
|
4
4
|
attr_reader :type, :status
|
|
5
5
|
|
|
6
6
|
# Run the given block within a task
|
|
7
7
|
def initialize(type)
|
|
8
|
-
|
|
9
|
-
@status = :new
|
|
8
|
+
super
|
|
10
9
|
|
|
11
10
|
@resume_queue = Queue.new
|
|
11
|
+
@exception_queue = Queue.new
|
|
12
12
|
@yield_mutex = Mutex.new
|
|
13
13
|
@yield_cond = ConditionVariable.new
|
|
14
14
|
|
|
15
|
-
actor
|
|
15
|
+
actor = Thread.current[:celluloid_actor]
|
|
16
|
+
mailbox = Thread.current[:celluloid_mailbox]
|
|
17
|
+
chain_id = Thread.current[:celluloid_chain_id]
|
|
18
|
+
|
|
16
19
|
raise NotActorError, "can't create tasks outside of actors" unless actor
|
|
17
20
|
|
|
18
|
-
@thread =
|
|
21
|
+
@thread = Celluloid.internal_pool.get do
|
|
19
22
|
begin
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
ex = @resume_queue.pop
|
|
24
|
+
raise ex if ex.is_a?(Task::TerminatedError)
|
|
25
|
+
|
|
26
|
+
@status = :running
|
|
27
|
+
Thread.current[:celluloid_actor] = actor
|
|
28
|
+
Thread.current[:celluloid_mailbox] = mailbox
|
|
29
|
+
Thread.current[:celluloid_task] = self
|
|
30
|
+
Thread.current[:celluloid_chain_id] = chain_id
|
|
31
|
+
|
|
32
|
+
actor.tasks << self
|
|
33
|
+
yield
|
|
29
34
|
rescue Task::TerminatedError
|
|
30
35
|
# Task was explicitly terminated
|
|
36
|
+
rescue Exception => ex
|
|
37
|
+
@exception_queue << ex
|
|
31
38
|
ensure
|
|
32
39
|
@status = :dead
|
|
33
40
|
actor.tasks.delete self
|
|
@@ -55,6 +62,9 @@ module Celluloid
|
|
|
55
62
|
@yield_mutex.synchronize do
|
|
56
63
|
@resume_queue.push(value)
|
|
57
64
|
@yield_cond.wait(@yield_mutex)
|
|
65
|
+
while @exception_queue.size > 0
|
|
66
|
+
raise @exception_queue.pop
|
|
67
|
+
end
|
|
58
68
|
end
|
|
59
69
|
|
|
60
70
|
nil
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
module Celluloid
|
|
2
|
+
# Asked to do task-related things outside a task
|
|
3
|
+
class NotTaskError < StandardError; end
|
|
4
|
+
|
|
5
|
+
# Trying to resume a dead task
|
|
6
|
+
class DeadTaskError < StandardError; end
|
|
7
|
+
|
|
8
|
+
# Tasks are interruptable/resumable execution contexts used to run methods
|
|
9
|
+
class Task
|
|
10
|
+
class TerminatedError < StandardError; end # kill a running task
|
|
11
|
+
|
|
12
|
+
# Obtain the current task
|
|
13
|
+
def self.current
|
|
14
|
+
Thread.current[:celluloid_task] or raise NotTaskError, "not within a task context"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Suspend the running task, deferring to the scheduler
|
|
18
|
+
def self.suspend(status)
|
|
19
|
+
Task.current.suspend(status)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Create a new task
|
|
23
|
+
def initialize(type)
|
|
24
|
+
@type = type
|
|
25
|
+
@status = :new
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class TaskSet
|
|
30
|
+
include Enumerable
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
@tasks = Set.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def <<(task)
|
|
37
|
+
@tasks += [task]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def delete(task)
|
|
41
|
+
@tasks -= [task]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def each(&blk)
|
|
45
|
+
@tasks.each &blk
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def first
|
|
49
|
+
@tasks.first
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def empty?
|
|
53
|
+
@tasks.empty?
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
require 'celluloid/tasks/task_fiber'
|
|
59
|
+
require 'celluloid/tasks/task_thread'
|
|
@@ -7,7 +7,7 @@ module Celluloid
|
|
|
7
7
|
@mutex = Mutex.new
|
|
8
8
|
@join = ConditionVariable.new
|
|
9
9
|
|
|
10
|
-
@thread =
|
|
10
|
+
@thread = Celluloid.internal_pool.get do
|
|
11
11
|
begin
|
|
12
12
|
yield
|
|
13
13
|
ensure
|
|
@@ -35,5 +35,14 @@ module Celluloid
|
|
|
35
35
|
@mutex.synchronize { @join.wait(@mutex) if @thread }
|
|
36
36
|
self
|
|
37
37
|
end
|
|
38
|
+
|
|
39
|
+
# Obtain the backtrace for this thread
|
|
40
|
+
def backtrace
|
|
41
|
+
@thread.backtrace
|
|
42
|
+
rescue NoMethodError
|
|
43
|
+
# undefined method `backtrace' for nil:NilClass
|
|
44
|
+
# Swallow this in case this ThreadHandle was terminated and @thread was
|
|
45
|
+
# set to nil
|
|
46
|
+
end
|
|
38
47
|
end
|
|
39
48
|
end
|
data/lib/celluloid/version.rb
CHANGED