From 24bcf55926212cfc26ffbed2cb71fe3ca2b99c68 Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Tue, 28 Aug 2007 03:29:14 +0000 Subject: [PATCH] * 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 --- headers/private/kernel/team.h | 7 +- headers/private/kernel/thread_types.h | 47 +++- src/system/kernel/signal.cpp | 28 ++- src/system/kernel/team.cpp | 350 +++++++++++++++++--------- src/system/kernel/thread.cpp | 72 +++--- 5 files changed, 348 insertions(+), 156 deletions(-) diff --git a/headers/private/kernel/team.h b/headers/private/kernel/team.h index 3faca4a019..bbd7328b18 100644 --- a/headers/private/kernel/team.h +++ b/headers/private/kernel/team.h @@ -29,13 +29,16 @@ 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); +struct job_control_entry* team_get_death_entry(struct team *team, + thread_id child, bool* _deleteEntry); bool team_is_valid(team_id id); struct team *team_get_team_struct_locked(team_id id); int32 team_max_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 *), void *data); status_t stop_watching_team(team_id team, void (*hook)(team_id, void *), diff --git a/headers/private/kernel/thread_types.h b/headers/private/kernel/thread_types.h index 98f6962832..fecefe7213 100644 --- a/headers/private/kernel/thread_types.h +++ b/headers/private/kernel/thread_types.h @@ -40,8 +40,17 @@ enum team_state { 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_INTERRUPTED 0x2 +#define THREAD_STOPPED 0x3 +#define THREAD_CONTINUED 0x4 struct image; // 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 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 #include +#include -struct team_dead_children { - ConditionVariable condition_variable; - // wait for dead child entries - struct list list; + +struct job_control_entry : DoublyLinkedListLinkImpl { + job_control_state state; // current team job control state + 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 JobControlEntryList; + +struct team_job_control_children { + ConditionVariable condition_variable; + JobControlEntryList entries; +}; + +struct team_dead_children : team_job_control_children { uint32 count; - vint32 wait_for_any; // count of wait_for_child() that wait for any - // child bigtime_t kernel_time; bigtime_t user_time; }; + #endif // __cplusplus @@ -120,7 +150,12 @@ struct team { int pending_signals; void *io_context; sem_id death_sem; // semaphore to wait on for dying threads + 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 thread *main_thread; struct thread *thread_list; diff --git a/src/system/kernel/signal.cpp b/src/system/kernel/signal.cpp index 960aef57de..5ffb247c80 100644 --- a/src/system/kernel/signal.cpp +++ b/src/system/kernel/signal.cpp @@ -7,6 +7,9 @@ /* POSIX signals handling routines */ +#include +#include + #include #include @@ -19,9 +22,8 @@ #include #include #include +#include -#include -#include //#define TRACE_SIGNAL #ifdef TRACE_SIGNAL @@ -133,13 +135,26 @@ handle_signals(struct thread *thread) switch (signal) { case SIGCHLD: case SIGWINCH: - case SIGCONT: case SIGURG: // notify the debugger if (debugSignal) notify_debugger(thread, signal, handler, false); 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 SIGTSTP: case SIGTTIN: @@ -151,6 +166,13 @@ handle_signals(struct thread *thread) thread->next_state = B_THREAD_SUSPENDED; 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; case SIGQUIT: diff --git a/src/system/kernel/team.cpp b/src/system/kernel/team.cpp index 41d6ba33e7..03ea49ea3f 100644 --- a/src/system/kernel/team.cpp +++ b/src/system/kernel/team.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include //#define TRACE_TEAM @@ -564,32 +565,63 @@ create_team_struct(const char *name, bool kernel) team->dead_threads_kernel_time = 0; team->dead_threads_user_time = 0; - team->dead_children - = (team_dead_children*)malloc(sizeof(team_dead_children)); + // dead children + team->dead_children = new(nothrow) team_dead_children; if (team->dead_children == NULL) return NULL; - MemoryDeleter deadChildrenDeleter(team->dead_children); + ObjectDeleter deadChildrenDeleter(team->dead_children); - list_init(&team->dead_children->list); team->dead_children->count = 0; - team->dead_children->wait_for_any = 0; team->dead_children->kernel_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 stoppedChildrenDeleter( + team->stopped_children); + + // continued children + team->continued_children = new(nothrow) team_job_control_children; + if (team->continued_children == NULL) + return NULL; + ObjectDeleter 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 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->watcher_list); clear_team_debug_info(&team->debug_info, true); - if (arch_team_init_team_struct(team, kernel) < 0) { - team->dead_children->condition_variable.Unpublish(); + if (arch_team_init_team_struct(team, kernel) < 0) 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(); teamDeleter.Detach(); + return team; } @@ -597,16 +629,19 @@ create_team_struct(const char *name, bool kernel) static void 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(); - while ((death = (struct death_entry*)list_remove_head_item( - &team->dead_children->list)) != NULL) { - free(death); - } + while (job_control_entry* entry = team->dead_children->entries.RemoveHead()) + delete entry; - 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); } @@ -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 - * to the reason why a thread has died than waitpid() can be. - */ +static job_control_entry* +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 -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 death_entry death, *freeDeath = NULL; + struct job_control_entry foundEntry; + struct job_control_entry* freeDeathEntry = NULL; status_t status = B_OK; - bool childrenExist = false; 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 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) { - ConditionVariableEntry waitEntry; + InterruptsSpinLocker locker(team_spinlock); - cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + // check whether any condition holds + job_control_entry* entry = get_job_control_entry(team->dead_children, + child); - if (child > 0 && !childrenExist) { - // wait for the specified child + if (entry == NULL && (flags & WCONTINUED) != 0) + entry = get_job_control_entry(team->continued_children, child); - struct thread *childThread = thread_get_thread_struct_locked(child); - if (childThread != NULL) { - // team is still running, so we would need to block - if ((flags & WNOHANG) != 0) - status = B_WOULD_BLOCK; + if (entry == NULL && (flags & WUNTRACED) != 0) + entry = get_job_control_entry(team->stopped_children, child); - // make sure this child is one of ours - if (childThread->team->parent != team) - status = ECHILD; - - childrenExist = true; - } - } - - 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) + // If we don't have an entry yet, check whether there are any children + // complying to the process group specification at all. + if (entry == NULL) { + // No success yet -- check whether there are any children we could + // wait for. + bool childrenExist = false; + if (child == -1) { childrenExist = team->children != NULL; - else if (child < -1) + } else if (child < -1) { childrenExist = has_children_in_group(team, -child); + } else { + if (struct team* childTeam = team_get_team_struct_locked(child)) + childrenExist = team->parent == team; + } if (!childrenExist) { // 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 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 deadWaitEntry; + ConditionVariableEntry continuedWaitEntry; + ConditionVariableEntry stoppedWaitEntry; + if (status == B_WOULD_BLOCK && (flags & WNOHANG) == 0) { - // We need to hold the team lock when changing this counter, - // but of course only if we really will wait later - waitEntry.Add(team->dead_children); + deadWaitEntry.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(); - restore_interrupts(state); + locker.Unlock(); - // 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) break; 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) - goto err; + return status; } - free(freeDeath); + delete freeDeathEntry; // when we got here, we have a valid death entry, and // already got unregistered from the team or group - *_returnCode = death.status; - *_reason = (death.signal << 16) | death.reason; + int reason = 0; + 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: - if (child < 0) - atomic_add(&team->dead_children->wait_for_any, -1); - - return status; + return foundEntry.thread; } @@ -1466,45 +1541,29 @@ team_used_teams(void) } -/** 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. - */ - -status_t -team_get_death_entry(struct team *team, thread_id child, struct death_entry *death, - struct death_entry **_freeDeath) +/*! Fills the provided death entry if it's in the team. + 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) { - struct death_entry *entry = NULL; + if (child <= 0) + return NULL; - // find matching death entry structure - - if (child == 0) { - // wait for all children in the process group of the calling team - child = -team->group_id; - } - - 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); + job_control_entry* entry = get_job_control_entry(team->dead_children, + child); + if (entry) { + // remove the entry only, if the caller is the parent of the found team + if (team_get_current_team_id() == entry->thread) { + team->dead_children->entries.Remove(entry); 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); vfs_free_io_context(team->io_context); - // ToDo: should our death_entries be moved one level up? delete_team_struct(team); // 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 * team goes away. * This call might get public in the future. diff --git a/src/system/kernel/thread.cpp b/src/system/kernel/thread.cpp index 79cf868a59..b6188d8bf0 100644 --- a/src/system/kernel/thread.cpp +++ b/src/system/kernel/thread.cpp @@ -1094,7 +1094,6 @@ thread_exit(void) struct thread *thread = thread_get_current_thread(); struct process_group *freeGroup = NULL; struct team *team = thread->team; - struct death_entry *death = NULL; thread_id parentID = -1; bool deleteTeam = false; sem_id cachedDeathSem = -1; @@ -1122,20 +1121,11 @@ thread_exit(void) delete_area_etc(team, area); } + struct job_control_entry *death = NULL; 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->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 @@ -1161,22 +1151,30 @@ thread_exit(void) // remember who our parent was so we can send a signal parentID = parent->id; - if (death != NULL) { - // insert death entry into the parent's list + // Set the team job control state to "dead" and detach the job + // 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 (++parent->dead_children->count > MAX_DEAD_CHILDREN) { - death = (struct death_entry*)list_remove_head_item( - &parent->dead_children->list); + if (death != NULL) { + death->team = 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; + + // 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--; } else death = NULL; 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 RELEASE_THREAD_LOCK(); @@ -1198,9 +1196,9 @@ thread_exit(void) team_delete_process_group(freeGroup); 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) - free(death); + delete death; send_signal_etc(parentID, SIGCHLD, B_DO_NOT_RESCHEDULE); cachedDeathSem = -1; @@ -1251,12 +1249,12 @@ thread_exit(void) thread->exit.sem = -1; // fill all death entries - death = NULL; - while ((death = (struct death_entry*)list_get_next_item( - &thread->exit.waiters, death)) != NULL) { - death->status = thread->exit.status; - death->reason = thread->exit.reason; - death->signal = thread->exit.signal; + death_entry* entry = NULL; + while ((entry = (struct death_entry*)list_get_next_item( + &thread->exit.waiters, entry)) != NULL) { + entry->status = thread->exit.status; + entry->reason = thread->exit.reason; + entry->signal = thread->exit.signal; } RELEASE_THREAD_LOCK(); @@ -1513,7 +1511,8 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, status_t *_returnCode) { 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; cpu_status state; 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 GRAB_TEAM_LOCK(); - status = team_get_death_entry(thread_get_current_thread()->team, - id, &death, &freeDeath); + bool deleteEntry; + 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(); } @@ -1553,7 +1559,7 @@ wait_for_thread_etc(thread_id id, uint32 flags, bigtime_t timeout, if (_returnCode) *_returnCode = death.status; - free(freeDeath); + delete freeDeath; return B_OK; }