Changed the team shutdown process a bit:

* The threads beside the main thread are killed earlier now (in the new
  team_shutdown_team()), before removing the team from the team hash and from
  its process group. This fixes #5296.
* Use a condition variable instead of a semaphore to wait for the non-main
  threads to die. We notify the condition right after a thread has left the
  team. The semaphore was released by the undertaker.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@35196 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2010-01-20 09:34:53 +00:00
parent 15779f70b9
commit 5662ae4585
5 changed files with 124 additions and 84 deletions
+19 -33
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2005-2009, Ingo Weinhold, [email protected].
* Copyright 2005-2010, Ingo Weinhold, [email protected].
* Copyright 2002-2009, Axel Dörfler, [email protected].
* Distributed under the terms of the MIT License.
*
@@ -78,13 +78,11 @@ static int32 sUsedThreads = 0;
struct UndertakerEntry : DoublyLinkedListLinkImpl<UndertakerEntry> {
struct thread* thread;
team_id teamID;
sem_id deathSem;
UndertakerEntry(struct thread* thread, team_id teamID, sem_id deathSem)
UndertakerEntry(struct thread* thread, team_id teamID)
:
thread(thread),
teamID(teamID),
deathSem(deathSem)
teamID(teamID)
{
}
};
@@ -486,8 +484,10 @@ create_thread(thread_creation_attributes& attributes, bool kernel)
// look at the team, make sure it's not being deleted
team = team_get_team_struct_locked(attributes.team);
if (team == NULL || team->state == TEAM_STATE_DEATH)
if (team == NULL || team->state == TEAM_STATE_DEATH
|| team->death_entry != NULL) {
abort = true;
}
if (!abort && !kernel) {
thread->user_thread = team_allocate_user_thread(team);
@@ -622,9 +622,6 @@ undertaker(void* /*args*/)
enable_interrupts();
// needed for the debugger notification below
if (entry.deathSem >= 0)
release_sem_etc(entry.deathSem, 1, B_DO_NOT_RESCHEDULE);
// free the thread structure
locker.Lock();
thread_enqueue(thread, &dead_q);
@@ -1368,8 +1365,6 @@ thread_exit(void)
struct thread *thread = thread_get_current_thread();
struct team *team = thread->team;
thread_id parentID = -1;
bool deleteTeam = false;
sem_id cachedDeathSem = -1;
status_t status;
struct thread_debug_info debugInfo;
team_id teamID = team->id;
@@ -1396,14 +1391,14 @@ thread_exit(void)
struct job_control_entry *death = NULL;
struct death_entry* threadDeathEntry = NULL;
ConditionVariableEntry waitForDebuggerEntry;
bool waitForDebugger = false;
bool deleteTeam = false;
port_id debuggerPort = -1;
if (team != team_get_kernel_team()) {
user_debug_thread_exiting(thread);
if (team->main_thread == thread) {
// this was the main thread in this team, so we will delete that as well
// The main thread is exiting. Shut down the whole team.
deleteTeam = true;
} else {
threadDeathEntry = (death_entry*)malloc(sizeof(death_entry));
@@ -1414,6 +1409,10 @@ thread_exit(void)
// put the thread into the kernel team until it dies
state = disable_interrupts();
GRAB_TEAM_LOCK();
if (deleteTeam)
debuggerPort = team_shutdown_team(team, state);
GRAB_THREAD_LOCK();
// removing the thread and putting its death entry to the parent
// team needs to be an atomic operation
@@ -1425,19 +1424,12 @@ thread_exit(void)
remove_thread_from_team(team, thread);
insert_thread_into_team(team_get_kernel_team(), thread);
cachedDeathSem = team->death_sem;
if (team->death_entry != NULL) {
if (--team->death_entry->remaining_threads == 0)
team->death_entry->condition.NotifyOne(true, B_OK);
}
if (deleteTeam) {
// If a debugger change is in progess for the team, we'll have to
// wait until it is done later.
GRAB_TEAM_DEBUG_INFO_LOCK(team->debug_info);
if (team->debug_info.debugger_changed_condition != NULL) {
team->debug_info.debugger_changed_condition->Add(
&waitForDebuggerEntry);
waitForDebugger = true;
}
RELEASE_TEAM_DEBUG_INFO_LOCK(team->debug_info);
struct team *parent = team->parent;
// remember who our parent was so we can send a signal
@@ -1508,17 +1500,11 @@ thread_exit(void)
// delete the team if we're its main thread
if (deleteTeam) {
// wait for a debugger change to be finished first
if (waitForDebugger)
waitForDebuggerEntry.Wait();
team_delete_team(team);
team_delete_team(team, debuggerPort);
// we need to delete any death entry that made it to here
if (death != NULL)
delete death;
cachedDeathSem = -1;
}
state = disable_interrupts();
@@ -1602,7 +1588,7 @@ thread_exit(void)
user_debug_thread_deleted(teamID, thread->id);
// enqueue in the undertaker list and reschedule for the last time
UndertakerEntry undertakerEntry(thread, teamID, cachedDeathSem);
UndertakerEntry undertakerEntry(thread, teamID);
disable_interrupts();
GRAB_THREAD_LOCK();