Compare commits

...

9 Commits

Author SHA1 Message Date
aedb72246b fix: do not acquire filesys_lock for tell and seek 2024-11-24 15:41:18 +00:00
e1f0258f8e fix: handle malloc result in init_process_result 2024-11-24 15:09:32 +00:00
Saleh Bubshait
eed4ce5130 Merge branch 'single-exit-status' into 'master'
Use a single `exit_status` instead of two

See merge request lab2425_autumn/pintos_22!50
2024-11-15 18:08:25 +00:00
8567434231 Use a single exit_status instead of two 2024-11-15 17:28:04 +00:00
e76712d3fd Merge branch 'task2/thread-init-bug-fix' into 'master'
Fix Bug in fd_counter initialisation when USERPROG is not defined

See merge request lab2425_autumn/pintos_22!49
2024-11-15 17:17:14 +00:00
sBubshait
8f82f9d747 Fix Bug in fd_counter initialisation when USERPROG is not defined 2024-11-15 16:57:16 +00:00
Saleh Bubshait
9ee29ac40a Merge branch 'child-results-hash-table' into 'master'
Implement hash table for child process results

See merge request lab2425_autumn/pintos_22!48
2024-11-15 16:48:41 +00:00
2566948a32 Implement hash table for child process results 2024-11-15 16:45:04 +00:00
222aeff90f Merge branch 'task2/refactoring/saleh' into 'master'
Refactor System Calls and Process for Readability; Change FD to local counter and use it as hash

See merge request lab2425_autumn/pintos_22!47
2024-11-15 16:41:12 +00:00
4 changed files with 102 additions and 97 deletions

View File

@@ -71,7 +71,7 @@ static void kernel_thread (thread_func *, void *aux);
static void idle (void *aux UNUSED); static void idle (void *aux UNUSED);
static struct thread *running_thread (void); static struct thread *running_thread (void);
static struct thread *next_thread_to_run (void); static struct thread *next_thread_to_run (void);
static void init_process_result (struct thread *t); static bool init_process_result (struct thread *t);
static void init_thread (struct thread *, const char *name, int nice, static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu); int priority, fp32_t recent_cpu);
static bool is_thread (struct thread *) UNUSED; static bool is_thread (struct thread *) UNUSED;
@@ -84,6 +84,10 @@ void thread_schedule_tail (struct thread *prev);
static tid_t allocate_tid (void); static tid_t allocate_tid (void);
static bool donor_priority_less (const struct list_elem *a_, static bool donor_priority_less (const struct list_elem *a_,
const struct list_elem *b_, void *aux UNUSED); const struct list_elem *b_, void *aux UNUSED);
static unsigned process_result_hash (const struct hash_elem *e,
void *aux UNUSED);
static bool process_result_less (const struct hash_elem *a,
const struct hash_elem *b, void *aux UNUSED);
/* Initializes the threading system by transforming the code /* Initializes the threading system by transforming the code
that's currently running into a thread. This can't work in that's currently running into a thread. This can't work in
@@ -122,6 +126,13 @@ thread_init (void)
void void
thread_start (void) thread_start (void)
{ {
/* Malloc has been initalised, we can allocate the child results table
for the main thread. */
struct thread *t = thread_current ();
if (!hash_init (&t->child_results, process_result_hash, process_result_less,
t))
PANIC ("Failed to initialise child results table for main thread.");
/* Create the idle thread. */ /* Create the idle thread. */
struct semaphore idle_started; struct semaphore idle_started;
sema_init (&idle_started, 0); sema_init (&idle_started, 0);
@@ -241,11 +252,25 @@ thread_create (const char *name, int priority,
struct thread *parent_thread = thread_current (); struct thread *parent_thread = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu); init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
tid = t->tid = allocate_tid (); tid = t->tid = allocate_tid ();
init_process_result (t); if (!init_process_result (t))
{
palloc_free_page (t);
return TID_ERROR;
}
#ifdef USERPROG #ifdef USERPROG
hash_init (&t->open_files, fd_hash, fd_less, NULL); /* Initialize the thread's file descriptor table. */
#endif t->fd_counter = MINIMUM_USER_FD;
if (!hash_init (&t->open_files, fd_hash, fd_less, NULL)
|| !hash_init (&t->child_results, process_result_hash,
process_result_less, t))
{
palloc_free_page (t);
free (t->result);
return TID_ERROR;
}
#endif
/* Prepare thread for first run by initializing its stack. /* Prepare thread for first run by initializing its stack.
Do this atomically so intermediate values for the 'stack' Do this atomically so intermediate values for the 'stack'
@@ -269,9 +294,7 @@ thread_create (const char *name, int priority,
intr_set_level (old_level); intr_set_level (old_level);
/* No need to synchronise child_results since it is only ever accessed by one hash_insert (&parent_thread->child_results, &t->result->elem);
thread. By the nature of increasing TIDs, this list is ordered. */
list_push_back (&parent_thread->child_results, &t->result->elem);
/* Add to run queue. */ /* Add to run queue. */
thread_unblock (t); thread_unblock (t);
@@ -649,15 +672,18 @@ is_thread (struct thread *t)
} }
/* Allocate and initialise a process result for given thread. */ /* Allocate and initialise a process result for given thread. */
static void static bool
init_process_result (struct thread *t) init_process_result (struct thread *t)
{ {
struct process_result *result = malloc (sizeof (struct process_result)); struct process_result *result = malloc (sizeof (struct process_result));
if (result == NULL)
return false;
result->tid = t->tid; result->tid = t->tid;
result->exit_status = t->exit_status; result->exit_status = -1;
lock_init (&result->lock); lock_init (&result->lock);
sema_init (&result->sema, 0); sema_init (&result->sema, 0);
t->result = result; t->result = result;
return true;
} }
/* Does basic initialization of T as a blocked thread named /* Does basic initialization of T as a blocked thread named
@@ -688,10 +714,6 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
t->recent_cpu = recent_cpu; t->recent_cpu = recent_cpu;
t->priority = t->base_priority; t->priority = t->base_priority;
t->fd_counter = MINIMUM_USER_FD;
t->exit_status = -1;
list_init (&t->child_results);
old_level = intr_disable (); old_level = intr_disable ();
list_push_back (&all_list, &t->allelem); list_push_back (&all_list, &t->allelem);
intr_set_level (old_level); intr_set_level (old_level);
@@ -822,6 +844,29 @@ allocate_tid (void)
return tid; return tid;
} }
/* Hashing function needed for child_results table.
Returns hash of process_result's TID. */
static unsigned
process_result_hash (const struct hash_elem *e, void *aux UNUSED)
{
const struct process_result *result
= hash_entry (e, struct process_result, elem);
return hash_int (result->tid);
}
/* Comparator function needed for child_results table.
Returns less than comparison on process_results' TIDs. */
static bool
process_result_less (const struct hash_elem *a_, const struct hash_elem *b_,
void *aux UNUSED)
{
const struct process_result *a
= hash_entry (a_, struct process_result, elem);
const struct process_result *b
= hash_entry (b_, struct process_result, elem);
return a->tid < b->tid;
}
/* Offset of `stack' member within `struct thread'. /* Offset of `stack' member within `struct thread'.
Used by switch.S, which can't figure it out on its own. */ Used by switch.S, which can't figure it out on its own. */
uint32_t thread_stack_ofs = offsetof (struct thread, stack); uint32_t thread_stack_ofs = offsetof (struct thread, stack);

