Fix a process exit/wait race. The wait*() code was making a faulty test
authorMatthew Dillon <dillon@dragonflybsd.org>
Fri, 2 Dec 2005 22:02:20 +0000 (22:02 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Fri, 2 Dec 2005 22:02:20 +0000 (22:02 +0000)
commitc1102e9f24b16377f5fe3cbd55828be64335bded
treef95f8081d020a6f741664a813bbf65715da53f11
parent25c776bee7bcbb56ef24a584f56c60f620e4d662
Fix a process exit/wait race.  The wait*() code was making a faulty test
to determine that the exiting process had completely exited and was no
longer running.  Testing the TDF_RUNNING flag is insufficient because an
exiting process may block at various points after becoming a Zombie, but
before it deschedules itself for the last time.

Add a new flag, TDF_EXITING, which is set just prior to a thread descheduling
itself for the last time.  The reaper then checks that TDF_EXITING is set
and TDF_RUNNING is clear.

Fix a second faulty test in both the exit and the thread cpu migration
code.  If a thread gets preempted, TDF_RUNNING will be temporarily cleared,
so testing TDF_RUNNING is not sufficient by itself.  We must also test
the TDF_PREEMPT_LOCK flag to be sure that it is also clear.

So the grand result is that to really be sure the zombie process has been
completely descheduled and is no longer running or will ever run again,
the TDF_EXITING, TDF_RUNNING, *and* TDF_PREEMPT_LOCK flags must be tested
and all must be clear except for TDF_EXITING.

It should be noted that TDF_RUNNING on the previously scheduled process
is always cleared AFTER we have context-switched into the next scheduled
thread or the idle thread, so seeing a cleared TDF_RUNNING along with the
appropriate state for the other flags does in fact guarentee that the thread
in question is no longer using its stack in any way.

Reported-by: Stefan Krueger <skrueger@meinberlikomm.de>
sys/i386/i386/vm_machdep.c
sys/kern/kern_exit.c
sys/kern/lwkt_thread.c
sys/platform/pc32/i386/vm_machdep.c
sys/sys/thread.h