summaryrefslogtreecommitdiffstats
path: root/sys/kern/kern_thr.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/kern/kern_thr.c')
-rw-r--r--sys/kern/kern_thr.c64
1 files changed, 46 insertions, 18 deletions
diff --git a/sys/kern/kern_thr.c b/sys/kern/kern_thr.c
index 98965b1..b01aecb 100644
--- a/sys/kern/kern_thr.c
+++ b/sys/kern/kern_thr.c
@@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/posix4.h>
+#include <sys/ptrace.h>
#include <sys/racct.h>
#include <sys/resourcevar.h>
#include <sys/rwlock.h>
@@ -252,6 +253,8 @@ thread_create(struct thread *td, struct rtprio *rtp,
thread_unlock(td);
if (P_SHOULDSTOP(p))
newtd->td_flags |= TDF_ASTPENDING | TDF_NEEDSUSPCHK;
+ if (p->p_ptevents & PTRACE_LWP)
+ newtd->td_dbgflags |= TDB_BORN;
PROC_UNLOCK(p);
tidhash_add(newtd);
@@ -314,29 +317,54 @@ kern_thr_exit(struct thread *td)
p = td->td_proc;
- rw_wlock(&tidhash_lock);
+ /*
+ * If all of the threads in a process call this routine to
+ * exit (e.g. all threads call pthread_exit()), exactly one
+ * thread should return to the caller to terminate the process
+ * instead of the thread.
+ *
+ * Checking p_numthreads alone is not sufficient since threads
+ * might be committed to terminating while the PROC_LOCK is
+ * dropped in either ptracestop() or while removing this thread
+ * from the tidhash. Instead, the p_pendingexits field holds
+ * the count of threads in either of those states and a thread
+ * is considered the "last" thread if all of the other threads
+ * in a process are already terminating.
+ */
PROC_LOCK(p);
-
- if (p->p_numthreads != 1) {
- racct_sub(p, RACCT_NTHR, 1);
- LIST_REMOVE(td, td_hash);
- rw_wunlock(&tidhash_lock);
- tdsigcleanup(td);
- umtx_thread_exit(td);
- PROC_SLOCK(p);
- thread_stopped(p);
- thread_exit();
- /* NOTREACHED */
+ if (p->p_numthreads == p->p_pendingexits + 1) {
+ /*
+ * Ignore attempts to shut down last thread in the
+ * proc. This will actually call _exit(2) in the
+ * usermode trampoline when it returns.
+ */
+ PROC_UNLOCK(p);
+ return (0);
}
+ p->p_pendingexits++;
+ td->td_dbgflags |= TDB_EXIT;
+ if (p->p_ptevents & PTRACE_LWP)
+ ptracestop(td, SIGTRAP);
+ PROC_UNLOCK(p);
+ tidhash_remove(td);
+ PROC_LOCK(p);
+ p->p_pendingexits--;
+
/*
- * Ignore attempts to shut down last thread in the proc. This
- * will actually call _exit(2) in the usermode trampoline when
- * it returns.
+ * The check above should prevent all other threads from this
+ * process from exiting while the PROC_LOCK is dropped, so
+ * there must be at least one other thread other than the
+ * current thread.
*/
- PROC_UNLOCK(p);
- rw_wunlock(&tidhash_lock);
- return (0);
+ KASSERT(p->p_numthreads > 1, ("too few threads"));
+ racct_sub(p, RACCT_NTHR, 1);
+ tdsigcleanup(td);
+ umtx_thread_exit(td);
+ PROC_SLOCK(p);
+ thread_stopped(p);
+ thread_exit();
+ /* NOTREACHED */
}
int
OpenPOWER on IntegriCloud