View File

@@ -44,7 +44,7 @@ struct process_result
struct lock lock; /* Lock the exit_status and sema. */ 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 hash_elem elem; /* Hash element for the parent's children map. */
}; };
/* A kernel thread or user process. /* A kernel thread or user process.
@@ -128,15 +128,13 @@ struct thread
/* Process wait properties. */ /* Process wait properties. */
struct process_result *result; /* Result of the process. */ struct process_result *result; /* Result of the process. */
struct list child_results; /* List of children's of this thread struct hash child_results; /* Map of children's of this thread
process results. */ TID to process result. */
struct file *exec_file; /* Thread's currently running file */ struct file *exec_file; /* Thread's currently running file */
/* Shared between thread.c and synch.c. */ /* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */ struct list_elem elem; /* List element. */
int exit_status; /* Exit Status: 0 = successful exit. */
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */

View File

@@ -1,5 +1,6 @@
#include "userprog/process.h" #include "userprog/process.h"
#include <debug.h> #include <debug.h>
#include <hash.h>
#include <inttypes.h> #include <inttypes.h>
#include <list.h> #include <list.h>
#include <round.h> #include <round.h>
@@ -55,6 +56,7 @@ struct process_start_data
}; };
static thread_func start_process NO_RETURN; static thread_func start_process NO_RETURN;
static void destruct_process_result (struct hash_elem *e, void *aux UNUSED);
static bool load (const char *cmdline, void (**eip) (void), void **esp); static bool load (const char *cmdline, void (**eip) (void), void **esp);
/* Starts a new thread running a user program executed via /* Starts a new thread running a user program executed via
@@ -315,32 +317,15 @@ push_to_stack (void **esp, void *data, size_t data_size)
int int
process_wait (tid_t child_tid) process_wait (tid_t child_tid)
{ {
struct process_result *child_result = NULL; struct thread *t = thread_current ();
struct list_elem *e; struct process_result fake_result;
struct thread *cur = thread_current (); fake_result.tid = child_tid;
struct hash_elem *e = hash_find (&t->child_results, &fake_result.elem);
for (e = list_begin (&cur->child_results); if (e == NULL)
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)
{
/* Found the child process. */
child_result = result;
break;
}
/* List is ordered, allowing us to break early if the child_tid is
greater than the current result's tid. */
else if (result->tid > child_tid)
break;
}
/* If the child process was not found, return -1. */
if (child_result == NULL)
return -1; return -1;
struct process_result *child_result
= hash_entry (e, struct process_result, elem);
/* Wait for child to die. */ /* Wait for child to die. */
sema_down (&child_result->sema); sema_down (&child_result->sema);
@@ -348,18 +333,17 @@ process_wait (tid_t child_tid)
wait) for it here to ensure we don't free the lock memory before it is wait) for it here to ensure we don't free the lock memory before it is
released in process_exit. */ released in process_exit. */
lock_acquire (&child_result->lock); lock_acquire (&child_result->lock);
/* To prevent waiting for child twice, remove it from the table.
/* 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); hash_delete (&t->child_results, &child_result->elem);
/* Get the exit status of the child */ /* Get the exit status of the child */
int exit_status = child_result->exit_status; int exit_status = child_result->exit_status;
/* Release the lock */ /* Release the lock */
lock_release (&child_result->lock); lock_release (&child_result->lock);
/* Result no-longer used by parent, nor child. Deallocate it. */
free (child_result); free (child_result);
return exit_status; return exit_status;
} }
@@ -371,8 +355,6 @@ process_exit (void)
struct thread *cur = thread_current (); struct thread *cur = thread_current ();
uint32_t *pd; uint32_t *pd;
printf ("%s: exit(%d)\n", cur->name, cur->exit_status);
/* Clean up all open files */ /* Clean up all open files */
hash_destroy (&cur->open_files, fd_cleanup); hash_destroy (&cur->open_files, fd_cleanup);
@@ -385,49 +367,15 @@ process_exit (void)
lock_release (&filesys_lock); lock_release (&filesys_lock);
} }
/* Update process result. */
if (cur->result != NULL) if (cur->result != NULL)
{ {
lock_acquire (&cur->result->lock); printf ("%s: exit(%d)\n", cur->name, cur->result->exit_status);
cur->result->exit_status = cur->exit_status; /* Update own process result. */
/* Parent has died, child has to free the struct process_result * */ destruct_process_result (&cur->result->elem, cur);
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);
}
} }
/* Free child process results or signal parent's death. */ /* Free child process results or signal parent's death. */
struct list_elem *e; hash_destroy (&cur->child_results, destruct_process_result);
for (e = list_begin (&cur->child_results);
e != list_end (&cur->child_results);)
{
struct process_result *result
= list_entry (e, struct process_result, elem);
struct list_elem *next = list_next (e);
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);
}
e = next;
}
/* Destroy the current process's page directory and switch back /* Destroy the current process's page directory and switch back
to the kernel-only page directory. */ to the kernel-only page directory. */
@@ -447,6 +395,28 @@ process_exit (void)
} }
} }
/* Destruct a process_result, with multi-thread awareness.
If the other thread is running, simply signals death. Otherwise
frees the result. */
static void
destruct_process_result (struct hash_elem *e, void *aux UNUSED)
{
struct process_result *result = hash_entry (e, struct process_result, elem);
lock_acquire (&result->lock);
/* Other thread has died (and was not waited for). Free the result. */
if (sema_try_down (&result->sema))
{
lock_release (&result->lock);
free (result);
}
/* Other thread is still alive, signal via sema that parent has died. */
else
{
sema_up (&result->sema);
lock_release (&result->lock);
}
}
/* Sets up the CPU for running user code in the current /* Sets up the CPU for running user code in the current
thread. thread.
This function is called on every context switch. */ This function is called on every context switch. */

