* Introduced new job_control_entry structure which, among other things,
is used instead of death_entry for team::dead_children.
* Added team::{stopped,continued}_children, which, analoguously to
dead_children, are used to track the state of stopped/continued
children.
* A team does have a job_control_entry, which is allocated at team
creation time. It will be inserted into the parent's
{stopped,continued}_children lists as the team's main thread is
stopped/continued and removed when waitpid() retrieves the child
state. When the team dies the entry is detached from the team and goes
into the parent's dead_children list.
* Removed the wait_for_any field from team_dead_children. It was solely
used to avoid deletion of the contained entries in certain situations.
wait_for_child() (the waitpid() backend) always deletes an entry now,
regardless of whether other threads are waiting; that's in
accordance with the waidpid() specification. wait_for_thread() removes
the entry only, if the caller is the parent of the respective team.
* Introduced team_set_job_control_state() which performes the job
control entry transitions between the respective lists and wakes up
threads waiting in wait_for_child(). It is invoked on team death and
when the team's main thread receives job control signals.
* Reorganized wait_for_child(). It handles WCONTINUED and WUNTRACED now,
too. Removed a block that interpreted the supplied ID as thread ID.
* Added missing parts in waitpid().
Job control starts to work, though it seems to have some glitches.
git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@22088 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -29,13 +29,16 @@ status_t team_get_address_space(team_id id,
|
|||||||
struct vm_address_space **_addressSpace);
|
struct vm_address_space **_addressSpace);
|
||||||
char **user_team_get_arguments(void);
|
char **user_team_get_arguments(void);
|
||||||
int user_team_get_arg_count(void);
|
int user_team_get_arg_count(void);
|
||||||
status_t team_get_death_entry(struct team *team, thread_id child,
|
struct job_control_entry* team_get_death_entry(struct team *team,
|
||||||
struct death_entry *death, struct death_entry **_freeDeath);
|
thread_id child, bool* _deleteEntry);
|
||||||
bool team_is_valid(team_id id);
|
bool team_is_valid(team_id id);
|
||||||
struct team *team_get_team_struct_locked(team_id id);
|
struct team *team_get_team_struct_locked(team_id id);
|
||||||
int32 team_max_teams(void);
|
int32 team_max_teams(void);
|
||||||
int32 team_used_teams(void);
|
int32 team_used_teams(void);
|
||||||
|
|
||||||
|
void team_set_job_control_state(struct team* team, job_control_state newState,
|
||||||
|
int signal, bool threadsLocked);
|
||||||
|
|
||||||
status_t start_watching_team(team_id team, void (*hook)(team_id, void *),
|
status_t start_watching_team(team_id team, void (*hook)(team_id, void *),
|
||||||
void *data);
|
void *data);
|
||||||
status_t stop_watching_team(team_id team, void (*hook)(team_id, void *),
|
status_t stop_watching_team(team_id team, void (*hook)(team_id, void *),
|
||||||
|
|||||||
@@ -40,8 +40,17 @@ enum team_state {
|
|||||||
TEAM_STATE_DEATH // being killed
|
TEAM_STATE_DEATH // being killed
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef enum job_control_state {
|
||||||
|
JOB_CONTROL_STATE_NONE,
|
||||||
|
JOB_CONTROL_STATE_STOPPED,
|
||||||
|
JOB_CONTROL_STATE_CONTINUED,
|
||||||
|
JOB_CONTROL_STATE_DEAD
|
||||||
|
} job_control_state;
|
||||||
|
|
||||||
#define THREAD_RETURN_EXIT 0x1
|
#define THREAD_RETURN_EXIT 0x1
|
||||||
#define THREAD_RETURN_INTERRUPTED 0x2
|
#define THREAD_RETURN_INTERRUPTED 0x2
|
||||||
|
#define THREAD_STOPPED 0x3
|
||||||
|
#define THREAD_CONTINUED 0x4
|
||||||
|
|
||||||
struct image;
|
struct image;
|
||||||
// defined in image.c
|
// defined in image.c
|
||||||
@@ -83,23 +92,44 @@ struct team_watcher {
|
|||||||
// this is a soft limit for the number of dead entries in a team
|
// this is a soft limit for the number of dead entries in a team
|
||||||
|
|
||||||
typedef struct team_dead_children team_dead_children;
|
typedef struct team_dead_children team_dead_children;
|
||||||
|
typedef struct team_job_control_children team_job_control_children;
|
||||||
|
typedef struct job_control_entry job_control_entry;
|
||||||
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
|
||||||
#include <condition_variable.h>
|
#include <condition_variable.h>
|
||||||
|
#include <util/DoublyLinkedList.h>
|
||||||
|
|
||||||
struct team_dead_children {
|
|
||||||
ConditionVariable<team_dead_children> condition_variable;
|
struct job_control_entry : DoublyLinkedListLinkImpl<job_control_entry> {
|
||||||
// wait for dead child entries
|
job_control_state state; // current team job control state
|
||||||
struct list list;
|
thread_id thread; // main thread ID == team ID
|
||||||
|
|
||||||
|
// valid while state != JOB_CONTROL_STATE_DEAD
|
||||||
|
struct team* team;
|
||||||
|
|
||||||
|
// valid when state == JOB_CONTROL_STATE_DEAD
|
||||||
|
pid_t group_id;
|
||||||
|
status_t status;
|
||||||
|
uint16 reason;
|
||||||
|
uint16 signal;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef DoublyLinkedList<job_control_entry> JobControlEntryList;
|
||||||
|
|
||||||
|
struct team_job_control_children {
|
||||||
|
ConditionVariable<team_job_control_children> condition_variable;
|
||||||
|
JobControlEntryList entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct team_dead_children : team_job_control_children {
|
||||||
uint32 count;
|
uint32 count;
|
||||||
vint32 wait_for_any; // count of wait_for_child() that wait for any
|
|
||||||
// child
|
|
||||||
bigtime_t kernel_time;
|
bigtime_t kernel_time;
|
||||||
bigtime_t user_time;
|
bigtime_t user_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
|
|
||||||
@@ -120,7 +150,12 @@ struct team {
|
|||||||
int pending_signals;
|
int pending_signals;
|
||||||
void *io_context;
|
void *io_context;
|
||||||
sem_id death_sem; // semaphore to wait on for dying threads
|
sem_id death_sem; // semaphore to wait on for dying threads
|
||||||
|
|
||||||
team_dead_children *dead_children;
|
team_dead_children *dead_children;
|
||||||
|
team_job_control_children *stopped_children;
|
||||||
|
team_job_control_children *continued_children;
|
||||||
|
struct job_control_entry* job_control_entry;
|
||||||
|
|
||||||
struct vm_address_space *address_space;
|
struct vm_address_space *address_space;
|
||||||
struct thread *main_thread;
|
struct thread *main_thread;
|
||||||
struct thread *thread_list;
|
struct thread *thread_list;
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
|
|
||||||
/* POSIX signals handling routines */
|
/* POSIX signals handling routines */
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include <OS.h>
|
#include <OS.h>
|
||||||
#include <KernelExport.h>
|
#include <KernelExport.h>
|
||||||
|
|
||||||
@@ -19,9 +22,8 @@
|
|||||||
#include <team.h>
|
#include <team.h>
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <user_debugger.h>
|
#include <user_debugger.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
//#define TRACE_SIGNAL
|
//#define TRACE_SIGNAL
|
||||||
#ifdef TRACE_SIGNAL
|
#ifdef TRACE_SIGNAL
|
||||||
@@ -133,13 +135,26 @@ handle_signals(struct thread *thread)
|
|||||||
switch (signal) {
|
switch (signal) {
|
||||||
case SIGCHLD:
|
case SIGCHLD:
|
||||||
case SIGWINCH:
|
case SIGWINCH:
|
||||||
case SIGCONT:
|
|
||||||
case SIGURG:
|
case SIGURG:
|
||||||
// notify the debugger
|
// notify the debugger
|
||||||
if (debugSignal)
|
if (debugSignal)
|
||||||
notify_debugger(thread, signal, handler, false);
|
notify_debugger(thread, signal, handler, false);
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
case SIGCONT:
|
||||||
|
// notify the debugger
|
||||||
|
if (debugSignal
|
||||||
|
&& !notify_debugger(thread, signal, handler, false))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// notify threads waiting for team state changes
|
||||||
|
if (thread == thread->team->main_thread) {
|
||||||
|
InterruptsSpinLocker locker(team_spinlock);
|
||||||
|
team_set_job_control_state(thread->team,
|
||||||
|
JOB_CONTROL_STATE_CONTINUED, signal, false);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
case SIGSTOP:
|
case SIGSTOP:
|
||||||
case SIGTSTP:
|
case SIGTSTP:
|
||||||
case SIGTTIN:
|
case SIGTTIN:
|
||||||
@@ -151,6 +166,13 @@ handle_signals(struct thread *thread)
|
|||||||
|
|
||||||
thread->next_state = B_THREAD_SUSPENDED;
|
thread->next_state = B_THREAD_SUSPENDED;
|
||||||
reschedule = true;
|
reschedule = true;
|
||||||
|
|
||||||
|
// notify threads waiting for team state changes
|
||||||
|
if (thread == thread->team->main_thread) {
|
||||||
|
InterruptsSpinLocker locker(team_spinlock);
|
||||||
|
team_set_job_control_state(thread->team,
|
||||||
|
JOB_CONTROL_STATE_STOPPED, signal, false);
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
case SIGQUIT:
|
case SIGQUIT:
|
||||||
|
|||||||
+238
-112
@@ -33,6 +33,7 @@
|
|||||||
#include <vfs.h>
|
#include <vfs.h>
|
||||||
#include <vm.h>
|
#include <vm.h>
|
||||||
#include <vm_address_space.h>
|
#include <vm_address_space.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
#include <util/khash.h>
|
#include <util/khash.h>
|
||||||
|
|
||||||
//#define TRACE_TEAM
|
//#define TRACE_TEAM
|
||||||
@@ -564,32 +565,63 @@ create_team_struct(const char *name, bool kernel)
|
|||||||
team->dead_threads_kernel_time = 0;
|
team->dead_threads_kernel_time = 0;
|
||||||
team->dead_threads_user_time = 0;
|
team->dead_threads_user_time = 0;
|
||||||
|
|
||||||
team->dead_children
|
// dead children
|
||||||
= (team_dead_children*)malloc(sizeof(team_dead_children));
|
team->dead_children = new(nothrow) team_dead_children;
|
||||||
if (team->dead_children == NULL)
|
if (team->dead_children == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
MemoryDeleter deadChildrenDeleter(team->dead_children);
|
ObjectDeleter<team_dead_children> deadChildrenDeleter(team->dead_children);
|
||||||
|
|
||||||
list_init(&team->dead_children->list);
|
|
||||||
team->dead_children->count = 0;
|
team->dead_children->count = 0;
|
||||||
team->dead_children->wait_for_any = 0;
|
|
||||||
team->dead_children->kernel_time = 0;
|
team->dead_children->kernel_time = 0;
|
||||||
team->dead_children->user_time = 0;
|
team->dead_children->user_time = 0;
|
||||||
team->dead_children->condition_variable.Publish(team->dead_children,
|
|
||||||
"dead children");
|
// stopped children
|
||||||
|
team->stopped_children = new(nothrow) team_job_control_children;
|
||||||
|
if (team->stopped_children == NULL)
|
||||||
|
return NULL;
|
||||||
|
ObjectDeleter<team_job_control_children> stoppedChildrenDeleter(
|
||||||
|
team->stopped_children);
|
||||||
|
|
||||||
|
// continued children
|
||||||
|
team->continued_children = new(nothrow) team_job_control_children;
|
||||||
|
if (team->continued_children == NULL)
|
||||||
|
return NULL;
|
||||||
|
ObjectDeleter<team_job_control_children> continuedChildrenDeleter(
|
||||||
|
team->continued_children);
|
||||||
|
|
||||||
|
// job control entry
|
||||||
|
team->job_control_entry = new(nothrow) job_control_entry;
|
||||||
|
if (team->job_control_entry == NULL)
|
||||||
|
return NULL;
|
||||||
|
ObjectDeleter<job_control_entry> jobControlEntryDeleter(
|
||||||
|
team->job_control_entry);
|
||||||
|
team->job_control_entry->state = JOB_CONTROL_STATE_NONE;
|
||||||
|
team->job_control_entry->thread = team->id;
|
||||||
|
team->job_control_entry->team = team;
|
||||||
|
|
||||||
list_init(&team->image_list);
|
list_init(&team->image_list);
|
||||||
list_init(&team->watcher_list);
|
list_init(&team->watcher_list);
|
||||||
|
|
||||||
clear_team_debug_info(&team->debug_info, true);
|
clear_team_debug_info(&team->debug_info, true);
|
||||||
|
|
||||||
if (arch_team_init_team_struct(team, kernel) < 0) {
|
if (arch_team_init_team_struct(team, kernel) < 0)
|
||||||
team->dead_children->condition_variable.Unpublish();
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
|
||||||
|
|
||||||
|
// publish dead/stopped/continued children condition vars
|
||||||
|
team->dead_children->condition_variable.Publish(team->dead_children,
|
||||||
|
"dead children");
|
||||||
|
team->stopped_children->condition_variable.Publish(team->stopped_children,
|
||||||
|
"stopped children");
|
||||||
|
team->continued_children->condition_variable.Publish(
|
||||||
|
team->continued_children, "continued children");
|
||||||
|
|
||||||
|
// keep all allocated structures
|
||||||
|
jobControlEntryDeleter.Detach();
|
||||||
|
continuedChildrenDeleter.Detach();
|
||||||
|
stoppedChildrenDeleter.Detach();
|
||||||
deadChildrenDeleter.Detach();
|
deadChildrenDeleter.Detach();
|
||||||
teamDeleter.Detach();
|
teamDeleter.Detach();
|
||||||
|
|
||||||
return team;
|
return team;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,16 +629,19 @@ create_team_struct(const char *name, bool kernel)
|
|||||||
static void
|
static void
|
||||||
delete_team_struct(struct team *team)
|
delete_team_struct(struct team *team)
|
||||||
{
|
{
|
||||||
struct death_entry *death;
|
team->stopped_children->condition_variable.Unpublish();
|
||||||
|
team->continued_children->condition_variable.Unpublish();
|
||||||
|
|
||||||
team->dead_children->condition_variable.Unpublish();
|
team->dead_children->condition_variable.Unpublish();
|
||||||
|
|
||||||
while ((death = (struct death_entry*)list_remove_head_item(
|
while (job_control_entry* entry = team->dead_children->entries.RemoveHead())
|
||||||
&team->dead_children->list)) != NULL) {
|
delete entry;
|
||||||
free(death);
|
|
||||||
}
|
|
||||||
|
|
||||||
free(team->dead_children);
|
delete team->job_control_entry;
|
||||||
|
// usually already NULL and transferred to the parent
|
||||||
|
delete team->continued_children;
|
||||||
|
delete team->stopped_children;
|
||||||
|
delete team->dead_children;
|
||||||
free(team);
|
free(team);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1262,17 +1297,40 @@ has_children_in_group(struct team *parent, pid_t groupID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** This is the kernel backend for waitpid(). It is a bit more powerful when it comes
|
static job_control_entry*
|
||||||
* to the reason why a thread has died than waitpid() can be.
|
get_job_control_entry(team_job_control_children* children, pid_t id)
|
||||||
*/
|
{
|
||||||
|
for (JobControlEntryList::Iterator it = children->entries.GetIterator();
|
||||||
|
job_control_entry* entry = it.Next();) {
|
||||||
|
|
||||||
|
if (id > 0) {
|
||||||
|
if (entry->thread == id)
|
||||||
|
return entry;
|
||||||
|
} else if (id == -1) {
|
||||||
|
return entry;
|
||||||
|
} else {
|
||||||
|
pid_t processGroup
|
||||||
|
= (entry->team ? entry->team->group_id : entry->group_id);
|
||||||
|
if (processGroup == -id)
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! 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.
|
||||||
|
*/
|
||||||
static thread_id
|
static thread_id
|
||||||
wait_for_child(thread_id child, uint32 flags, int32 *_reason, status_t *_returnCode)
|
wait_for_child(pid_t child, uint32 flags, int32 *_reason,
|
||||||
|
status_t *_returnCode)
|
||||||
{
|
{
|
||||||
struct team *team = thread_get_current_thread()->team;
|
struct team *team = thread_get_current_thread()->team;
|
||||||
struct death_entry death, *freeDeath = NULL;
|
struct job_control_entry foundEntry;
|
||||||
|
struct job_control_entry* freeDeathEntry = NULL;
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
bool childrenExist = false;
|
|
||||||
|
|
||||||
TRACE(("wait_for_child(child = %ld, flags = %ld)\n", child, flags));
|
TRACE(("wait_for_child(child = %ld, flags = %ld)\n", child, flags));
|
||||||
|
|
||||||
@@ -1280,52 +1338,34 @@ wait_for_child(thread_id child, uint32 flags, int32 *_reason, status_t *_returnC
|
|||||||
// wait for all children in the process group of the calling team
|
// wait for all children in the process group of the calling team
|
||||||
child = -team->group_id;
|
child = -team->group_id;
|
||||||
}
|
}
|
||||||
if (child < 0) {
|
|
||||||
// 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) {
|
while (true) {
|
||||||
ConditionVariableEntry<team_dead_children> waitEntry;
|
InterruptsSpinLocker locker(team_spinlock);
|
||||||
|
|
||||||
cpu_status state = disable_interrupts();
|
// check whether any condition holds
|
||||||
GRAB_THREAD_LOCK();
|
job_control_entry* entry = get_job_control_entry(team->dead_children,
|
||||||
|
child);
|
||||||
|
|
||||||
if (child > 0 && !childrenExist) {
|
if (entry == NULL && (flags & WCONTINUED) != 0)
|
||||||
// wait for the specified child
|
entry = get_job_control_entry(team->continued_children, child);
|
||||||
|
|
||||||
struct thread *childThread = thread_get_thread_struct_locked(child);
|
if (entry == NULL && (flags & WUNTRACED) != 0)
|
||||||
if (childThread != NULL) {
|
entry = get_job_control_entry(team->stopped_children, child);
|
||||||
// team is still running, so we would need to block
|
|
||||||
if ((flags & WNOHANG) != 0)
|
|
||||||
status = B_WOULD_BLOCK;
|
|
||||||
|
|
||||||
// make sure this child is one of ours
|
// If we don't have an entry yet, check whether there are any children
|
||||||
if (childThread->team->parent != team)
|
// complying to the process group specification at all.
|
||||||
status = ECHILD;
|
if (entry == NULL) {
|
||||||
|
// No success yet -- check whether there are any children we could
|
||||||
childrenExist = true;
|
// wait for.
|
||||||
}
|
bool childrenExist = false;
|
||||||
}
|
if (child == -1) {
|
||||||
|
|
||||||
RELEASE_THREAD_LOCK();
|
|
||||||
|
|
||||||
if (status != B_OK) {
|
|
||||||
restore_interrupts(state);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
// see if there is any death entry for us already
|
|
||||||
|
|
||||||
GRAB_TEAM_LOCK();
|
|
||||||
|
|
||||||
status = team_get_death_entry(team, child, &death, &freeDeath);
|
|
||||||
if (status < B_OK) {
|
|
||||||
// there is no matching death entry
|
|
||||||
if (child == -1)
|
|
||||||
childrenExist = team->children != NULL;
|
childrenExist = team->children != NULL;
|
||||||
else if (child < -1)
|
} else if (child < -1) {
|
||||||
childrenExist = has_children_in_group(team, -child);
|
childrenExist = has_children_in_group(team, -child);
|
||||||
|
} else {
|
||||||
|
if (struct team* childTeam = team_get_team_struct_locked(child))
|
||||||
|
childrenExist = team->parent == team;
|
||||||
|
}
|
||||||
|
|
||||||
if (!childrenExist) {
|
if (!childrenExist) {
|
||||||
// there is no child we could wait for
|
// there is no child we could wait for
|
||||||
@@ -1334,41 +1374,76 @@ wait_for_child(thread_id child, uint32 flags, int32 *_reason, status_t *_returnC
|
|||||||
// the children we're waiting for are still running
|
// the children we're waiting for are still running
|
||||||
status = B_WOULD_BLOCK;
|
status = B_WOULD_BLOCK;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// got something
|
||||||
|
foundEntry = *entry;
|
||||||
|
if (entry->state == JOB_CONTROL_STATE_DEAD) {
|
||||||
|
// The child is dead. Reap its death entry.
|
||||||
|
freeDeathEntry = entry;
|
||||||
|
team->dead_children->entries.Remove(entry);
|
||||||
|
team->dead_children->count--;
|
||||||
|
} else {
|
||||||
|
// The child is well. Reset its job control state.
|
||||||
|
team_set_job_control_state(entry->team,
|
||||||
|
JOB_CONTROL_STATE_NONE, 0, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we haven't got anything yet, add prepare for waiting for the
|
||||||
|
// condition variables.
|
||||||
|
ConditionVariableEntry<team_dead_children> deadWaitEntry;
|
||||||
|
ConditionVariableEntry<team_job_control_children> continuedWaitEntry;
|
||||||
|
ConditionVariableEntry<team_job_control_children> stoppedWaitEntry;
|
||||||
|
|
||||||
if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0) {
|
if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0) {
|
||||||
// We need to hold the team lock when changing this counter,
|
deadWaitEntry.Add(team->dead_children);
|
||||||
// but of course only if we really will wait later
|
|
||||||
waitEntry.Add(team->dead_children);
|
if ((flags & WCONTINUED) != 0) {
|
||||||
|
continuedWaitEntry.Add(team->continued_children,
|
||||||
|
&deadWaitEntry);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((flags & WUNTRACED) != 0)
|
||||||
|
stoppedWaitEntry.Add(team->stopped_children, &deadWaitEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
locker.Unlock();
|
||||||
restore_interrupts(state);
|
|
||||||
|
|
||||||
// we got our death entry and can return to our caller
|
// we got our entry and can return to our caller
|
||||||
if (status == B_OK)
|
if (status == B_OK)
|
||||||
break;
|
break;
|
||||||
if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0)
|
if (status != B_WOULD_BLOCK || (flags & WNOHANG) != 0)
|
||||||
goto err;
|
return status;
|
||||||
|
|
||||||
status = waitEntry.Wait(B_CAN_INTERRUPT);
|
status = deadWaitEntry.Wait(B_CAN_INTERRUPT);
|
||||||
if (status == B_INTERRUPTED)
|
if (status == B_INTERRUPTED)
|
||||||
goto err;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(freeDeath);
|
delete freeDeathEntry;
|
||||||
|
|
||||||
// when we got here, we have a valid death entry, and
|
// when we got here, we have a valid death entry, and
|
||||||
// already got unregistered from the team or group
|
// already got unregistered from the team or group
|
||||||
*_returnCode = death.status;
|
int reason = 0;
|
||||||
*_reason = (death.signal << 16) | death.reason;
|
switch (foundEntry.state) {
|
||||||
|
case JOB_CONTROL_STATE_DEAD:
|
||||||
|
reason = foundEntry.reason;
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_STOPPED:
|
||||||
|
reason = THREAD_STOPPED;
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_CONTINUED:
|
||||||
|
reason = THREAD_CONTINUED;
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_NONE:
|
||||||
|
// can't happen
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return death.thread;
|
*_returnCode = foundEntry.status;
|
||||||
|
*_reason = (foundEntry.signal << 16) | reason;
|
||||||
|
|
||||||
err:
|
return foundEntry.thread;
|
||||||
if (child < 0)
|
|
||||||
atomic_add(&team->dead_children->wait_for_any, -1);
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1466,45 +1541,29 @@ team_used_teams(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Fills the provided death entry if it's in the team.
|
/*! Fills the provided death entry if it's in the team.
|
||||||
* The \a child parameter follows waitpid() semantics.
|
You need to have the team lock held when calling this function.
|
||||||
* You need to have the team lock held when calling this function.
|
*/
|
||||||
*/
|
job_control_entry*
|
||||||
|
team_get_death_entry(struct team *team, thread_id child, bool* _deleteEntry)
|
||||||
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;
|
if (child <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
// find matching death entry structure
|
job_control_entry* entry = get_job_control_entry(team->dead_children,
|
||||||
|
child);
|
||||||
if (child == 0) {
|
if (entry) {
|
||||||
// wait for all children in the process group of the calling team
|
// remove the entry only, if the caller is the parent of the found team
|
||||||
child = -team->group_id;
|
if (team_get_current_team_id() == entry->thread) {
|
||||||
}
|
team->dead_children->entries.Remove(entry);
|
||||||
|
|
||||||
while ((entry = (struct death_entry*)list_get_next_item(
|
|
||||||
&team->dead_children->list, entry)) != NULL) {
|
|
||||||
if (child != -1 && entry->thread != child && entry->group_id != -child)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// we found one
|
|
||||||
|
|
||||||
*death = *entry;
|
|
||||||
|
|
||||||
// only remove the death entry if there aren't any other interested parties
|
|
||||||
if ((child < 0 && atomic_add(&team->dead_children->wait_for_any, -1) == 1)
|
|
||||||
|| (child > 0 && team->dead_children->wait_for_any == 0)) {
|
|
||||||
list_remove_link(entry);
|
|
||||||
team->dead_children->count--;
|
team->dead_children->count--;
|
||||||
*_freeDeath = entry;
|
*_deleteEntry = true;
|
||||||
|
} else {
|
||||||
|
*_deleteEntry = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return B_ERROR;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1722,7 +1781,6 @@ team_delete_team(struct team *team)
|
|||||||
remove_images(team);
|
remove_images(team);
|
||||||
vfs_free_io_context(team->io_context);
|
vfs_free_io_context(team->io_context);
|
||||||
|
|
||||||
// ToDo: should our death_entries be moved one level up?
|
|
||||||
delete_team_struct(team);
|
delete_team_struct(team);
|
||||||
|
|
||||||
// notify the debugger, that the team is gone
|
// notify the debugger, that the team is gone
|
||||||
@@ -1787,6 +1845,74 @@ team_get_address_space(team_id id, vm_address_space **_addressSpace)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*! Sets the team's job control state.
|
||||||
|
Interrupts must be disabled and the team lock being held.
|
||||||
|
\a threadsLocked indicates whether the thread lock is being held, too.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
team_set_job_control_state(struct team* team, job_control_state newState,
|
||||||
|
int signal, bool threadsLocked)
|
||||||
|
{
|
||||||
|
if (team == NULL || team->job_control_entry == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// don't touch anything, if the state stays the same or the team is already
|
||||||
|
// dead
|
||||||
|
job_control_entry* entry = team->job_control_entry;
|
||||||
|
if (entry->state == newState || entry->state == JOB_CONTROL_STATE_DEAD)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// remove from the old list
|
||||||
|
switch (entry->state) {
|
||||||
|
case JOB_CONTROL_STATE_NONE:
|
||||||
|
// entry is in no list ATM
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_DEAD:
|
||||||
|
// can't get here
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_STOPPED:
|
||||||
|
team->parent->stopped_children->entries.Remove(entry);
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_CONTINUED:
|
||||||
|
team->parent->continued_children->entries.Remove(entry);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry->state = newState;
|
||||||
|
entry->signal = signal;
|
||||||
|
|
||||||
|
// add to new list
|
||||||
|
team_job_control_children* childList = NULL;
|
||||||
|
switch (entry->state) {
|
||||||
|
case JOB_CONTROL_STATE_NONE:
|
||||||
|
// entry doesn't get into any list
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_DEAD:
|
||||||
|
childList = team->parent->dead_children;
|
||||||
|
team->parent->dead_children->count++;
|
||||||
|
// When a child dies, we need to notify all lists, since that might
|
||||||
|
// have been the last of the parent's children, and a waiting
|
||||||
|
// parent thread wouldn't wake up otherwise.
|
||||||
|
team->parent->stopped_children->condition_variable.NotifyAll(
|
||||||
|
threadsLocked);
|
||||||
|
team->parent->continued_children->condition_variable.NotifyAll(
|
||||||
|
threadsLocked);
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_STOPPED:
|
||||||
|
childList = team->parent->stopped_children;
|
||||||
|
break;
|
||||||
|
case JOB_CONTROL_STATE_CONTINUED:
|
||||||
|
childList = team->parent->continued_children;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (childList != NULL) {
|
||||||
|
childList->entries.Add(entry);
|
||||||
|
childList->condition_variable.NotifyAll(threadsLocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Adds a hook to the team that is called as soon as this
|
/** Adds a hook to the team that is called as soon as this
|
||||||
* team goes away.
|
* team goes away.
|
||||||
* This call might get public in the future.
|
* This call might get public in the future.
|
||||||
|
|||||||
@@ -1094,7 +1094,6 @@ thread_exit(void)
|
|||||||
struct thread *thread = thread_get_current_thread();
|
struct thread *thread = thread_get_current_thread();
|
||||||
struct process_group *freeGroup = NULL;
|
struct process_group *freeGroup = NULL;
|
||||||
struct team *team = thread->team;
|
struct team *team = thread->team;
|
||||||
struct death_entry *death = NULL;
|
|
||||||
thread_id parentID = -1;
|
thread_id parentID = -1;
|
||||||
bool deleteTeam = false;
|
bool deleteTeam = false;
|
||||||
sem_id cachedDeathSem = -1;
|
sem_id cachedDeathSem = -1;
|
||||||
@@ -1122,20 +1121,11 @@ thread_exit(void)
|
|||||||
delete_area_etc(team, area);
|
delete_area_etc(team, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct job_control_entry *death = NULL;
|
||||||
if (team != team_get_kernel_team()) {
|
if (team != team_get_kernel_team()) {
|
||||||
if (team->main_thread == thread) {
|
if (team->main_thread == thread) {
|
||||||
// this was the main thread in this team, so we will delete that as well
|
// this was the main thread in this team, so we will delete that as well
|
||||||
deleteTeam = true;
|
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->group_id = team->group_id;
|
|
||||||
death->thread = thread->id;
|
|
||||||
death->status = thread->exit.status;
|
|
||||||
death->reason = thread->exit.reason;
|
|
||||||
death->signal = thread->exit.signal;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove this thread from the current team and add it to the kernel
|
// remove this thread from the current team and add it to the kernel
|
||||||
@@ -1161,22 +1151,30 @@ thread_exit(void)
|
|||||||
// remember who our parent was so we can send a signal
|
// remember who our parent was so we can send a signal
|
||||||
parentID = parent->id;
|
parentID = parent->id;
|
||||||
|
|
||||||
if (death != NULL) {
|
// Set the team job control state to "dead" and detach the job
|
||||||
// insert death entry into the parent's list
|
// control entry from our team struct.
|
||||||
|
team_set_job_control_state(team, JOB_CONTROL_STATE_DEAD, 0, true);
|
||||||
|
death = team->job_control_entry;
|
||||||
|
team->job_control_entry = NULL;
|
||||||
|
|
||||||
list_add_link_to_tail(&parent->dead_children->list, death);
|
if (death != NULL) {
|
||||||
if (++parent->dead_children->count > MAX_DEAD_CHILDREN) {
|
death->team = NULL;
|
||||||
death = (struct death_entry*)list_remove_head_item(
|
death->group_id = team->group_id;
|
||||||
&parent->dead_children->list);
|
death->thread = thread->id;
|
||||||
|
death->status = thread->exit.status;
|
||||||
|
death->reason = thread->exit.reason;
|
||||||
|
death->signal = thread->exit.signal;
|
||||||
|
|
||||||
|
// team_set_job_control_state() already moved our entry
|
||||||
|
// into the parent's list. We just check the soft limit of
|
||||||
|
// death entries.
|
||||||
|
if (parent->dead_children->count > MAX_DEAD_CHILDREN) {
|
||||||
|
death = parent->dead_children->entries.RemoveHead();
|
||||||
parent->dead_children->count--;
|
parent->dead_children->count--;
|
||||||
} else
|
} else
|
||||||
death = NULL;
|
death = NULL;
|
||||||
|
|
||||||
RELEASE_THREAD_LOCK();
|
RELEASE_THREAD_LOCK();
|
||||||
|
|
||||||
// notify listeners that a new death entry is available
|
|
||||||
// TODO: should that be moved to handle_signal() (for SIGCHLD)?
|
|
||||||
parent->dead_children->condition_variable.NotifyOne();
|
|
||||||
} else
|
} else
|
||||||
RELEASE_THREAD_LOCK();
|
RELEASE_THREAD_LOCK();
|
||||||
|
|
||||||
@@ -1198,9 +1196,9 @@ thread_exit(void)
|
|||||||
team_delete_process_group(freeGroup);
|
team_delete_process_group(freeGroup);
|
||||||
team_delete_team(team);
|
team_delete_team(team);
|
||||||
|
|
||||||
// we need to remove any death entry that made it to here
|
// we need to delete any death entry that made it to here
|
||||||
if (death != NULL)
|
if (death != NULL)
|
||||||
free(death);
|
delete death;
|
||||||
|
|
||||||
send_signal_etc(parentID, SIGCHLD, B_DO_NOT_RESCHEDULE);
|
send_signal_etc(parentID, SIGCHLD, B_DO_NOT_RESCHEDULE);
|
||||||
cachedDeathSem = -1;
|
cachedDeathSem = -1;
|
||||||
@@ -1251,12 +1249,12 @@ thread_exit(void)
|
|||||||
thread->exit.sem = -1;
|
thread->exit.sem = -1;
|
||||||
|
|
||||||
// fill all death entries
|
// fill all death entries
|
||||||
death = NULL;
|
death_entry* entry = NULL;
|
||||||
while ((death = (struct death_entry*)list_get_next_item(
|
while ((entry = (struct death_entry*)list_get_next_item(
|
||||||
&thread->exit.waiters, death)) != NULL) {
|
&thread->exit.waiters, entry)) != NULL) {
|
||||||
death->status = thread->exit.status;
|
entry->status = thread->exit.status;
|
||||||
death->reason = thread->exit.reason;
|
entry->reason = thread->exit.reason;
|
||||||
death->signal = thread->exit.signal;
|
entry->signal = thread->exit.signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
RELEASE_THREAD_LOCK();
|
RELEASE_THREAD_LOCK();
|
||||||
@@ -1513,7 +1511,8 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout,
|
|||||||
status_t *_returnCode)
|
status_t *_returnCode)
|
||||||
{
|
{
|
||||||
sem_id exitSem = B_BAD_THREAD_ID;
|
sem_id exitSem = B_BAD_THREAD_ID;
|
||||||
struct death_entry death, *freeDeath = NULL;
|
struct death_entry death;
|
||||||
|
job_control_entry* freeDeath = NULL;
|
||||||
struct thread *thread;
|
struct thread *thread;
|
||||||
cpu_status state;
|
cpu_status state;
|
||||||
status_t status = B_OK;
|
status_t status = B_OK;
|
||||||
@@ -1540,8 +1539,15 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout,
|
|||||||
// find its death entry in our team
|
// find its death entry in our team
|
||||||
GRAB_TEAM_LOCK();
|
GRAB_TEAM_LOCK();
|
||||||
|
|
||||||
status = team_get_death_entry(thread_get_current_thread()->team,
|
bool deleteEntry;
|
||||||
id, &death, &freeDeath);
|
freeDeath = team_get_death_entry(thread_get_current_thread()->team, id,
|
||||||
|
&deleteEntry);
|
||||||
|
if (freeDeath != NULL) {
|
||||||
|
death.status = freeDeath->status;
|
||||||
|
if (!deleteEntry)
|
||||||
|
freeDeath = NULL;
|
||||||
|
} else
|
||||||
|
status = B_BAD_THREAD_ID;
|
||||||
|
|
||||||
RELEASE_TEAM_LOCK();
|
RELEASE_TEAM_LOCK();
|
||||||
}
|
}
|
||||||
@@ -1553,7 +1559,7 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout,
|
|||||||
if (_returnCode)
|
if (_returnCode)
|
||||||
*_returnCode = death.status;
|
*_returnCode = death.status;
|
||||||
|
|
||||||
free(freeDeath);
|
delete freeDeath;
|
||||||
return B_OK;
|
return B_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user