Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • y2-rhymansaib/pintos_student
1 result
Select Git revision
Show changes
Showing
with 364 additions and 187 deletions
# Percentage of the testing point total designated for each set of
# tests.
20.0% tests/threads/Rubric.alarm
40.0% tests/threads/Rubric.priority
40.0% tests/threads/Rubric.mlfqs
30.0% tests/threads/Rubric.alarm
70.0% tests/threads/Rubric.priority
#40.0% tests/threads/Rubric.mlfqs
......@@ -7,9 +7,9 @@ alarm-negative priority-change priority-donate-one \
priority-donate-multiple priority-donate-multiple2 \
priority-donate-nest priority-donate-sema priority-donate-lower \
priority-fifo priority-preempt priority-sema priority-condvar \
priority-donate-chain \
mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 \
mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
priority-donate-chain)
#mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2 \
#mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
# Sources for tests.
tests/threads_SRC = tests/threads/tests.c
......
/* Invokes a system call with the stack pointer (%esp) set to a
bad address. The process must be terminated with -1 exit
code.
For Project 3: The bad address lies approximately 64MB below
the code segment, so there is no ambiguity that this attempt
must be rejected even after stack growth is implemented.
Moreover, a good stack growth heuristics should probably not
grow the stack for the purpose of reading the system call
number and arguments. */
#include "tests/lib.h"
#include "tests/main.h"
......
Functionality of virtual memory subsystem:
- Test stack growth.
3 pt-grow-stack
3 pt-grow-stk-sc
3 pt-big-stk-obj
3 pt-grow-pusha
- Test paging behavior.
3 page-linear
3 page-parallel
3 page-shuffle
4 page-merge-seq
4 page-merge-par
4 page-merge-mm
4 page-merge-stk
- Test "mmap" system call.
2 mmap-read
2 mmap-write
2 mmap-shuffle
2 mmap-twice
2 mmap-unmap
1 mmap-exit
3 mmap-clean
2 mmap-close
2 mmap-remove
Robustness of virtual memory subsystem:
- Test robustness of page table support.
2 pt-bad-addr
3 pt-bad-read
2 pt-write-code
3 pt-write-code2
4 pt-grow-bad
- Test robustness of "mmap" system call.
1 mmap-bad-fd
1 mmap-inherit
1 mmap-null
1 mmap-zero
2 mmap-misalign
2 mmap-over-code
2 mmap-over-data
2 mmap-over-stk
2 mmap-overlap
char sample[] = {
"=== ALL USERS PLEASE NOTE ========================\n"
"\n"
"CAR and CDR now return extra values.\n"
"\n"
"The function CAR now returns two values. Since it has to go to the\n"
"trouble to figure out if the object is carcdr-able anyway, we figured\n"
"you might as well get both halves at once. For example, the following\n"
"code shows how to destructure a cons (SOME-CONS) into its two slots\n"
"(THE-CAR and THE-CDR):\n"
"\n"
" (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n"
"\n"
"For symmetry with CAR, CDR returns a second value which is the CAR of\n"
"the object. In a related change, the functions MAKE-ARRAY and CONS\n"
"have been fixed so they don't allocate any storage except on the\n"
"stack. This should hopefully help people who don't like using the\n"
"garbage collector because it cold boots the machine so often.\n"
};
=== ALL USERS PLEASE NOTE ========================
CAR and CDR now return extra values.
The function CAR now returns two values. Since it has to go to the
trouble to figure out if the object is carcdr-able anyway, we figured
you might as well get both halves at once. For example, the following
code shows how to destructure a cons (SOME-CONS) into its two slots
(THE-CAR and THE-CDR):
(MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)
For symmetry with CAR, CDR returns a second value which is the CAR of
the object. In a related change, the functions MAKE-ARRAY and CONS
have been fixed so they don't allocate any storage except on the
stack. This should hopefully help people who don't like using the
garbage collector because it cold boots the machine so often.
......@@ -287,7 +287,7 @@ run_task (char **argv)
#ifdef USERPROG
process_wait (process_execute (task));
#else
run_test (task);
// run_test (task);
#endif
printf ("Execution of '%s' complete.\n", task);
}
......
......@@ -32,6 +32,10 @@
#include "threads/interrupt.h"
#include "threads/thread.h"
static bool comparator_greater_thread_priority(const struct list_elem*, const struct list_elem*, void *);
static bool comparator_greater_lock_priority(const struct list_elem*, const struct list_elem*, void *);
static bool comparator_greater_sema_priority(const struct list_elem*, const struct list_elem*, void *);
/* Initializes semaphore SEMA to VALUE. A semaphore is a
nonnegative integer along with two atomic operators for
manipulating it:
......@@ -68,7 +72,8 @@ sema_down (struct semaphore *sema)
old_level = intr_disable ();
while (sema->value == 0)
{
list_push_back (&sema->waiters, &thread_current ()->elem);
list_insert_ordered (&sema->waiters, &thread_current()->elem,
comparator_greater_thread_priority, NULL);
thread_block ();
}
sema->value--;
......@@ -109,14 +114,22 @@ void
sema_up (struct semaphore *sema)
{
enum intr_level old_level;
struct thread *target = NULL;
ASSERT (sema != NULL);
old_level = intr_disable ();
if (!list_empty (&sema->waiters))
thread_unblock (list_entry (list_pop_front (&sema->waiters),
struct thread, elem));
sema->value++;
if (!list_empty (&sema->waiters)) {
list_sort(&(sema->waiters), comparator_greater_thread_priority, NULL);
// the thread of highest priority (in sema waiters) should wake up
target = list_entry (list_pop_front (&sema->waiters), struct thread, elem);
thread_unblock (target);
}
intr_set_level (old_level);
}
......@@ -178,6 +191,7 @@ lock_init (struct lock *lock)
ASSERT (lock != NULL);
lock->holder = NULL;
lock->priority = PRI_MIN;
sema_init (&lock->semaphore, 1);
}
......@@ -196,8 +210,38 @@ lock_acquire (struct lock *lock)
ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock));
// priority donation, when locking
struct lock *current_lock = lock;
struct thread *t_holder = lock->holder; // current holder thread
struct thread *t_current = thread_current();
// The current process is waiting on [lock]
t_current->waiting_lock = lock;
if(t_holder == NULL) {
current_lock->priority = t_current->priority;
}
while (t_holder != NULL && t_holder->priority < t_current->priority) {
// Donate priority to [t_holder]
thread_priority_donate(t_holder, t_current->priority);
if (current_lock->priority < t_current->priority) {
current_lock->priority = t_current->priority;
}
current_lock = t_holder->waiting_lock;
if(current_lock == NULL) break;
t_holder = current_lock->holder;
}
sema_down (&lock->semaphore);
lock->holder = thread_current ();
// lock is finally acquired.
lock->holder->waiting_lock = NULL; // no longer waiting
list_insert_ordered(&(lock->holder->locks), &(lock->lockelem),
comparator_greater_lock_priority, NULL);
}
/* Tries to acquires LOCK and returns true if successful or false
......@@ -215,8 +259,10 @@ lock_try_acquire (struct lock *lock)
ASSERT (!lock_held_by_current_thread (lock));
success = sema_try_down (&lock->semaphore);
if (success)
lock->holder = thread_current ();
if (success) {
struct thread *t_current = thread_current();
lock->holder = t_current;
}
return success;
}
......@@ -231,8 +277,30 @@ lock_release (struct lock *lock)
ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock));
struct thread *t_current = thread_current();
lock->holder = NULL;
sema_up (&lock->semaphore);
// Remove the lock from locklist
list_remove (&lock->lockelem);
// priority donation : restoration
if (list_empty(&t_current->locks)) {
// no more locks: there is no priority donors
// the original priority of the current thread
thread_priority_donate(t_current, t_current->original_priority);
}
else {
// donated: lookup the donors, find the highest priority lock
// then it should be the (newly updated) donated priority of t
list_sort(&(t_current->locks), comparator_greater_lock_priority, NULL); // TODO why it is needed?
struct lock *highest_lock = list_entry( list_front(&(t_current->locks)), struct lock, lockelem );
thread_priority_donate(t_current, highest_lock->priority);
}
}
/* Returns true if the current thread holds LOCK, false
......@@ -295,7 +363,11 @@ cond_wait (struct condition *cond, struct lock *lock)
ASSERT (lock_held_by_current_thread (lock));
sema_init (&waiter.semaphore, 0);
list_push_back (&cond->waiters, &waiter.elem);
// sort threads in condition's waiters
waiter.semaphore.priority = thread_current()->priority;
list_insert_ordered (&cond->waiters, &(waiter.elem), comparator_greater_sema_priority, NULL);
lock_release (lock);
sema_down (&waiter.semaphore);
lock_acquire (lock);
......@@ -316,10 +388,11 @@ cond_signal (struct condition *cond, struct lock *lock UNUSED)
ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock));
if (!list_empty (&cond->waiters))
if (!list_empty (&cond->waiters)) {
sema_up (&list_entry (list_pop_front (&cond->waiters),
struct semaphore_elem, elem)->semaphore);
}
}
/* Wakes up all threads, if any, waiting on COND (protected by
LOCK). LOCK must be held before calling this function.
......@@ -336,3 +409,32 @@ cond_broadcast (struct condition *cond, struct lock *lock)
while (!list_empty (&cond->waiters))
cond_signal (cond, lock);
}
/* Helpers */
static bool
comparator_greater_thread_priority(const struct list_elem* a, const struct list_elem *b, void* aux UNUSED)
{
const struct thread* x = list_entry(a, struct thread, elem);
const struct thread* y = list_entry(b, struct thread, elem);
ASSERT(x != NULL && y != NULL);
return x->priority > y->priority;
}
static bool
comparator_greater_lock_priority(const struct list_elem* a, const struct list_elem *b, void* aux UNUSED)
{
const struct lock* x = list_entry(a, struct lock, lockelem);
const struct lock* y = list_entry(b, struct lock, lockelem);
ASSERT(x != NULL && y != NULL);
return x->priority > y->priority;
}
static bool
comparator_greater_sema_priority(const struct list_elem* a, const struct list_elem *b, void* aux UNUSED)
{
const struct semaphore_elem* x = list_entry(a, struct semaphore_elem, elem);
const struct semaphore_elem* y = list_entry(b, struct semaphore_elem, elem);
ASSERT(x != NULL && y != NULL);
return x->semaphore.priority > y->semaphore.priority;
}
......@@ -9,6 +9,7 @@ struct semaphore
{
unsigned value; /* Current value. */
struct list waiters; /* List of waiting threads. */
int priority; /* Priority of semaphore */
};
void sema_init (struct semaphore *, unsigned value);
......@@ -22,6 +23,9 @@ struct lock
{
struct thread *holder; /* Thread holding lock (for debugging). */
struct semaphore semaphore; /* Binary semaphore controlling access. */
struct list_elem lockelem; /* List element for the thread's 'locks' list. */
int priority; /* priority of the the thread holding the lock (for priority donation) */
};
void lock_init (struct lock *);
......
......@@ -11,7 +11,6 @@
#include "threads/switch.h"
#include "threads/synch.h"
#include "threads/vaddr.h"
#ifdef USERPROG
#include "userprog/process.h"
#endif
......@@ -25,6 +24,9 @@
that are ready to run but not actually running. */
static struct list ready_list;
/* List of processes in sleep (wait) state (i.e. wait queue). */
static struct list wait_list;
/* List of all processes. Processes are added to this list
when they are first scheduled and removed when they exit. */
static struct list all_list;
......@@ -65,15 +67,20 @@ static void kernel_thread (thread_func *, void *aux);
static void idle (void *aux UNUSED);
static struct thread *running_thread (void);
static struct thread *next_thread_to_run (void);
static void init_thread (struct thread *, const char *name, int priority);
static bool is_thread (struct thread *) UNUSED;
static void *alloc_frame (struct thread *, size_t size);
static void schedule (void);
void thread_schedule_tail (struct thread *prev);
static tid_t allocate_tid (void);
void thread_awake (int64_t current_tick);
/* Helper (Auxiliary) functions */
static bool comparator_greater_thread_priority
(const struct list_elem *, const struct list_elem *, void *aux);
/* Initializes the threading system by transforming the code
that's currently running into a thread. This can't work in
general and it is possible in this case only because loader.S
......@@ -94,15 +101,15 @@ thread_init (void)
lock_init (&tid_lock);
list_init (&ready_list);
list_init (&wait_list);
list_init (&all_list);
/* Set up a thread structure for the running thread. */
initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT);
initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid ();
initial_thread->sleep_endtick = 0; // a dummy value
}
/* Starts preemptive thread scheduling by enabling interrupts.
......@@ -112,9 +119,6 @@ thread_start (void)
{
/* Create the idle thread. */
struct semaphore idle_started;
//init_info (initial_thread, initial_thread->tid);
sema_init (&idle_started, 0);
thread_create ("idle", PRI_MIN, idle, &idle_started);
......@@ -126,9 +130,12 @@ thread_start (void)
}
/* Called by the timer interrupt handler at each timer tick.
Thus, this function runs in an external interrupt context. */
Thus, this function runs in an external interrupt context.
The parameter 'tick' is the current tick count held by
the timer device. */
void
thread_tick (void)
thread_tick (int64_t tick)
{
struct thread *t = thread_current ();
......@@ -142,11 +149,40 @@ thread_tick (void)
else
kernel_ticks++;
/* Wake any thread whose ticks_end has been expired. */
thread_awake(tick);
/* Enforce preemption. */
if (++thread_ticks >= TIME_SLICE)
intr_yield_on_return ();
}
/* Wake up all sleeping threads whose ticks_end has been expired,
removing from the wait queue and pushing it into the ready queue.
This function must be called with interrupts turned off. */
void
thread_awake (int64_t current_tick) {
struct list_elem *e;
// interrupt level check
ASSERT (intr_get_level () == INTR_OFF);
// enumerate all threads in wait queue
for (e = list_begin (&wait_list); e != list_end (&wait_list); e = list_next (e))
{
struct thread *t = list_entry (e, struct thread, waitelem);
if (t->sleep_endtick <= current_tick) {
/* sleep is expired. awake up t */
t->sleep_endtick = 0;
list_remove (&t->waitelem);
// Add to run queue.
thread_unblock (t);
}
}
}
/* Prints thread statistics. */
void
thread_print_stats (void)
......@@ -209,9 +245,36 @@ thread_create (const char *name, int priority,
/* Add to run queue. */
thread_unblock (t);
/* If the newly created thread has a higher priority than
* the currently running thread, then there should be an
* immediate context switching, for thread priority scheduling. */
if (priority > thread_current()->priority) {
// current thread releases off its running
thread_yield();
}
return tid;
}
/* Make the current thread T to sleep, until the timer ticks
reaches 'ticks_end'.
It subsequently calls thread_block(), making T sleep actually.
This function must be called with interrupts turned off. */
void
thread_sleep_until (int64_t ticks_end)
{
struct thread *t = thread_current();
t->sleep_endtick = ticks_end;
// put T into the wait queue
list_push_back (&wait_list, &t->waitelem);
// make the current thread block (sleeped)
thread_block();
}
/* Puts the current thread to sleep. It will not be scheduled
again until awoken by thread_unblock().
......@@ -245,8 +308,17 @@ thread_unblock (struct thread *t)
old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED);
list_push_back (&ready_list, &t->elem);
// t will turn into ready-to-run state : inserting into ready_list
// maintaining in the non-decreasing order of thread priority
list_insert_ordered (&ready_list, &t->elem, comparator_greater_thread_priority, NULL);
t->status = THREAD_READY;
// ensure preemption : compare priorities of current thread and t (to be unblocked),
if (thread_current() != idle_thread && thread_current()->priority < t->priority )
thread_yield();
intr_set_level (old_level);
}
......@@ -294,6 +366,15 @@ thread_exit (void)
process_exit ();
#endif
struct thread *curr = thread_current();
// release all locks
struct list_elem *e;
for (e = list_begin (&curr->locks); e != list_end (&curr->locks); e = list_next (e)) {
struct lock *lock = list_entry(e, struct lock, lockelem);
lock_release(lock);
}
/* Remove thread from all threads list, set our status to dying,
and schedule another process. That process will destroy us
when it calls thread_schedule_tail(). */
......@@ -315,8 +396,10 @@ thread_yield (void)
ASSERT (!intr_context ());
old_level = intr_disable ();
if (cur != idle_thread)
list_push_back (&ready_list, &cur->elem);
if (cur != idle_thread) {
// t will turn into ready-to-run state : inserting into ready_list
list_insert_ordered (&ready_list, &cur->elem, comparator_greater_thread_priority, NULL);
}
cur->status = THREAD_READY;
schedule ();
intr_set_level (old_level);
......@@ -339,13 +422,53 @@ thread_foreach (thread_action_func *func, void *aux)
}
}
/* Sets the current thread's priority to NEW_PRIORITY. */
/* Sets the current thread's priority to NEW_PRIORITY when a donation
was not performed. */
void
thread_set_priority (int new_priority)
{
thread_current ()->priority = new_priority;
// if the current thread has no donation, then it is normal priority change request.
struct thread *t_current = thread_current();
if (t_current->priority == t_current->original_priority) {
t_current->priority = new_priority;
t_current->original_priority = new_priority;
}
// otherwise, it has a donation: the original priority only should have changed
else {
t_current->original_priority = new_priority;
}
// if current thread gets its priority decreased, then yield
// (foremost entry in ready_list shall have the highest priority)
if (!list_empty (&ready_list)) {
struct thread *next = list_entry(list_begin(&ready_list), struct thread, elem);
if (next != NULL && next->priority > new_priority) {
thread_yield();
}
}
}
/* Let the thread [target] be donated the priority. */
void
thread_priority_donate(struct thread *target, int new_priority)
{
// donation : change only current priority
target->priority = new_priority;
// if current thread gets its priority decreased, then yield
// (foremost entry in ready_list shall have the highest priority)
if (target == thread_current() && !list_empty (&ready_list)) {
struct thread *next = list_entry(list_begin(&ready_list), struct thread, elem);
if (next != NULL && next->priority > new_priority) {
thread_yield();
}
}
}
/* Returns the current thread's priority. */
int
thread_get_priority (void)
......@@ -470,15 +593,24 @@ init_thread (struct thread *t, const char *name, int priority)
strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE;
t->priority = priority;
t->original_priority = priority;
t->waiting_lock = NULL;
list_init (&t->locks);
t->sleep_endtick = 0;
t->magic = THREAD_MAGIC;
//t->is_kernel = is_kernel;
old_level = intr_disable ();
list_push_back (&all_list, &t->allelem);
intr_set_level (old_level);
}
#ifdef USERPROG
// init process-related informations.
t->pcb = NULL;
list_init(&t->child_list);
list_init(&t->file_descriptors);
t->executing_file = NULL;
#endif
}
/* Allocates a SIZE-byte frame at the top of thread T's stack and
returns a pointer to the frame's base. */
......@@ -589,7 +721,25 @@ allocate_tid (void)
return tid;
}
/* Helper function: implementations */
// A comparator function for thread priority, w.r.t ready_list element.
// returns true iff (thread a)'s priority > (thread b)'s priority.
static bool
comparator_greater_thread_priority (
const struct list_elem *a,
const struct list_elem *b, void *aux UNUSED)
{
struct thread *ta, *tb;
ASSERT (a != NULL);
ASSERT (b != NULL);
ta = list_entry (a, struct thread, elem);
tb = list_entry (b, struct thread, elem);
return ta->priority > tb->priority;
}
/* Offset of `stack' member within `struct thread'.
Used by switch.S, which can't figure it out on its own. */
uint32_t thread_stack_ofs = offsetof (struct thread, stack);
......@@ -88,14 +88,30 @@ struct thread
char name[16]; /* Name (for debugging purposes). */
uint8_t *stack; /* Saved stack pointer. */
int priority; /* Priority. */
int original_priority; /* Priority, before donation */
struct list_elem allelem; /* List element for all threads list. */
struct list_elem waitelem; /* List element, stored in the wait_list queue */
int64_t sleep_endtick; /* The tick after which the thread should awake (if the thread is in sleep) */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
struct list_elem elem; /* List element, stored in the ready_list queue */
// needed for priority donations
struct lock *waiting_lock; /* The lock object on which this thread is waiting (or NULL if not locked) */
struct list locks; /* List of locks the thread holds (for multiple donations) */
#ifdef USERPROG
/* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */
struct process_control_block *pcb; /* Process Control Block */
struct list child_list; /* List of children processes of this thread,
each elem is defined by pcb#elem */
struct list file_descriptors; /* List of file_descriptors the thread contains */
struct file *executing_file; /* The executable file of associated process. */
#endif
/* Owned by thread.c. */
......@@ -110,7 +126,7 @@ extern bool thread_mlfqs;
void thread_init (void);
void thread_start (void);
void thread_tick (void);
void thread_tick (int64_t tick);
void thread_print_stats (void);
typedef void thread_func (void *aux);
......@@ -119,6 +135,8 @@ tid_t thread_create (const char *name, int priority, thread_func *, void *);
void thread_block (void);
void thread_unblock (struct thread *);
void thread_sleep_until (int64_t wake_tick);
struct thread *thread_current (void);
tid_t thread_tid (void);
const char *thread_name (void);
......@@ -132,6 +150,7 @@ void thread_foreach (thread_action_func *, void *);
int thread_get_priority (void);
void thread_set_priority (int);
void thread_priority_donate(struct thread *, int priority);
int thread_get_nice (void);
void thread_set_nice (int);
......