View File

@@ -131,7 +131,7 @@ syscall_exit (int status)
{ {
/* Sets exit_status of the thread to status. thread_exit () will call /* Sets exit_status of the thread to status. thread_exit () will call
process_exit () if user programs are allowed. */ process_exit () if user programs are allowed. */
thread_current ()->exit_status = status; thread_current ()->result->exit_status = status;
thread_exit (); thread_exit ();
} }
@@ -271,7 +271,7 @@ syscall_read (int fd, void *buffer, unsigned size)
{ {
/* Reading from the console. */ /* Reading from the console. */
char *write_buffer = buffer; char *write_buffer = buffer;
for (int i = 0; i < size; i++) for (unsigned i = 0; i < size; i++)
write_buffer[i] = input_getc (); write_buffer[i] = input_getc ();
/* In case of console, read is always (eventually) successful. So return /* In case of console, read is always (eventually) successful. So return
@@ -348,12 +348,7 @@ syscall_seek (int fd, unsigned position)
/* Find the file from the FD. If it does not exist, do nothing. */ /* Find the file from the FD. If it does not exist, do nothing. */
struct open_file *file_info = fd_get_file (fd); struct open_file *file_info = fd_get_file (fd);
if (file_info != NULL) if (file_info != NULL)
{
/* File exists: Acquire the file system lock to prevent race conditions. */
lock_acquire (&filesys_lock);
file_seek (file_info->file, position); file_seek (file_info->file, position);
lock_release (&filesys_lock);
}
} }
/* Handles the syscall for returning the next byte in a file referenced by /* Handles the syscall for returning the next byte in a file referenced by
@@ -367,10 +362,7 @@ syscall_tell (int fd)
if (file_info == NULL) if (file_info == NULL)
return 0; return 0;
/* Acquire the file system lock to prevent race conditions. */
lock_acquire (&filesys_lock);
unsigned pos = file_tell (file_info->file); unsigned pos = file_tell (file_info->file);
lock_release (&filesys_lock);
/* Return the current position in the file. */ /* Return the current position in the file. */
return pos; return pos;