Fix process_result locking by acquiring in process_wait as well to prevent freeing memory too early

This commit is contained in:
2024-11-08 10:50:10 +00:00
parent 84fe637c7e
commit 6ed1ccd21e
3 changed files with 28 additions and 7 deletions

View File

@@ -647,6 +647,7 @@ init_process_result (struct thread *t)
struct process_result *result = malloc (sizeof (struct process_result)); struct process_result *result = malloc (sizeof (struct process_result));
result->tid = t->tid; result->tid = t->tid;
result->exit_status = t->exit_status; result->exit_status = t->exit_status;
lock_init (&result->lock);
sema_init (&result->sema, 0); sema_init (&result->sema, 0);
t->result = result; t->result = result;
} }

View File

@@ -34,9 +34,10 @@ typedef int tid_t;
/* A process result, synchronised between parent and child. */ /* A process result, synchronised between parent and child. */
struct process_result struct process_result
{ {
tid_t tid; /* The tid of the child process. */ tid_t tid; /* The tid of the child process. */
int exit_status; /* The exit status of the child process. Initially set to int exit_status; /* The exit status of the child process. Initially set
-1, then to exit_status when child dies. */ to -1, then to exit_status when child dies. */
struct lock lock; /* Lock the exit_status and sema. */
struct semaphore sema; /* Semaphore to signal the parent that the exit_status struct semaphore sema; /* Semaphore to signal the parent that the exit_status
has been set. */ has been set. */
struct list_elem elem; /* List element for the parent's children list. */ struct list_elem elem; /* List element for the parent's children list. */

View File

@@ -224,11 +224,16 @@ process_wait (tid_t child_tid UNUSED)
return -1; return -1;
/* Wait for child to die. */ /* Wait for child to die. */
sema_down (&child_result->sema); sema_down (&child_result->sema);
/* We need lock release in process_exit, so we need to acquire (and possibly
wait) for it here to ensure we don't free the lock memory before it is
released in process_exit. */
lock_acquire (&child_result->lock);
/* To prevent waiting for child twice, remove it from the list. /* To prevent waiting for child twice, remove it from the list.
No need to use lock since this is the only thread with access to No need to use lock since this is the only thread with access to
the struct process_result now. */ the struct process_result now. */
list_remove (&child_result->elem); list_remove (&child_result->elem);
int exit_status = child_result->exit_status; int exit_status = child_result->exit_status;
lock_release (&child_result->lock);
free (child_result); free (child_result);
return exit_status; return exit_status;
} }
@@ -245,14 +250,21 @@ process_exit (void)
/* Update process result. */ /* Update process result. */
if (cur->result != NULL) if (cur->result != NULL)
{ {
lock_acquire (&cur->result->lock);
cur->result->exit_status = cur->exit_status; cur->result->exit_status = cur->exit_status;
/* Parent has died, child has to free the struct process_result * */ /* Parent has died, child has to free the struct process_result * */
if (sema_try_down (&cur->result->sema)) if (sema_try_down (&cur->result->sema))
free (cur->result); {
lock_release (&cur->result->lock);
free (cur->result);
}
/* Parent is still alive and will be the one to free the /* Parent is still alive and will be the one to free the
struct process_result *, and may be waiting so call sema_up */ struct process_result *, and may be waiting so call sema_up */
else else
sema_up (&cur->result->sema); {
sema_up (&cur->result->sema);
lock_release (&cur->result->lock);
}
} }
struct list_elem *e; struct list_elem *e;
@@ -261,12 +273,19 @@ process_exit (void)
{ {
struct process_result *result struct process_result *result
= list_entry (e, struct process_result, elem); = list_entry (e, struct process_result, elem);
lock_acquire (&result->lock);
/* Child has died (and was not waited for). Free the result. */ /* Child has died (and was not waited for). Free the result. */
if (sema_try_down (&result->sema)) if (sema_try_down (&result->sema))
free (result); {
lock_release (&result->lock);
free (result);
}
/* Child is still alive, signal via sema that parent has died. */ /* Child is still alive, signal via sema that parent has died. */
else else
sema_up (&result->sema); {
sema_up (&result->sema);
lock_release (&result->lock);
}
} }
/* Destroy the current process's page directory and switch back /* Destroy the current process's page directory and switch back