Big update, but everything is related:
- we now have a working process group/session model. Implemented related syscalls and the kernel backend, some POSIX process functions are now available in the kernel as well. - dying teams now monitor their exitus by leaving death_entry structures in their parent team. - Implemented wait_for_child() for pid == -1 and pid > 0; IOW waiting for process groups is missing yet. Hasn't yet been tested intensively. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@9350 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
f197107a3d
commit
6a70123a9d
@ -19,8 +19,10 @@
|
||||
#include <kimage.h>
|
||||
#include <elf.h>
|
||||
#include <syscalls.h>
|
||||
#include <syscall_process_info.h>
|
||||
#include <tls.h>
|
||||
|
||||
#include <sys/wait.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@ -54,6 +56,7 @@ struct fork_arg {
|
||||
struct arch_fork_arg arch_info;
|
||||
};
|
||||
|
||||
|
||||
// team list
|
||||
static void *team_hash = NULL;
|
||||
static team_id next_team_id = 1;
|
||||
@ -61,6 +64,10 @@ static struct team *kernel_team = NULL;
|
||||
|
||||
spinlock team_spinlock = 0;
|
||||
|
||||
static void insert_group_into_session(struct process_session *session, struct process_group *group);
|
||||
static void insert_team_into_group(struct process_group *group, struct team *team);
|
||||
static struct process_session *create_process_session(pid_t id);
|
||||
static struct process_group *create_process_group(pid_t id);
|
||||
static struct team *create_team_struct(const char *name, bool kernel);
|
||||
static void delete_team_struct(struct team *p);
|
||||
static int team_struct_compare(void *_p, const void *_key);
|
||||
@ -133,22 +140,37 @@ dump_team_info(int argc, char **argv)
|
||||
int
|
||||
team_init(kernel_args *ka)
|
||||
{
|
||||
struct process_session *session;
|
||||
struct process_group *group;
|
||||
|
||||
// create the team hash table
|
||||
team_hash = hash_init(15, (addr)&kernel_team->next - (addr)kernel_team,
|
||||
&team_struct_compare, &team_struct_hash);
|
||||
|
||||
// create initial session and process groups
|
||||
|
||||
session = create_process_session(1);
|
||||
if (session == NULL)
|
||||
panic("Could not create initial session.\n");
|
||||
|
||||
group = create_process_group(1);
|
||||
if (group == NULL)
|
||||
panic("Could not create initial process group.\n");
|
||||
|
||||
insert_group_into_session(session, group);
|
||||
|
||||
// create the kernel team
|
||||
kernel_team = create_team_struct("kernel_team", true);
|
||||
if (kernel_team == NULL)
|
||||
panic("could not create kernel team!\n");
|
||||
kernel_team->state = TEAM_STATE_NORMAL;
|
||||
|
||||
insert_team_into_group(group, kernel_team);
|
||||
|
||||
kernel_team->io_context = vfs_new_io_context(NULL);
|
||||
if (kernel_team->io_context == NULL)
|
||||
panic("could not create io_context for kernel team!\n");
|
||||
|
||||
//XXX should initialize kernel_team->path here. Set it to "/"?
|
||||
|
||||
// stick it in the team hash
|
||||
hash_insert(team_hash, kernel_team);
|
||||
|
||||
@ -346,6 +368,163 @@ reparent_children(struct team *team)
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
is_process_group_leader(struct team *team)
|
||||
{
|
||||
return team->group_id == team->main_thread->id;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
insert_group_into_session(struct process_session *session, struct process_group *group)
|
||||
{
|
||||
if (group == NULL)
|
||||
return;
|
||||
|
||||
group->session = session;
|
||||
list_add_link_to_tail(&session->groups, group);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
insert_team_into_group(struct process_group *group, struct team *team)
|
||||
{
|
||||
team->group = group;
|
||||
team->group_id = group->id;
|
||||
team->session_id = group->session->id;
|
||||
|
||||
team->group_next = group->teams;
|
||||
group->teams = team;
|
||||
}
|
||||
|
||||
|
||||
/** Removes a group from a session, and puts the session object
|
||||
* back into the session cache, if it's not used anymore.
|
||||
* You must hold the team lock when calling this function.
|
||||
*/
|
||||
|
||||
static void
|
||||
remove_group_from_session(struct process_group *group)
|
||||
{
|
||||
struct process_session *session = group->session;
|
||||
|
||||
// the group must be in any session to let this function have any effect
|
||||
if (session == NULL)
|
||||
return;
|
||||
|
||||
list_remove_link(group);
|
||||
|
||||
// we cannot free the resource here, so we're keeping the group link
|
||||
// around - this way it'll be freed by free_process_group()
|
||||
if (!list_is_empty(&session->groups))
|
||||
group->session = NULL;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
team_delete_process_group(struct process_group *group)
|
||||
{
|
||||
if (group == NULL)
|
||||
return;
|
||||
|
||||
TRACE(("team_delete_process_group(id = %ld)\n", group->id));
|
||||
|
||||
delete_sem(group->dead_child_sem);
|
||||
|
||||
// remove_group_from_session() keeps this pointer around
|
||||
// only if the session can be freed as well
|
||||
if (group->session) {
|
||||
TRACE(("team_delete_process_group(): frees session %ld\n", group->session->id));
|
||||
free(group->session);
|
||||
}
|
||||
|
||||
free(group);
|
||||
}
|
||||
|
||||
|
||||
/** Removes the team from the group. If that group becomes therefore
|
||||
* unused, it will set \a _freeGroup to point to the group - otherwise
|
||||
* it will be \c NULL.
|
||||
* It cannot be freed here because this function has to be called
|
||||
* with having the team lock held.
|
||||
*
|
||||
* \param team the team that'll be removed from it's group
|
||||
* \param _freeGroup points to the group to be freed or NULL
|
||||
*/
|
||||
|
||||
static void
|
||||
remove_team_from_group(struct team *team, struct process_group **_freeGroup)
|
||||
{
|
||||
struct process_group *group = team->group;
|
||||
struct team *current, *last = NULL;
|
||||
|
||||
*_freeGroup = NULL;
|
||||
|
||||
// the team must be in any team to let this function have any effect
|
||||
if (group == NULL)
|
||||
return;
|
||||
|
||||
for (current = group->teams; current != NULL; current = current->group_next) {
|
||||
if (current == team) {
|
||||
if (last == NULL)
|
||||
group->teams = current->group_next;
|
||||
else
|
||||
last->group_next = current->group_next;
|
||||
|
||||
team->group = NULL;
|
||||
break;
|
||||
}
|
||||
last = current;
|
||||
}
|
||||
|
||||
team->group = NULL;
|
||||
team->group_next = NULL;
|
||||
|
||||
if (group->teams != NULL)
|
||||
return;
|
||||
|
||||
// we can remove this group as it is no longer used
|
||||
|
||||
remove_group_from_session(group);
|
||||
*_freeGroup = group;
|
||||
}
|
||||
|
||||
|
||||
static struct process_group *
|
||||
create_process_group(pid_t id)
|
||||
{
|
||||
struct process_group *group = (struct process_group *)malloc(sizeof(struct process_group));
|
||||
if (group == NULL)
|
||||
return NULL;
|
||||
|
||||
group->dead_child_sem = create_sem(0, "dead group children");
|
||||
if (group->dead_child_sem < B_OK) {
|
||||
free(group);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
group->id = id;
|
||||
group->session = NULL;
|
||||
group->teams = NULL;
|
||||
|
||||
return group;
|
||||
}
|
||||
|
||||
|
||||
static struct process_session *
|
||||
create_process_session(pid_t id)
|
||||
{
|
||||
struct process_session *session = (struct process_session *)malloc(sizeof(struct process_session));
|
||||
if (session == NULL)
|
||||
return NULL;
|
||||
|
||||
session->id = id;
|
||||
list_init(&session->groups);
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
|
||||
struct team *
|
||||
team_get_kernel_team(void)
|
||||
{
|
||||
@ -425,14 +604,24 @@ create_team_struct(const char *name, bool kernel)
|
||||
team->pending_signals = 0;
|
||||
team->death_sem = -1;
|
||||
team->user_env_base = 0;
|
||||
|
||||
list_init(&team->dead_children.list);
|
||||
team->dead_children.count = 0;
|
||||
team->dead_children.wait_for_any = 0;
|
||||
team->dead_children.sem = create_sem(0, "dead children");
|
||||
if (team->dead_children.sem < B_OK)
|
||||
goto error1;
|
||||
|
||||
list_init(&team->image_list);
|
||||
|
||||
if (arch_team_init_team_struct(team, kernel) < 0)
|
||||
goto error;
|
||||
goto error2;
|
||||
|
||||
return team;
|
||||
|
||||
error:
|
||||
error2:
|
||||
delete_sem(team->dead_children.sem);
|
||||
error1:
|
||||
free(team);
|
||||
return NULL;
|
||||
}
|
||||
@ -441,12 +630,26 @@ error:
|
||||
static void
|
||||
delete_team_struct(struct team *team)
|
||||
{
|
||||
struct death_entry *death = NULL;
|
||||
|
||||
delete_sem(team->dead_children.sem);
|
||||
|
||||
while ((death = list_get_next_item(&team->dead_children.list, death)) != NULL)
|
||||
free(death);
|
||||
|
||||
free(team);
|
||||
}
|
||||
|
||||
|
||||
/** Removes the specified team from the global team hash, and from its parent.
|
||||
* It also moves all of its children up to the parent.
|
||||
* You must hold the team lock when you call this function.
|
||||
* If \a _freeGroup is set to a value other than \c NULL, it must be freed
|
||||
* from the calling function.
|
||||
*/
|
||||
|
||||
void
|
||||
team_remove_team(struct team *team)
|
||||
team_remove_team(struct team *team, struct process_group **_freeGroup)
|
||||
{
|
||||
hash_remove(team_hash, team);
|
||||
team->state = TEAM_STATE_DEATH;
|
||||
@ -454,6 +657,9 @@ team_remove_team(struct team *team)
|
||||
// reparent each of the team's children
|
||||
reparent_children(team);
|
||||
|
||||
// remove us from our process group
|
||||
remove_team_from_group(team, _freeGroup);
|
||||
|
||||
// remove us from our parent
|
||||
remove_team_from_parent(team->parent, team);
|
||||
}
|
||||
@ -465,10 +671,10 @@ team_delete_team(struct team *team)
|
||||
if (team->num_threads > 0) {
|
||||
// there are other threads still in this team,
|
||||
// cycle through and signal kill on each of the threads
|
||||
// XXX this can be optimized. There's got to be a better solution.
|
||||
cpu_status state;
|
||||
// ToDo: this can be optimized. There's got to be a better solution.
|
||||
struct thread *temp_thread;
|
||||
char death_sem_name[B_OS_NAME_LENGTH];
|
||||
cpu_status state;
|
||||
|
||||
sprintf(death_sem_name, "team %ld death sem", team->id);
|
||||
team->death_sem = create_sem(0, death_sem_name);
|
||||
@ -667,6 +873,7 @@ team_create_thread_start(void *args)
|
||||
team_id
|
||||
team_create_team(const char *path, const char *name, char **args, int argc, char **env, int envCount, int priority)
|
||||
{
|
||||
struct process_group *group;
|
||||
struct team *team, *parent;
|
||||
const char *threadName;
|
||||
thread_id tid;
|
||||
@ -690,6 +897,7 @@ team_create_team(const char *path, const char *name, char **args, int argc, char
|
||||
|
||||
hash_insert(team_hash, team);
|
||||
insert_team_into_parent(parent, team);
|
||||
insert_team_into_group(parent->group, team);
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
@ -742,11 +950,14 @@ err1:
|
||||
state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
remove_team_from_group(team, &group);
|
||||
remove_team_from_parent(parent, team);
|
||||
hash_remove(team_hash, team);
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
team_delete_process_group(group);
|
||||
delete_team_struct(team);
|
||||
|
||||
return err;
|
||||
@ -947,6 +1158,36 @@ err1:
|
||||
}
|
||||
|
||||
|
||||
static status_t
|
||||
get_death_entry(struct team *team, thread_id child, struct death_entry *death)
|
||||
{
|
||||
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--;
|
||||
free(entry);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return child > 0 ? B_BAD_THREAD_ID : B_WOULD_BLOCK;
|
||||
}
|
||||
|
||||
|
||||
/** This is the kernel backend for waitpid(). It is a bit more powerful when it comes
|
||||
* to the reason why a thread has died than waitpid() can be.
|
||||
*/
|
||||
@ -955,21 +1196,65 @@ err1:
|
||||
static thread_id
|
||||
wait_for_child(thread_id child, uint32 flags, int32 *_reason, status_t *_returnCode)
|
||||
{
|
||||
// ToDo: implement me! We need to store the death of children in the team structure!
|
||||
struct team *team = thread_get_current_thread()->team;
|
||||
struct death_entry death;
|
||||
status_t status = B_OK;
|
||||
cpu_status state;
|
||||
|
||||
dprintf("wait_for_child(child = %ld, flags = %lu) is not yet implemented\n", child, flags);
|
||||
TRACE(("wait_for_child(child = %ld, flags = %ld)\n", child, flags));
|
||||
|
||||
if (child > 0) {
|
||||
// wait for the specified child
|
||||
} else if (child == -1) {
|
||||
// wait for any children of this team to die
|
||||
} else if (child == 0) {
|
||||
// wait for any children of this process group to die
|
||||
} else {
|
||||
// wait for any children with progress group of the absolute value of "child"
|
||||
if (child == 0 || child < -1) {
|
||||
dprintf("wait_for_child() process group ID waiting not yet implemented!\n");
|
||||
return EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
if (child == -1) {
|
||||
// we need to make sure the death entries won't get deleted too soon
|
||||
atomic_add(&team->dead_children.wait_for_any, 1);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (child > 0) {
|
||||
// wait for the specified child
|
||||
|
||||
if (thread_get_thread_struct(child) != NULL) {
|
||||
// team is still running, so we would need to block
|
||||
return B_WOULD_BLOCK;
|
||||
}
|
||||
} else if (child == -1) {
|
||||
// wait for any children of this team to die
|
||||
} else if (child == 0) {
|
||||
// wait for any children of this process group to die
|
||||
} else {
|
||||
// wait for any children with progress group of the absolute value of "child"
|
||||
}
|
||||
|
||||
// see if there is any death entry for us already
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
status = get_death_entry(team, child, &death);
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (status == B_BAD_THREAD_ID)
|
||||
return B_BAD_THREAD_ID;
|
||||
|
||||
if ((flags & WNOHANG) != 0)
|
||||
return B_WOULD_BLOCK;
|
||||
|
||||
status = acquire_sem(team->dead_children.sem);
|
||||
if (status == B_INTERRUPTED)
|
||||
return B_INTERRUPTED;
|
||||
}
|
||||
|
||||
// when we got here, we have a valid death entry
|
||||
*_returnCode = death.status;
|
||||
*_reason = death.reason;
|
||||
|
||||
return death.thread;
|
||||
}
|
||||
|
||||
|
||||
@ -1124,6 +1409,84 @@ err:
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
getpid(void)
|
||||
{
|
||||
return thread_get_current_thread()->team->main_thread->id;
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
getppid(void)
|
||||
{
|
||||
struct team *team = thread_get_current_thread()->team;
|
||||
cpu_status state;
|
||||
pid_t parent;
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
parent = team->parent->main_thread->id;
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
getpgid(pid_t process)
|
||||
{
|
||||
struct thread *thread;
|
||||
pid_t result = -1;
|
||||
cpu_status state;
|
||||
|
||||
if (process == 0)
|
||||
process = thread_get_current_thread()->team->main_thread->id;
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
thread = thread_get_thread_struct_locked(process);
|
||||
if (thread != NULL)
|
||||
result = thread->team->group_id;
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
return thread != NULL ? result : B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
getsid(pid_t process)
|
||||
{
|
||||
struct thread *thread;
|
||||
pid_t result = -1;
|
||||
cpu_status state;
|
||||
|
||||
if (process == 0)
|
||||
process = thread_get_current_thread()->team->main_thread->id;
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
thread = thread_get_thread_struct_locked(process);
|
||||
if (thread != NULL)
|
||||
result = thread->team->session_id;
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
return thread != NULL ? result : B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark -
|
||||
// syscalls
|
||||
|
||||
|
||||
int
|
||||
sys_setenv(const char *name, const char *value, int overwrite)
|
||||
{
|
||||
@ -1310,6 +1673,182 @@ _user_wait_for_child(thread_id child, uint32 flags, int32 *_userReason, status_t
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
_user_process_info(pid_t process, int32 which)
|
||||
{
|
||||
// we only allow to return the parent of the current process
|
||||
if (which == PARENT_ID
|
||||
&& process != 0 && process != thread_get_current_thread()->team->main_thread->id)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
switch (which) {
|
||||
case SESSION_ID:
|
||||
return getsid(process);
|
||||
case GROUP_ID:
|
||||
return getpgid(process);
|
||||
case PARENT_ID:
|
||||
return getppid();
|
||||
}
|
||||
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
_user_setpgid(pid_t processID, pid_t groupID)
|
||||
{
|
||||
struct team *currentTeam = thread_get_current_thread()->team;
|
||||
struct process_group *group = NULL, *freeGroup = NULL;
|
||||
struct thread *thread;
|
||||
struct team *team;
|
||||
cpu_status state;
|
||||
team_id teamID = -1;
|
||||
status_t status = B_OK;
|
||||
|
||||
if (groupID < 0)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
if (processID == 0) {
|
||||
// get our own process ID
|
||||
processID = currentTeam->main_thread->id;
|
||||
teamID = currentTeam->id;
|
||||
|
||||
// we must not change our process group ID if we're a group leader
|
||||
if (is_process_group_leader(currentTeam))
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
status = B_OK;
|
||||
} else {
|
||||
state = disable_interrupts();
|
||||
GRAB_THREAD_LOCK();
|
||||
|
||||
thread = thread_get_thread_struct_locked(processID);
|
||||
|
||||
// the thread must be the team's main thread, as that
|
||||
// determines its process ID
|
||||
if (thread != NULL && thread == thread->team->main_thread) {
|
||||
// check if the thread is in a child team of the calling team and
|
||||
// if it's already a process group leader and in the same session
|
||||
if (thread->team->parent != currentTeam
|
||||
|| is_process_group_leader(thread->team)
|
||||
|| thread->team->session_id != currentTeam->session_id)
|
||||
status = B_NOT_ALLOWED;
|
||||
else
|
||||
teamID = thread->team->id;
|
||||
} else
|
||||
status = B_BAD_THREAD_ID;
|
||||
|
||||
RELEASE_THREAD_LOCK();
|
||||
restore_interrupts(state);
|
||||
}
|
||||
|
||||
if (status != B_OK)
|
||||
return status;
|
||||
|
||||
// if the group ID is not specified, a new group should be created
|
||||
if (groupID == 0)
|
||||
groupID = processID;
|
||||
|
||||
if (groupID == processID) {
|
||||
// We need to create a new process group for this team
|
||||
group = create_process_group(groupID);
|
||||
if (group == NULL)
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
team = team_get_team_struct_locked(teamID);
|
||||
if (team != NULL) {
|
||||
if (processID == groupID) {
|
||||
// we created a new process group, let us insert it into the team's session
|
||||
insert_group_into_session(team->group->session, group);
|
||||
remove_team_from_group(team, &freeGroup);
|
||||
insert_team_into_group(group, team);
|
||||
} else {
|
||||
struct process_session *session = team->group->session;
|
||||
struct process_group *group = NULL;
|
||||
|
||||
// check if this team can have the group ID; there must be one matching
|
||||
// process ID in the team's session
|
||||
|
||||
while ((group = list_get_next_item(&session->groups, group)) != NULL) {
|
||||
if (group->id == groupID)
|
||||
break;
|
||||
}
|
||||
|
||||
if (group) {
|
||||
// we got a group, let's move the team there
|
||||
remove_team_from_group(team, &freeGroup);
|
||||
insert_team_into_group(group, team);
|
||||
} else
|
||||
status = B_NOT_ALLOWED;
|
||||
}
|
||||
} else
|
||||
status = B_NOT_ALLOWED;
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (status != B_OK && group != NULL)
|
||||
team_delete_process_group(group);
|
||||
|
||||
team_delete_process_group(freeGroup);
|
||||
|
||||
return status == B_OK ? groupID : status;
|
||||
}
|
||||
|
||||
|
||||
pid_t
|
||||
_user_setsid(void)
|
||||
{
|
||||
struct team *team = thread_get_current_thread()->team;
|
||||
struct process_session *session;
|
||||
struct process_group *group, *freeGroup = NULL;
|
||||
cpu_status state;
|
||||
bool failed = false;
|
||||
|
||||
// the team must not already be a process group leader
|
||||
if (is_process_group_leader(team))
|
||||
return B_NOT_ALLOWED;
|
||||
|
||||
group = create_process_group(team->main_thread->id);
|
||||
if (group == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
session = create_process_session(group->id);
|
||||
if (session == NULL) {
|
||||
team_delete_process_group(group);
|
||||
return B_NO_MEMORY;
|
||||
}
|
||||
|
||||
state = disable_interrupts();
|
||||
GRAB_TEAM_LOCK();
|
||||
|
||||
// this may have changed since the check above
|
||||
if (!is_process_group_leader(team)) {
|
||||
remove_team_from_group(team, &freeGroup);
|
||||
|
||||
insert_group_into_session(session, group);
|
||||
insert_team_into_group(group, team);
|
||||
} else
|
||||
failed = true;
|
||||
|
||||
RELEASE_TEAM_LOCK();
|
||||
restore_interrupts(state);
|
||||
|
||||
if (failed) {
|
||||
team_delete_process_group(group);
|
||||
free(session);
|
||||
return B_NOT_ALLOWED;
|
||||
} else
|
||||
team_delete_process_group(freeGroup);
|
||||
|
||||
return team->group_id;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_user_wait_for_team(team_id id, status_t *_userReturnCode)
|
||||
{
|
||||
|
@ -1,13 +1,13 @@
|
||||
/* Threading routines */
|
||||
|
||||
/*
|
||||
** Copyright 2002-2004, The OpenBeOS Team. All rights reserved.
|
||||
** Distributed under the terms of the OpenBeOS License.
|
||||
** Copyright 2002-2004, The Haiku Team. All rights reserved.
|
||||
** Distributed under the terms of the Haiku License.
|
||||
**
|
||||
** Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
|
||||
** Distributed under the terms of the NewOS License.
|
||||
*/
|
||||
|
||||
/* Threading routines */
|
||||
|
||||
#include <OS.h>
|
||||
|
||||
#include <thread.h>
|
||||
@ -75,6 +75,10 @@ static void thread_kthread_entry(void);
|
||||
static void thread_kthread_exit(void);
|
||||
|
||||
|
||||
/** Inserts a thread into a team.
|
||||
* You must hold the team lock when you call this function.
|
||||
*/
|
||||
|
||||
static void
|
||||
insert_thread_into_team(struct team *p, struct thread *t)
|
||||
{
|
||||
@ -89,6 +93,10 @@ insert_thread_into_team(struct team *p, struct thread *t)
|
||||
}
|
||||
|
||||
|
||||
/** Removes a thread from a team.
|
||||
* You must hold the team lock when you call this function.
|
||||
*/
|
||||
|
||||
static void
|
||||
remove_thread_from_team(struct team *p, struct thread *t)
|
||||
{
|
||||
@ -430,7 +438,7 @@ _dump_thread_info(struct thread *t)
|
||||
dprintf("name: '%s'\n", t->name);
|
||||
dprintf("all_next: %p\nteam_next: %p\nq_next: %p\n",
|
||||
t->all_next, t->team_next, t->queue_next);
|
||||
dprintf("priority: 0x%x\n", t->priority);
|
||||
dprintf("priority: 0x%lx\n", t->priority);
|
||||
dprintf("state: %s\n", state_to_text(t->state));
|
||||
dprintf("next_state: %s\n", state_to_text(t->next_state));
|
||||
dprintf("cpu: %p ", t->cpu);
|
||||
@ -441,9 +449,9 @@ _dump_thread_info(struct thread *t)
|
||||
dprintf("sig_pending: 0x%lx\n", t->sig_pending);
|
||||
dprintf("in_kernel: %d\n", t->in_kernel);
|
||||
dprintf("sem.blocking: 0x%lx\n", t->sem.blocking);
|
||||
dprintf("sem.count: 0x%x\n", t->sem.count);
|
||||
dprintf("sem.acquire_status: 0x%x\n", t->sem.acquire_status);
|
||||
dprintf("sem.flags: 0x%x\n", t->sem.flags);
|
||||
dprintf("sem.count: 0x%lx\n", t->sem.count);
|
||||
dprintf("sem.acquire_status: 0x%lx\n", t->sem.acquire_status);
|
||||
dprintf("sem.flags: 0x%lx\n", t->sem.flags);
|
||||
dprintf("fault_handler: %p\n", (void *)t->fault_handler);
|
||||
dprintf("args: %p %p\n", t->args1, t->args2);
|
||||
dprintf("entry: %p\n", (void *)t->entry);
|
||||
@ -710,11 +718,13 @@ thread_exit(void)
|
||||
{
|
||||
cpu_status state;
|
||||
struct thread *thread = thread_get_current_thread();
|
||||
struct process_group *freeGroup = NULL;
|
||||
struct team *team = thread->team;
|
||||
struct death_entry *death = NULL;
|
||||
thread_id mainParentThread = -1;
|
||||
bool deleteTeam = false;
|
||||
uint32 death_stack;
|
||||
sem_id cached_death_sem;
|
||||
sem_id cachedDeathSem, parentDeadSem = -1, groupDeadSem = -1;
|
||||
status_t status;
|
||||
|
||||
if (!are_interrupts_enabled())
|
||||
@ -755,6 +765,20 @@ thread_exit(void)
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// put a death entry into the dead children list of our parent
|
||||
death = (struct death_entry *)malloc(sizeof(struct death_entry));
|
||||
if (death != NULL) {
|
||||
death->team = team->id;
|
||||
death->thread = thread->id;
|
||||
death->status = thread->exit.status;
|
||||
death->reason = thread->exit.reason;
|
||||
}
|
||||
}
|
||||
|
||||
// remove this thread from the current team and add it to the kernel
|
||||
// put the thread into the kernel team until it dies
|
||||
state = disable_interrupts();
|
||||
@ -763,14 +787,27 @@ thread_exit(void)
|
||||
remove_thread_from_team(team, thread);
|
||||
insert_thread_into_team(team_get_kernel_team(), thread);
|
||||
|
||||
if (team->main_thread == thread) {
|
||||
// this was main thread in this team
|
||||
deleteTeam = true;
|
||||
if (deleteTeam) {
|
||||
struct team *parent = team->parent;
|
||||
|
||||
// remember who our parent was so we can send a signal
|
||||
mainParentThread = team->parent->main_thread->id;
|
||||
mainParentThread = parent->main_thread->id;
|
||||
|
||||
team_remove_team(team);
|
||||
if (death != NULL) {
|
||||
// insert death entry into the parent's list
|
||||
|
||||
list_add_link_to_tail(&parent->dead_children.list, death);
|
||||
if (++parent->dead_children.count > MAX_DEAD_CHILDREN) {
|
||||
death = list_remove_head_item(&parent->dead_children.list);
|
||||
parent->dead_children.count--;
|
||||
} else
|
||||
death = NULL;
|
||||
|
||||
parentDeadSem = parent->dead_children.sem;
|
||||
groupDeadSem = team->group->dead_child_sem;
|
||||
}
|
||||
|
||||
team_remove_team(team, &freeGroup);
|
||||
}
|
||||
RELEASE_TEAM_LOCK();
|
||||
// swap address spaces, to make sure we're running on the kernel's pgdir
|
||||
@ -782,17 +819,26 @@ thread_exit(void)
|
||||
|
||||
// delete the team if we're its main thread
|
||||
if (deleteTeam) {
|
||||
// notify listeners that a new death entry is available
|
||||
// ToDo: should that be moved to handle_signal() (for SIGCHLD)?
|
||||
release_sem_etc(parentDeadSem, 0, B_RELEASE_ALL | B_DO_NOT_RESCHEDULE);
|
||||
release_sem_etc(groupDeadSem, 0, B_RELEASE_ALL | B_DO_NOT_RESCHEDULE);
|
||||
|
||||
team_delete_process_group(freeGroup);
|
||||
team_delete_team(team);
|
||||
|
||||
// we need to remove any death entry that made it to here
|
||||
if (death != NULL)
|
||||
free(death);
|
||||
|
||||
send_signal_etc(mainParentThread, SIGCHLD, B_DO_NOT_RESCHEDULE);
|
||||
cached_death_sem = -1;
|
||||
cachedDeathSem = -1;
|
||||
} else
|
||||
cached_death_sem = team->death_sem;
|
||||
cachedDeathSem = team->death_sem;
|
||||
|
||||
// fill all death entries and delete the sem that others will use to wait on us
|
||||
{
|
||||
sem_id cachedExitSem = thread->exit.sem;
|
||||
struct death_entry *death = NULL;
|
||||
cpu_status state;
|
||||
|
||||
state = disable_interrupts();
|
||||
@ -802,6 +848,7 @@ thread_exit(void)
|
||||
thread->exit.sem = -1;
|
||||
|
||||
// fill all death entries
|
||||
death = NULL;
|
||||
while ((death = list_get_next_item(&thread->exit.waiters, death)) != NULL) {
|
||||
death->status = thread->exit.status;
|
||||
death->reason = thread->exit.reason;
|
||||
@ -822,7 +869,7 @@ thread_exit(void)
|
||||
args.thread = thread;
|
||||
args.old_kernel_stack = thread->kernel_stack_region_id;
|
||||
args.death_stack = death_stack;
|
||||
args.death_sem = cached_death_sem;
|
||||
args.death_sem = cachedDeathSem;
|
||||
|
||||
// set the new kernel stack officially to the death stack, wont be really switched until
|
||||
// the next function is called. This bookkeeping must be done now before a context switch
|
||||
|
Loading…
x
Reference in New Issue
Block a user