diff --git a/headers/private/kernel/team.h b/headers/private/kernel/team.h index 990d1befba..9e86229a0b 100644 --- a/headers/private/kernel/team.h +++ b/headers/private/kernel/team.h @@ -1,5 +1,5 @@ /* - * Copyright 2004-2005, Haiku Inc. + * Copyright 2004-2007, Haiku Inc. All Rights Reserved. * Distributed under the terms of the MIT License. */ #ifndef _TEAM_H @@ -28,6 +28,8 @@ team_id team_get_current_team_id(void); status_t team_get_address_space(team_id id, struct vm_address_space **_addressSpace); char **user_team_get_arguments(void); int user_team_get_arg_count(void); +status_t team_get_death_entry(struct team *team, thread_id child, + struct death_entry *death, struct death_entry **_freeDeath); bool team_is_valid(team_id id); struct team *team_get_team_struct_locked(team_id id); int32 team_max_teams(void); diff --git a/src/system/kernel/team.c b/src/system/kernel/team.c index 9fa455ba59..5ac18a2fc3 100644 --- a/src/system/kernel/team.c +++ b/src/system/kernel/team.c @@ -1289,37 +1289,6 @@ register_wait_for_any(struct team *team, thread_id child) } -static status_t -get_team_death_entry(struct team *team, thread_id child, struct death_entry *death, - struct death_entry **_freeDeath) -{ - struct death_entry *entry = NULL; - - // find matching death entry structure - - while ((entry = list_get_next_item(&team->dead_children.list, entry)) != NULL) { - if (child != -1 && entry->thread != child) - continue; - - // we found one - - *death = *entry; - - // only remove the death entry if there aren't any other interested parties - if ((child == -1 && atomic_add(&team->dead_children.wait_for_any, -1) == 1) - || (child != -1 && team->dead_children.wait_for_any == 0)) { - list_remove_link(entry); - team->dead_children.count--; - *_freeDeath = entry; - } - - return B_OK; - } - - return child > 0 ? B_BAD_THREAD_ID : B_WOULD_BLOCK; -} - - /** Gets the next pending death entry, if any. Also fills in \a _waitSem * to the semaphore the caller have to wait for for other death entries. * Must be called with the team lock held. @@ -1336,7 +1305,7 @@ get_death_entry(struct team *team, pid_t child, struct death_entry *death, // wait for any children or a specific child of this team to die *_waitSem = team->dead_children.sem; *_waitCount = &team->dead_children.waiters; - return get_team_death_entry(team, child, death, _freeDeath); + return team_get_death_entry(team, child, death, _freeDeath); } else if (child < 0) { // we wait for all children of the specified process group group = team_get_process_group_locked(team->group->session, -child); @@ -1348,7 +1317,7 @@ get_death_entry(struct team *team, pid_t child, struct death_entry *death, } for (team = group->teams; team; team = team->group_next) { - status = get_team_death_entry(team, -1, death, _freeDeath); + status = team_get_death_entry(team, -1, death, _freeDeath); if (status == B_OK) { atomic_add(&group->wait_for_any, -1); return B_OK; @@ -1568,6 +1537,41 @@ team_used_teams(void) } +/** Fills the provided death entry if it's in the team. + * You need to have the team lock held when calling this function. + */ + +status_t +team_get_death_entry(struct team *team, thread_id child, struct death_entry *death, + struct death_entry **_freeDeath) +{ + struct death_entry *entry = NULL; + + // find matching death entry structure + + while ((entry = list_get_next_item(&team->dead_children.list, entry)) != NULL) { + if (child != -1 && entry->thread != child) + continue; + + // we found one + + *death = *entry; + + // only remove the death entry if there aren't any other interested parties + if ((child == -1 && atomic_add(&team->dead_children.wait_for_any, -1) == 1) + || (child != -1 && team->dead_children.wait_for_any == 0)) { + list_remove_link(entry); + team->dead_children.count--; + *_freeDeath = entry; + } + + return B_OK; + } + + return child > 0 ? B_BAD_THREAD_ID : B_WOULD_BLOCK; +} + + /** Quick check to see if we have a valid team ID. */ diff --git a/src/system/kernel/thread.c b/src/system/kernel/thread.c index 67aa06c3be..1d7ff170ed 100644 --- a/src/system/kernel/thread.c +++ b/src/system/kernel/thread.c @@ -1300,15 +1300,15 @@ status_t wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t *_returnCode) { sem_id sem = B_BAD_THREAD_ID; - struct death_entry death; + struct death_entry death, *freeDeath = NULL; struct thread *thread; cpu_status state; - status_t status; + status_t status = B_OK; + + if (id < B_OK) + return B_BAD_THREAD_ID; // we need to resume the thread we're waiting for first - status = resume_thread(id); - if (status != B_OK) - return status; state = disable_interrupts(); GRAB_THREAD_LOCK(); @@ -1321,11 +1321,36 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t *_re } RELEASE_THREAD_LOCK(); + + if (thread == NULL) { + // we couldn't find this thread - maybe it's already gone, and we'll + // find its death entry + GRAB_TEAM_LOCK(); + + status = team_get_death_entry(thread_get_current_thread()->team, + id, &death, &freeDeath); + + RELEASE_TEAM_LOCK(); + } + restore_interrupts(state); + if (thread == NULL && status == B_OK) { + // we found the thread's death entry in our team + if (_returnCode) + *_returnCode = death.status; + + free(freeDeath); + return B_OK; + } + + // we need to wait for the death of the thread + if (sem < B_OK) return B_BAD_THREAD_ID; + resume_thread(id); + status = acquire_sem_etc(sem, 1, flags, timeout); if (status == B_OK) {