From 92ab20b3a4994eca38234479d344e80519019652 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Wed, 28 Nov 2007 00:07:32 +0000 Subject: [PATCH] Added a list of death_entry's to the teams structure. It stores the exit status of (non-main) threads of a team. Fixes bug #1644. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@23009 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/thread_types.h | 6 ++- src/system/kernel/team.cpp | 9 ++++ src/system/kernel/thread.cpp | 59 ++++++++++++++++++++++++--- 3 files changed, 67 insertions(+), 7 deletions(-) diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index 22af8d2cb5..b276728029 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -94,7 +94,9 @@ struct team_watcher { }; #define MAX_DEAD_CHILDREN 32 - // this is a soft limit for the number of dead entries in a team + // this is a soft limit for the number of child death entries in a team +#define MAX_DEAD_THREADS 32 + // this is a soft limit for the number of thread death entries in a team typedef struct team_dead_children team_dead_children; typedef struct team_job_control_children team_job_control_children; @@ -155,6 +157,8 @@ struct team { int pending_signals; void *io_context; sem_id death_sem; // semaphore to wait on for dying threads + struct list dead_threads; + int dead_threads_count; team_dead_children *dead_children; team_job_control_children *stopped_children; diff --git a/src/system/kernel/team.cpp b/src/system/kernel/team.cpp index 5534473e49..72735bf181 100644 --- a/src/system/kernel/team.cpp +++ b/src/system/kernel/team.cpp @@ -567,6 +567,10 @@ create_team_struct(const char *name, bool kernel) team->dead_threads_kernel_time = 0; team->dead_threads_user_time = 0; + // dead threads + list_init(&team->dead_threads); + team->dead_threads_count = 0; + // dead children team->dead_children = new(nothrow) team_dead_children; if (team->dead_children == NULL) @@ -636,6 +640,11 @@ delete_team_struct(struct team *team) team->dead_children->condition_variable.Unpublish(); + while (death_entry* threadDeathEntry = (death_entry*)list_remove_head_item( + &team->dead_threads)) { + free(threadDeathEntry); + } + while (job_control_entry* entry = team->dead_children->entries.RemoveHead()) delete entry; diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index 79b043e782..29e829fc12 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -1215,11 +1215,14 @@ thread_exit(void) } struct job_control_entry *death = NULL; + struct death_entry* threadDeathEntry = NULL; + if (team != team_get_kernel_team()) { if (team->main_thread == thread) { // this was the main thread in this team, so we will delete that as well deleteTeam = true; - } + } else + threadDeathEntry = (death_entry*)malloc(sizeof(death_entry)); // remove this thread from the current team and add it to the kernel // put the thread into the kernel team until it dies @@ -1272,8 +1275,30 @@ thread_exit(void) RELEASE_THREAD_LOCK(); team_remove_team(team, &freeGroup); - } else + } else { + // The thread is not the main thread. We store a thread death + // entry for it, unless someone is already waiting it. + if (threadDeathEntry != NULL + && list_is_empty(&thread->exit.waiters)) { + threadDeathEntry->thread = thread->id; + threadDeathEntry->status = thread->exit.status; + threadDeathEntry->reason = thread->exit.reason; + threadDeathEntry->signal = thread->exit.signal; + + // add entry -- remove and old one, if we hit the limit + list_add_item(&team->dead_threads, threadDeathEntry); + team->dead_threads_count++; + threadDeathEntry = NULL; + + if (team->dead_threads_count > MAX_DEAD_THREADS) { + threadDeathEntry = (death_entry*)list_remove_head_item( + &team->dead_threads); + team->dead_threads_count--; + } + } + RELEASE_THREAD_LOCK(); + } RELEASE_TEAM_LOCK(); @@ -1284,6 +1309,9 @@ thread_exit(void) TRACE(("thread_exit: thread %ld now a kernel thread!\n", thread->id)); } + if (threadDeathEntry != NULL) + free(threadDeathEntry); + // delete the team if we're its main thread if (deleteTeam) { team_delete_process_group(freeGroup); @@ -1640,6 +1668,8 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, list_add_link_to_head(&thread->exit.waiters, &death); } + death_entry* threadDeathEntry = NULL; + RELEASE_THREAD_LOCK(); if (thread == NULL) { @@ -1647,15 +1677,31 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, // find its death entry in our team GRAB_TEAM_LOCK(); + struct team* team = thread_get_current_thread()->team; + + // check the child death entries first (i.e. main threads of child + // teams) bool deleteEntry; - freeDeath = team_get_death_entry(thread_get_current_thread()->team, id, - &deleteEntry); + freeDeath = team_get_death_entry(team, id, &deleteEntry); if (freeDeath != NULL) { death.status = freeDeath->status; if (!deleteEntry) freeDeath = NULL; - } else - status = B_BAD_THREAD_ID; + } else { + // check the thread death entries of the team (non-main threads) + while ((threadDeathEntry = (death_entry*)list_get_next_item( + &team->dead_threads, threadDeathEntry)) != NULL) { + if (threadDeathEntry->thread == id) { + list_remove_item(&team->dead_threads, threadDeathEntry); + team->dead_threads_count--; + death.status = threadDeathEntry->status; + break; + } + } + + if (threadDeathEntry == NULL) + status = B_BAD_THREAD_ID; + } RELEASE_TEAM_LOCK(); } @@ -1668,6 +1714,7 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, *_returnCode = death.status; delete freeDeath; + free(threadDeathEntry); return B_OK; }