diff --git a/src/threads/thread.h b/src/threads/thread.h index cf35b82..6465d53 100644 --- a/src/threads/thread.h +++ b/src/threads/thread.h @@ -36,9 +36,8 @@ struct process_result { tid_t tid; /* The tid of the child process. */ int exit_status; /* The exit status of the child process. Initially set to - -1, then to 0 when parent dies, or to exit_status when - child dies (whichever happens first). */ - struct lock lock; /* Lock to synchronise access to the exit_status. */ + -1, then to exit_status when child dies. */ + struct lock lock; /* Lock to synchronise access to the status and sema. */ struct semaphore sema; /* Semaphore to signal the parent that the exit_status has been set. */ struct list_elem elem; /* List element for the parent's children list. */ diff --git a/src/userprog/process.c b/src/userprog/process.c index 6c8ef2d..5b41a34 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -1,6 +1,7 @@ #include "userprog/process.h" #include #include +#include #include #include #include @@ -202,12 +203,34 @@ start_process (void *file_name_) int process_wait (tid_t child_tid UNUSED) { - /* As a temporary wait, waiting will just put the thread to sleep for one - second (TIMER_FREQ = 100 ticks ~ 1 second). */ - /* TODO: Implement process_wait () correctly. Remove the next line. */ - timer_sleep (TIMER_FREQ); - - return 0; /* TODO: Change this too */ + struct process_result *child_result = NULL; + struct list_elem *e; + struct thread *cur = thread_current (); + for (e = list_begin (&cur->child_results); + e != list_end (&cur->child_results); e = list_next (e)) + { + struct process_result *result + = list_entry (e, struct process_result, elem); + if (result->tid == child_tid) + { + child_result = result; + break; + } + /* List is ordered, allowing us to break early. */ + else if (result->tid > child_tid) + break; + } + if (child_result == NULL) + return -1; + /* Wait for child to die. */ + sema_down (&child_result->sema); + /* 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 + the struct process_result now. */ + list_remove (&child_result->elem); + int exit_status = child_result->exit_status; + free (child_result); + return exit_status; } /* Free the current process's resources. */ @@ -219,6 +242,47 @@ process_exit (void) printf ("%s: exit(%d)\n", cur->name, cur->exit_status); + /* Update process result. */ + if (cur->result != NULL) + { + lock_acquire (&cur->result->lock); + cur->result->exit_status = cur->exit_status; + /* Parent has died, child has to free the struct process_result * */ + if (sema_try_down (&cur->result->sema)) + { + lock_release (&cur->result->lock); + free (cur->result); + } + /* Parent is still alive and will be the one to free the + struct process_result *, and may be waiting so call sema_up */ + else + { + sema_up (&cur->result->sema); + lock_release (&cur->result->lock); + } + } + + struct list_elem *e; + for (e = list_begin (&cur->child_results); + e != list_end (&cur->child_results); e = list_next (e)) + { + struct process_result *result + = list_entry (e, struct process_result, elem); + lock_acquire (&result->lock); + /* Child has died (and was not waited for). Free the result. */ + if (sema_try_down (&result->sema)) + { + lock_release (&result->lock); + free (result); + } + /* Child is still alive, signal via sema that parent has died. */ + else + { + sema_up (&result->sema); + lock_release (&result->lock); + } + } + /* Destroy the current process's page directory and switch back to the kernel-only page directory. */ pd = cur->pagedir;