Commit 19022830 authored by Austin Clements's avatar Austin Clements

Fix handling of non-waitable zombie threads. Now they are not

considered running, so WaitStop doesn't lock up and
breakpoints get installed and uninstalled.  We also don't try
to detach from them, since that will fail.

R=rsc
APPROVED=rsc
DELTA=35  (26 added, 2 deleted, 7 changed)
OCL=31683
CL=31731
parent 13960ae8
...@@ -115,7 +115,8 @@ don't think the process is done until all of the threads have exited. ...@@ -115,7 +115,8 @@ don't think the process is done until all of the threads have exited.
Unfortunately, signals cannot be delivered to non-waitable zombies. Unfortunately, signals cannot be delivered to non-waitable zombies.
Most notably, SIGSTOP cannot be delivered; as a result, when you Most notably, SIGSTOP cannot be delivered; as a result, when you
broadcast SIGSTOP to all of the threads, you must not wait for broadcast SIGSTOP to all of the threads, you must not wait for
non-waitable zombies to stop. non-waitable zombies to stop. Furthermore, any ptrace command on a
non-waitable zombie, including PTRACE_DETACH, will return ESRCH.
== Multi-threaded debuggers == == Multi-threaded debuggers ==
......
...@@ -43,7 +43,21 @@ const ( ...@@ -43,7 +43,21 @@ const (
*/ */
// Each thread can be in one of the following set of states. // Each thread can be in one of the following set of states.
// Each state satisfies (isRunning() || isStopped() || isTerminal()). // Each state satisfies
// isRunning() || isStopped() || isZombie() || isTerminal().
//
// Running threads can be sent signals and must be waited on, but they
// cannot be inspected using ptrace.
//
// Stopped threads can be inspected and continued, but cannot be
// meaningfully waited on. They can be sent signals, but the signals
// will be queued until they are running again.
//
// Zombie threads cannot be inspected, continued, or sent signals (and
// therefore they cannot be stopped), but they must be waited on.
//
// Terminal threads no longer exist in the OS and thus you can't do
// anything with them.
type threadState string; type threadState string;
const ( const (
...@@ -61,13 +75,17 @@ const ( ...@@ -61,13 +75,17 @@ const (
) )
func (ts threadState) isRunning() bool { func (ts threadState) isRunning() bool {
return ts == running || ts == singleStepping || ts == stopping || ts == exiting; return ts == running || ts == singleStepping || ts == stopping;
} }
func (ts threadState) isStopped() bool { func (ts threadState) isStopped() bool {
return ts == stopped || ts == stoppedBreakpoint || ts == stoppedSignal || ts == stoppedThreadCreate || ts == stoppedExiting; return ts == stopped || ts == stoppedBreakpoint || ts == stoppedSignal || ts == stoppedThreadCreate || ts == stoppedExiting;
} }
func (ts threadState) isZombie() bool {
return ts == exiting;
}
func (ts threadState) isTerminal() bool { func (ts threadState) isTerminal() bool {
return ts == exited || ts == detached; return ts == exited || ts == detached;
} }
...@@ -429,7 +447,7 @@ func (t *thread) setState(new threadState) { ...@@ -429,7 +447,7 @@ func (t *thread) setState(new threadState) {
t.state = new; t.state = new;
t.logTrace("state %v -> %v", old, new); t.logTrace("state %v -> %v", old, new);
if !old.isRunning() && new.isRunning() { if !old.isRunning() && (new.isRunning() || new.isZombie()) {
// Start waiting on this thread // Start waiting on this thread
go t.wait(); go t.wait();
} }
...@@ -1020,8 +1038,12 @@ func (p *process) Continue() os.Error { ...@@ -1020,8 +1038,12 @@ func (p *process) Continue() os.Error {
if err != nil { if err != nil {
return err; return err;
} }
if t.state == stoppedExiting {
t.setState(exiting);
} else {
t.setState(running); t.setState(running);
} }
}
return nil; return nil;
}); });
if err != nil { if err != nil {
...@@ -1050,8 +1072,6 @@ func (p *process) WaitStop() os.Error { ...@@ -1050,8 +1072,6 @@ func (p *process) WaitStop() os.Error {
h := &transitionHandler{}; h := &transitionHandler{};
h.handle = func (st *thread, old, new threadState) { h.handle = func (st *thread, old, new threadState) {
if !new.isRunning() { if !new.isRunning() {
// TODO(austin) This gets stuck on
// zombie threads.
if p.someRunningThread() == nil { if p.someRunningThread() == nil {
ready <- nil; ready <- nil;
return; return;
...@@ -1094,9 +1114,12 @@ func (p *process) Detach() os.Error { ...@@ -1094,9 +1114,12 @@ func (p *process) Detach() os.Error {
} }
for pid, t := range p.threads { for pid, t := range p.threads {
if t.state.isStopped() {
// We can't detach from zombies.
if err := t.ptraceDetach(); err != nil { if err := t.ptraceDetach(); err != nil {
return err; return err;
} }
}
t.setState(detached); t.setState(detached);
p.threads[pid] = nil, false; p.threads[pid] = nil, false;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment