There was a race condition between removing the thread from the hash and putting

its death entry into the parent team's queue - we need to do this atomically.
As an effect, wait_for_thread() sometimes failed with B_BAD_THREAD_ID.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@19808 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2007-01-15 12:41:08 +00:00
parent 7383c05e71
commit 6f19c1db2d
1 changed files with 26 additions and 6 deletions

View File

@ -925,15 +925,12 @@ thread_exit(void)
// boost our priority to get this over with // boost our priority to get this over with
thread->priority = thread->next_priority = B_URGENT_DISPLAY_PRIORITY; thread->priority = thread->next_priority = B_URGENT_DISPLAY_PRIORITY;
// Stop debugging for this thread, and remove it from the hashtable, so it // Stop debugging for this thread
// cannot be found afterwards anymore
state = disable_interrupts(); state = disable_interrupts();
GRAB_THREAD_LOCK(); GRAB_THREAD_LOCK();
debugInfo = thread->debug_info; debugInfo = thread->debug_info;
clear_thread_debug_info(&thread->debug_info, true); clear_thread_debug_info(&thread->debug_info, true);
hash_remove(sThreadHash, thread);
sUsedThreads--;
RELEASE_THREAD_LOCK(); RELEASE_THREAD_LOCK();
restore_interrupts(state); restore_interrupts(state);
@ -987,6 +984,9 @@ thread_exit(void)
// put the thread into the kernel team until it dies // put the thread into the kernel team until it dies
state = disable_interrupts(); state = disable_interrupts();
GRAB_TEAM_LOCK(); GRAB_TEAM_LOCK();
GRAB_THREAD_LOCK();
// removing the thread and putting its death entry to the parent
// team needs to be an atomic operation
// remember how long this thread lasted // remember how long this thread lasted
team->dead_threads_kernel_time += thread->kernel_time; team->dead_threads_kernel_time += thread->kernel_time;
@ -997,6 +997,10 @@ thread_exit(void)
cachedDeathSem = team->death_sem; cachedDeathSem = team->death_sem;
// remove thread from hash, so it's no longer accessible
hash_remove(sThreadHash, thread);
sUsedThreads--;
if (deleteTeam) { if (deleteTeam) {
struct team *parent = team->parent; struct team *parent = team->parent;
@ -1013,6 +1017,8 @@ thread_exit(void)
} else } else
death = NULL; death = NULL;
RELEASE_THREAD_LOCK();
// notify listeners that a new death entry is available // notify listeners that a new death entry is available
// TODO: should that be moved to handle_signal() (for SIGCHLD)? // TODO: should that be moved to handle_signal() (for SIGCHLD)?
release_sem_etc(parent->dead_children.sem, release_sem_etc(parent->dead_children.sem,
@ -1022,16 +1028,30 @@ thread_exit(void)
parent->dead_children.waiters = 0; parent->dead_children.waiters = 0;
team->group->dead_child_waiters = 0; team->group->dead_child_waiters = 0;
} } else
RELEASE_THREAD_LOCK();
team_remove_team(team, &freeGroup); team_remove_team(team, &freeGroup);
} } else
RELEASE_THREAD_LOCK();
RELEASE_TEAM_LOCK(); RELEASE_TEAM_LOCK();
// swap address spaces, to make sure we're running on the kernel's pgdir // swap address spaces, to make sure we're running on the kernel's pgdir
vm_swap_address_space(vm_kernel_address_space()); vm_swap_address_space(vm_kernel_address_space());
restore_interrupts(state); restore_interrupts(state);
TRACE(("thread_exit: thread 0x%lx now a kernel thread!\n", thread->id)); TRACE(("thread_exit: thread 0x%lx now a kernel thread!\n", thread->id));
} else {
// for kernel threads, we don't need to care about their death entries
state = disable_interrupts();
GRAB_THREAD_LOCK();
hash_remove(sThreadHash, thread);
sUsedThreads--;
RELEASE_THREAD_LOCK();
restore_interrupts(state);
} }
// delete the team if we're its main thread // delete the team if we're its main thread