]> Cypherpunks repositories - gostls13.git/commitdiff
Fix handling of non-waitable zombie threads. Now they are not
authorAustin Clements <aclements@csail.mit.edu>
Thu, 16 Jul 2009 16:48:37 +0000 (09:48 -0700)
committerAustin Clements <aclements@csail.mit.edu>
Thu, 16 Jul 2009 16:48:37 +0000 (09:48 -0700)
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

usr/austin/ptrace/ptrace-nptl.txt
usr/austin/ptrace/ptrace_linux.go

index c5264047373f7eeb0eefa1dc4feecf4fdca5ec39..62cbf7700352fc93a8c3951ec52b9aa19c9674ae 100644 (file)
@@ -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.
 Most notably, SIGSTOP cannot be delivered; as a result, when you
 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 ==
 
index 43a509401acbb2b0a4cc89037793fcb23d820551..b1e1b3da9321302b6188763d96f042b4f8ba8635 100644 (file)
@@ -43,7 +43,21 @@ const (
  */
 
 // 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;
 
 const (
@@ -61,13 +75,17 @@ const (
 )
 
 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 {
        return ts == stopped || ts == stoppedBreakpoint || ts == stoppedSignal || ts == stoppedThreadCreate || ts == stoppedExiting;
 }
 
+func (ts threadState) isZombie() bool {
+       return ts == exiting;
+}
+
 func (ts threadState) isTerminal() bool {
        return ts == exited || ts == detached;
 }
@@ -429,7 +447,7 @@ func (t *thread) setState(new threadState) {
        t.state = 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
                go t.wait();
        }
@@ -1020,7 +1038,11 @@ func (p *process) Continue() os.Error {
                        if err != nil {
                                return err;
                        }
-                       t.setState(running);
+                       if t.state == stoppedExiting {
+                               t.setState(exiting);
+                       } else {
+                               t.setState(running);
+                       }
                }
                return nil;
        });
@@ -1050,8 +1072,6 @@ func (p *process) WaitStop() os.Error {
                h := &transitionHandler{};
                h.handle = func (st *thread, old, new threadState) {
                        if !new.isRunning() {
-                               // TODO(austin) This gets stuck on
-                               // zombie threads.
                                if p.someRunningThread() == nil {
                                        ready <- nil;
                                        return;
@@ -1094,8 +1114,11 @@ func (p *process) Detach() os.Error {
                }
 
                for pid, t := range p.threads {
-                       if err := t.ptraceDetach(); err != nil {
-                               return err;
+                       if t.state.isStopped() {
+                               // We can't detach from zombies.
+                               if err := t.ptraceDetach(); err != nil {
+                                       return err;
+                               }
                        }
                        t.setState(detached);
                        p.threads[pid] = nil, false;