Compare commits

..

25 Commits

Author SHA1 Message Date
1e09712cc2 fix mlfqs tests, complete BSD scheduling 2024-10-17 02:38:57 +01:00
c767cfe159 Ignore thread_set_priority when BSD enabled 2024-10-16 22:58:03 +01:00
eacd93b32d Optimise load_avg and recent_cpu updates
- only run when BSD scheduler is enabled
2024-10-16 22:56:47 +01:00
de8f303fa2 Implement BSD calculations
- load_avg, recent_cpu, priority calculations
- reduce frac bits to 14
2024-10-16 22:50:46 +01:00
5967257bb0 Fix fixed-point returns 2024-10-16 19:48:08 +01:00
EDiasAlberto
4a5de13d1e implement recent_cpu calculations on every second 2024-10-16 18:45:28 +01:00
EDiasAlberto
7a1aa21e1e implement logic to increment recent_cpu on every timer_interrupt () call 2024-10-16 18:34:56 +01:00
EDiasAlberto
0db3551a9a implement behaviour for thread recent_cpu to be defined based on parent recent_cpu, 0 for initial thread 2024-10-16 18:27:21 +01:00
Dias Alberto, Ethan
96fa718be1 Merge branch 'BSD-merged' into 'ethan-BSD'
Bring personal branch up to date

See merge request lab2425_autumn/pintos_22!5
2024-10-16 17:03:17 +00:00
Dias Alberto, Ethan
51d208f3ef Merge branch 'gleb/BSD' into 'BSD-merged'
Merge rewritten inline funcs into main BSD repo

See merge request lab2425_autumn/pintos_22!4
2024-10-16 17:02:13 +00:00
63742c5717 implement thread_get_nice & thread_get_recent_cpu 2024-10-15 19:56:46 +01:00
27d564ab49 inline funcs instead of macros for fixed-point 2024-10-15 19:45:11 +01:00
EDiasAlberto
efed660968 implement thread_set_nice and make skeleton for calculate_priority 2024-10-15 19:25:02 +01:00
EDiasAlberto
ab66551c06 implement thread_get_nice 2024-10-15 19:21:15 +01:00
EDiasAlberto
3e379acd5e modify gitignore to ignore vscode files 2024-10-15 17:27:16 +01:00
EDiasAlberto
2834af032d fix bracketing issue in ROUNDING_FP_TO_INT 2024-10-15 17:23:03 +01:00
EDiasAlberto
df89bda71e modify thread struct to track thread niceness and recent_cpu time 2024-10-15 17:09:06 +01:00
EDiasAlberto
5178b72370 comment fixed point arithmetic header 2024-10-15 15:34:06 +01:00
EDiasAlberto
724b6065f7 implement macros for fp multiplication and division 2024-10-15 15:24:34 +01:00
EDiasAlberto
1f1ffe4470 add brackets to fixed-point conversion_const calculation for clarity 2024-10-15 15:15:16 +01:00
EDiasAlberto
42b0ff9d17 define basic fixed-point arithmetic operations 2024-10-15 15:13:40 +01:00
EDiasAlberto
4e271ea5ab rewrite fixed-point.h to not have magic numbers 2024-10-15 15:04:26 +01:00
EDiasAlberto
4f6849c4a4 fix gitignore formatting 2024-10-15 14:59:57 +01:00
EDiasAlberto
1eb9abfdcd update gitignore to remove CLion files 2024-10-15 14:58:35 +01:00
EDiasAlberto
1042295e5f define fixed point arithmetic constants 2024-10-15 14:57:37 +01:00
8 changed files with 87 additions and 425 deletions

7
.gitignore vendored
View File

@@ -4,13 +4,6 @@
#ignore pdf files (just keep source files) #ignore pdf files (just keep source files)
*.pdf *.pdf
#ignore Mac OS generated files
.DS_Store
#ignore code editor generated directories
.idea
.vscode
#ignore junk files from latex output #ignore junk files from latex output
*.out *.out
*.log *.log

View File

@@ -1,44 +0,0 @@
stages:
- test
.pintos_tests:
stage: test
image: gitlab.doc.ic.ac.uk:4567/lab2425_autumn/pintos_22/pintos-testing:latest
artifacts:
when: always
paths:
- src/$DIR/build/tests/$DIR/
before_script:
- cd src/utils
- make
- export PATH=$PWD:$PATH
- cd ../..
script:
- cd src/$DIR
- make check | tee build.log
- grep -q "FAIL tests/$DIR" build.log && exit 1 || exit 0
test_devices:
extends: .pintos_tests
variables:
DIR: devices
test_filesys:
extends: .pintos_tests
variables:
DIR: filesys
test_threads:
extends: .pintos_tests
variables:
DIR: threads
test_userprog:
extends: .pintos_tests
variables:
DIR: userprog
test_vm:
extends: .pintos_tests
variables:
DIR: vm

View File

@@ -1,3 +0,0 @@
FROM debian:12-slim
RUN apt update && apt install gcc perl make qemu-system-i386 gdb -y

View File

@@ -119,6 +119,9 @@ timer_sleep (int64_t ticks)
NULL); NULL);
intr_set_level (old_level); intr_set_level (old_level);
sema_down (&st.semaphore); sema_down (&st.semaphore);
old_level = intr_disable ();
list_remove (&st.elem);
intr_set_level (old_level);
} }
/* Sleeps for approximately MS milliseconds. Interrupts must be /* Sleeps for approximately MS milliseconds. Interrupts must be
@@ -201,10 +204,7 @@ timer_interrupt (struct intr_frame *args UNUSED)
{ {
struct asleep_thread *st = list_entry (e, struct asleep_thread, elem); struct asleep_thread *st = list_entry (e, struct asleep_thread, elem);
if (ticks >= st->end_at) if (ticks >= st->end_at)
{ sema_up (&st->semaphore);
list_remove (&st->elem);
sema_up (&st->semaphore);
}
else else
break; break;
} }

View File

@@ -3,40 +3,37 @@
#define FIXED_POINT_H #define FIXED_POINT_H
typedef struct typedef struct
{ {
int32_t raw; int32_t raw;
} fp32_t; } fp32_t;
/* Fixed Point Arithmetic bit count constants */ /* Fixed Point Arithmetic bit count constants */
#define NUM_FRAC_BITS 14 #define NUM_FRAC_BITS 14
#define NUM_INT_BITS (31 - NUM_FRAC_BITS) #define NUM_INT_BITS (31 - NUM_FRAC_BITS)
#define CONVERSION_FACTOR (1 << NUM_FRAC_BITS) /* f = 2^q, (2^14) */ #define CONVERSION_CONST (1 << NUM_FRAC_BITS) /* f = 2^q, (2^20) */
/* Fixed Point Arithmetic conversion operations */ /* Fixed Point Arithmetic conversion operations */
/* Converts an integer n to a fixed point number */ /* Converts an integer n to a fixed point number */
inline fp32_t inline fp32_t
fp_from_int (int32_t n) int_to_fp (int32_t n)
{ {
return (fp32_t){ n * CONVERSION_FACTOR }; return (fp32_t){ n * CONVERSION_CONST };
} }
/* Handles conversion of fixed point to integer, /* Handles conversion of fixed point to integer. First version truncates, second one rounds */
with truncation */
inline int32_t inline int32_t
fp_floor (fp32_t x) fp_floor (fp32_t x)
{ {
return x.raw / CONVERSION_FACTOR; return x.raw / CONVERSION_CONST;
} }
/* Handles conversion of fixed point to integer,
with rounding */
inline int32_t inline int32_t
fp_round (fp32_t x) fp_round (fp32_t x)
{ {
if (x.raw >= 0) if (x.raw >= 0)
return (x.raw + CONVERSION_FACTOR / 2) / CONVERSION_FACTOR; return (x.raw + CONVERSION_CONST / 2) / CONVERSION_CONST;
else else
return (x.raw - CONVERSION_FACTOR / 2) / CONVERSION_FACTOR; return (x.raw - CONVERSION_CONST / 2) / CONVERSION_CONST;
} }
/* Add two fixed points */ /* Add two fixed points */
@@ -53,20 +50,32 @@ fp_sub (fp32_t x, fp32_t y)
return (fp32_t){ x.raw - y.raw }; return (fp32_t){ x.raw - y.raw };
} }
/* Add fixed point to integer */
inline fp32_t
fp_add_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw + n * CONVERSION_CONST };
}
/* Subtract integer from fixed point */
inline fp32_t
fp_sub_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw - n * CONVERSION_CONST };
}
/* Multiple two fixed points */ /* Multiple two fixed points */
inline fp32_t inline fp32_t
fp_mul (fp32_t x, fp32_t y) fp_mul (fp32_t x, fp32_t y)
{ {
return (fp32_t){ ((int64_t)x.raw) * y.raw / CONVERSION_FACTOR }; return (fp32_t){ ((int64_t)x.raw) * y.raw / CONVERSION_CONST };
} }
/* Divide two fixed points */ /* Divide two fixed points */
inline fp32_t inline fp32_t
fp_div (fp32_t x, fp32_t y) fp_div (fp32_t x, fp32_t y)
{ {
return (fp32_t){ ((int64_t)x.raw) * CONVERSION_FACTOR / y.raw }; return (fp32_t){ ((int64_t)x.raw) * CONVERSION_CONST / y.raw };
} }
/* Multiply fixed point and integer */ /* Multiply fixed point and integer */
@@ -83,18 +92,4 @@ fp_div_int (fp32_t x, int32_t n)
return (fp32_t){ x.raw / n }; return (fp32_t){ x.raw / n };
} }
/* Add fixed point to integer */
inline fp32_t
fp_add_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw + n * CONVERSION_FACTOR };
}
/* Subtract integer from fixed point */
inline fp32_t
fp_sub_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw - n * CONVERSION_FACTOR };
}
#endif //FIXED_POINT_H #endif //FIXED_POINT_H

View File

@@ -32,10 +32,6 @@
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/thread.h" #include "threads/thread.h"
static bool
priority_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED);
/* Initializes semaphore SEMA to VALUE. A semaphore is a /* Initializes semaphore SEMA to VALUE. A semaphore is a
nonnegative integer along with two atomic operators for nonnegative integer along with two atomic operators for
manipulating it: manipulating it:
@@ -110,31 +106,18 @@ sema_try_down (struct semaphore *sema)
This function may be called from an interrupt handler. */ This function may be called from an interrupt handler. */
void void
sema_up (struct semaphore *sema) sema_up (struct semaphore *sema)
{ {
enum intr_level old_level; enum intr_level old_level;
ASSERT (sema != NULL); ASSERT (sema != NULL);
old_level = intr_disable (); old_level = intr_disable ();
if (!list_empty (&sema->waiters)) if (!list_empty (&sema->waiters))
{ thread_unblock (list_entry (list_pop_front (&sema->waiters),
/* Enforces wake-up of the highest priority thread waiting for the struct thread, elem));
semaphore. */
struct list_elem *e = list_max (&sema->waiters, priority_less, NULL);
list_remove (e);
thread_unblock (list_entry (e, struct thread, elem));
}
sema->value++; sema->value++;
intr_set_level (old_level); intr_set_level (old_level);
/* Yields the CPU in case the thread that has been woken up has a higher
priority that the current running thread, including the case when called
within an interrupt handler. */
if (intr_context ())
intr_yield_on_return ();
else
thread_yield ();
} }
static void sema_test_helper (void *sema_); static void sema_test_helper (void *sema_);
@@ -198,47 +181,6 @@ lock_init (struct lock *lock)
sema_init (&lock->semaphore, 1); sema_init (&lock->semaphore, 1);
} }
/* Current thread donates its priority to donee, iteratively
propagating the donation in the case of chains in the wait-for graph.
Also keeps track of the donation by updating the donors list. Expects
interrupts to be disabled. */
static void
donate_priority (struct thread *donee) {
ASSERT (intr_get_level () == INTR_OFF);
struct thread *donor = thread_current ();
list_push_back (&donee->donors_list, &donor->donor_elem);
while (donee != NULL)
{
/* Stop propagation of donation once a donee is reached that has
a higher effective priority (as its donees can't have less
priority than that being donated). */
if (donor->priority <= donee->priority)
break;
/* Also stop propagation of donation once a donee is reached with
no donees of its own (sink node in WFG). */
if (donee->waiting_lock == NULL)
{
/* Only the sink node of the WFG isn't waiting for a lock and
could be on the ready list. Thus, as its priority changed,
it must be reinserted into the list. */
enum intr_level old_level = intr_disable ();
donee->priority = donor->priority;
ready_list_reinsert (donee);
intr_set_level (old_level);
donee = NULL;
}
else
{
donee->priority = donor->priority;
donee = donee->waiting_lock->holder;
}
}
}
/* Acquires LOCK, sleeping until it becomes available if /* Acquires LOCK, sleeping until it becomes available if
necessary. The lock must not already be held by the current necessary. The lock must not already be held by the current
thread. thread.
@@ -254,19 +196,8 @@ lock_acquire (struct lock *lock)
ASSERT (!intr_context ()); ASSERT (!intr_context ());
ASSERT (!lock_held_by_current_thread (lock)); ASSERT (!lock_held_by_current_thread (lock));
struct thread *t = thread_current ();
enum intr_level old_level = intr_disable ();
if (lock->holder != NULL)
{
t->waiting_lock = lock;
donate_priority (lock->holder);
}
intr_set_level (old_level);
sema_down (&lock->semaphore); sema_down (&lock->semaphore);
lock->holder = thread_current (); lock->holder = thread_current ();
t->waiting_lock = NULL;
} }
/* Tries to acquires LOCK and returns true if successful or false /* Tries to acquires LOCK and returns true if successful or false
@@ -300,51 +231,6 @@ lock_release (struct lock *lock)
ASSERT (lock != NULL); ASSERT (lock != NULL);
ASSERT (lock_held_by_current_thread (lock)); ASSERT (lock_held_by_current_thread (lock));
struct thread *current_thread = thread_current ();
struct thread *max_donor = NULL;
struct list orphan_list;
list_init (&orphan_list);
enum intr_level old_level = intr_disable ();
/* Loop through current thread's donors, removing the ones waiting for the
lock being released and keeping track of them (within orphan_list).
Also identifies the highest priority donor thread among them. */
struct list_elem *tail = list_tail (&current_thread->donors_list);
struct list_elem *e = list_begin (&current_thread->donors_list);
while (e != tail)
{
struct thread *donor = list_entry (e, struct thread, donor_elem);
struct list_elem *next = list_next (e);
/* Excludes donors that aren't waiting for the lock being released,
and tracks the rest. */
if (donor->waiting_lock == lock)
{
list_remove (e);
list_push_back (&orphan_list, e);
/* Identify highest priority donor. */
if (max_donor == NULL || donor->priority > max_donor->priority)
max_donor = donor;
}
e = next;
}
/* If there exists a maximum donor thread waiting for this lock to be
released, transfer the remaining orphaned donors to its donor list. */
if (max_donor != NULL)
{
while (!list_empty (&orphan_list))
list_push_back (&max_donor->donors_list, list_pop_front (&orphan_list));
}
intr_set_level (old_level);
/* Removal of donors to this thread may change its effective priority,
so recalculate. */
thread_recalculate_priority ();
lock->holder = NULL; lock->holder = NULL;
sema_up (&lock->semaphore); sema_up (&lock->semaphore);
thread_yield (); thread_yield ();
@@ -368,49 +254,6 @@ struct semaphore_elem
struct semaphore semaphore; /* This semaphore. */ struct semaphore semaphore; /* This semaphore. */
}; };
/* Function that compares the two threads associated with the provided
pointers to their 'elem' member. Returns true if the thread associated
with a_ has a lower priority than that of b_. */
static bool
priority_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{
struct thread *a = list_entry (a_, struct thread, elem);
struct thread *b = list_entry (b_, struct thread, elem);
return a->priority < b->priority;
}
/* Function that compares the two *semaphores* associated with the provided
list_elem structures. [i.e., takes list_elem of semaphore_elem, and]
Returns true if the thread associated with the semaphore associated with a_
has a higher priority than that of b_.
If aux is provided, then it is a pointer to an integer representing the
priority of the first semaphore. This is useful when the thread has not been
sema'd down yet. */
static bool
sema_priority_more(const struct list_elem *a, const struct list_elem *b,
void *inserting_telem)
{
struct list_elem *te_a, *te_b;
te_b = list_front (
&list_entry (b, struct semaphore_elem, elem)->semaphore.waiters);
if (inserting_telem == NULL)
{
te_a = list_front (
&list_entry (a, struct semaphore_elem, elem)->semaphore.waiters);
}
else
{
te_a = inserting_telem;
}
return priority_more (te_a, te_b, NULL);
}
/* Initializes condition variable COND. A condition variable /* Initializes condition variable COND. A condition variable
allows one piece of code to signal a condition and cooperating allows one piece of code to signal a condition and cooperating
code to receive the signal and act upon it. */ code to receive the signal and act upon it. */
@@ -474,14 +317,9 @@ cond_signal (struct condition *cond, struct lock *lock UNUSED)
ASSERT (!intr_context ()); ASSERT (!intr_context ());
ASSERT (lock_held_by_current_thread (lock)); 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),
/* Enforce wake-up of highest priority thread within the singleton struct semaphore_elem, elem)->semaphore);
semaphores waiting for condvar. */
struct list_elem *e = list_min (&cond->waiters, sema_priority_more, NULL);
list_remove (e);
sema_up (&list_entry (e, struct semaphore_elem, elem)->semaphore);
}
} }
/* Wakes up all threads, if any, waiting on COND (protected by /* Wakes up all threads, if any, waiting on COND (protected by

View File

@@ -55,7 +55,6 @@ static fp32_t load_avg = { 0 }; /* System load average. */
/* Scheduling. */ /* Scheduling. */
#define TIME_SLICE 4 /* # of timer ticks to give each thread. */ #define TIME_SLICE 4 /* # of timer ticks to give each thread. */
#define PRI_UPDATE_FREQ 4 /* # of timer ticks to update priorities. */
static unsigned thread_ticks; /* # of timer ticks since last yield. */ static unsigned thread_ticks; /* # of timer ticks since last yield. */
/* If false (default), use round-robin scheduler. /* If false (default), use round-robin scheduler.
@@ -73,13 +72,12 @@ static void init_thread (struct thread *, const char *name, int nice,
static bool is_thread (struct thread *) UNUSED; static bool is_thread (struct thread *) UNUSED;
static void *alloc_frame (struct thread *, size_t size); static void *alloc_frame (struct thread *, size_t size);
static int calculate_bsd_priority (fp32_t recent_cpu, int nice); static int calculate_bsd_priority (fp32_t recent_cpu, int nice);
static void update_recent_cpu (struct thread *t, void *aux UNUSED); static void thread_update_recent_cpu (struct thread *t, void *aux UNUSED);
static void recalculate_priority (struct thread *t); static bool thread_priority_less (const struct list_elem *a,
const struct list_elem *b, void *aux UNUSED);
static void schedule (void); static void schedule (void);
void thread_schedule_tail (struct thread *prev); 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_,
const struct list_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
@@ -159,31 +157,24 @@ thread_tick (void)
/* Update system load_avg and all threads recent_cpu every second. */ /* Update system load_avg and all threads recent_cpu every second. */
int64_t ticks = timer_ticks (); int64_t ticks = timer_ticks ();
if (thread_mlfqs) if (thread_mlfqs && (ticks % TIMER_FREQ == 0))
{ {
size_t ready = threads_ready ();
if (t != idle_thread) if (t != idle_thread)
t->recent_cpu = fp_add_int (t->recent_cpu, 1); ready++;
fp32_t old_coeff = fp_mul (fp_div_int (int_to_fp (59), 60), load_avg);
fp32_t new_coeff = fp_div_int (int_to_fp (ready), 60);
load_avg = fp_add (old_coeff, new_coeff);
if (ticks % TIMER_FREQ == 0) thread_foreach (thread_update_recent_cpu, NULL);
{ }
size_t ready = threads_ready ();
if (t != idle_thread)
ready++;
fp32_t old_coeff = fp_div_int (fp_mul_int (load_avg, 59), 60);
fp32_t new_coeff = fp_div_int (fp_from_int (ready), 60);
load_avg = fp_add (old_coeff, new_coeff);
thread_foreach (update_recent_cpu, NULL); /* Update current thread's recent_cpu. */
/* Priorities have been updated, need to re-sort. */ if (thread_mlfqs && (t != idle_thread))
list_sort (&ready_list, priority_more, NULL); {
} t->recent_cpu = fp_add_int (t->recent_cpu, 1);
if (ticks % 4 == 0) // recent_cpu was updated, update priority.
/* Recent cpu was updated, update priority. */ t->priority = calculate_bsd_priority (t->recent_cpu, t->nice);
if (t != idle_thread && ticks % PRI_UPDATE_FREQ == 0)
{
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
}
} }
/* Enforce preemption. */ /* Enforce preemption. */
@@ -233,8 +224,8 @@ thread_create (const char *name, int priority,
return TID_ERROR; return TID_ERROR;
/* Initialize thread. */ /* Initialize thread. */
struct thread *parent_thread = thread_current (); struct thread *pt = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu); init_thread (t, name, pt->nice, priority, pt->recent_cpu);
tid = t->tid = allocate_tid (); tid = t->tid = allocate_tid ();
/* Prepare thread for first run by initializing its stack. /* Prepare thread for first run by initializing its stack.
@@ -262,11 +253,6 @@ thread_create (const char *name, int priority,
/* Add to run queue. */ /* Add to run queue. */
thread_unblock (t); thread_unblock (t);
/* Yield if the newly created thread has higher priority than the current
thread. */
if (t->priority > thread_get_priority ())
thread_yield ();
return tid; return tid;
} }
@@ -303,10 +289,10 @@ thread_unblock (struct thread *t)
old_level = intr_disable (); old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED); ASSERT (t->status == THREAD_BLOCKED);
if (thread_mlfqs)
/* Insert the thread back into the ready list in priority order. */ list_insert_ordered (&ready_list, &t->elem, thread_priority_less, NULL);
list_insert_ordered (&ready_list, &t->elem, priority_more, NULL); else
list_push_back (&ready_list, &t->elem);
t->status = THREAD_READY; t->status = THREAD_READY;
intr_set_level (old_level); intr_set_level (old_level);
} }
@@ -376,13 +362,14 @@ thread_yield (void)
ASSERT (!intr_context ()); ASSERT (!intr_context ());
old_level = intr_disable (); old_level = intr_disable ();
if (cur != idle_thread) if (cur != idle_thread)
{ {
/* Insert the thread back into the ready list in priority order. */ if (thread_mlfqs)
list_insert_ordered (&ready_list, &cur->elem, priority_more, NULL); list_insert_ordered (&ready_list, &cur->elem, thread_priority_less,
NULL);
else
list_push_back (&ready_list, &cur->elem);
} }
cur->status = THREAD_READY; cur->status = THREAD_READY;
schedule (); schedule ();
intr_set_level (old_level); intr_set_level (old_level);
@@ -405,68 +392,25 @@ thread_foreach (thread_action_func *func, void *aux)
} }
} }
/* Function that compares the two threads associated with the provided /* Sets the current thread's priority to NEW_PRIORITY. */
pointers to their 'elem' member. Returns true if the thread associated
with a_ has a higher priority than that of b_. */
bool
priority_more (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{
struct thread *a = list_entry (a_, struct thread, elem);
struct thread *b = list_entry (b_, struct thread, elem);
return a->priority > b->priority;
}
/* Function that compares the two threads associated with the provided
pointers to their 'donor_elem' member. Returns true if the thread associated
with a_ has a lower priority than that of b_. */
static bool
donor_priority_less (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED)
{
struct thread *a = list_entry (a_, struct thread, donor_elem);
struct thread *b = list_entry (b_, struct thread, donor_elem);
return a->priority < b->priority;
}
/* Sets the current thread's base priority to new_base_priority.
Updates the current thread's effective priority if necessary. */
void void
thread_set_priority (int new_base_priority) thread_set_priority (int new_priority)
{ {
if (thread_mlfqs) if (thread_mlfqs)
return; return;
thread_current ()->priority = new_priority;
ASSERT (new_base_priority >= PRI_MIN);
ASSERT (new_base_priority <= PRI_MAX);
struct thread *t = thread_current ();
/* If the base priority is unchanged, do nothing. */
if (new_base_priority == t->base_priority)
return;
t->base_priority = new_base_priority;
recalculate_priority (t);
thread_yield ();
} }
/* Returns the current thread's effective priority. */ /* Returns the current thread's priority. */
int int
thread_get_priority (void) thread_get_priority (void)
{ {
return thread_current ()->priority; return thread_current ()->priority;
} }
/* Updates recent_cpu for a thread. */ /* Updates recent_cpu for a thread. */
static void static void
update_recent_cpu (struct thread *t, void *aux UNUSED) thread_update_recent_cpu (struct thread *t, void *aux UNUSED)
{ {
fp32_t curr_recent_cpu = t->recent_cpu; fp32_t curr_recent_cpu = t->recent_cpu;
fp32_t dbl_load_avg = fp_mul_int (load_avg, 2); fp32_t dbl_load_avg = fp_mul_int (load_avg, 2);
@@ -475,38 +419,7 @@ update_recent_cpu (struct thread *t, void *aux UNUSED)
t->recent_cpu t->recent_cpu
= fp_add_int (fp_mul (recent_cpu_coeff, curr_recent_cpu), t->nice); = fp_add_int (fp_mul (recent_cpu_coeff, curr_recent_cpu), t->nice);
// recent_cpu was updated, update priority. // recent_cpu was updated, update priority.
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice); t->priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
}
/* Recalculates the effective priority of the current thread. */
void
thread_recalculate_priority (void)
{
struct thread *t = thread_current ();
recalculate_priority (t);
}
static void
recalculate_priority (struct thread *t)
{
enum intr_level old_level = intr_disable ();
t->priority = t->base_priority;
/* If there are no donors to the current thread, then the effective
priority is just the base priority. */
if (!list_empty (&t->donors_list))
{
int max_donated_priority =
list_entry (list_max (&t->donors_list, donor_priority_less, NULL),
struct thread, donor_elem)->priority;
/* The effective priority is the max donated priority if this is
higher than the base priority. */
if (max_donated_priority > t->priority)
t->priority = max_donated_priority;
}
intr_set_level (old_level);
} }
/* Sets the current thread's nice value to NICE. */ /* Sets the current thread's nice value to NICE. */
@@ -517,11 +430,10 @@ thread_set_nice (int nice)
struct thread *t = thread_current (); struct thread *t = thread_current ();
t->nice = nice; t->nice = nice;
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice); int priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
struct thread *next_t struct thread *next_t
= list_entry (list_begin (&ready_list), struct thread, elem); = list_entry (list_begin (&ready_list), struct thread, elem);
if (t->priority < next_t->priority) if (priority < next_t->priority)
thread_yield (); thread_yield ();
} }
@@ -546,22 +458,6 @@ thread_get_recent_cpu (void)
return fp_round (fp_mul_int (thread_current ()->recent_cpu, 100)); return fp_round (fp_mul_int (thread_current ()->recent_cpu, 100));
} }
/* Reinsert thread t into the ready list at its correct position
in descending order of priority. Used when this thread's priority
may have changed. Must be called with interrupts disabled. */
void
ready_list_reinsert (struct thread *t)
{
ASSERT (intr_get_level () == INTR_OFF);
/* If the thread isn't ready to run, do nothing. */
if (t->status != THREAD_READY)
return;
list_remove (&t->elem);
list_insert_ordered (&ready_list, &t->elem, priority_more, NULL);
}
/* Idle thread. Executes when no other thread is ready to run. /* Idle thread. Executes when no other thread is ready to run.
The idle thread is initially put on the ready list by The idle thread is initially put on the ready list by
@@ -648,16 +544,11 @@ init_thread (struct thread *t, const char *name, int nice, int priority,
t->status = THREAD_BLOCKED; t->status = THREAD_BLOCKED;
strlcpy (t->name, name, sizeof t->name); strlcpy (t->name, name, sizeof t->name);
t->stack = (uint8_t *) t + PGSIZE; t->stack = (uint8_t *) t + PGSIZE;
t->magic = THREAD_MAGIC; t->priority
t->base_priority
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority; = thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
list_init (&t->donors_list);
t->waiting_lock = NULL;
t->nice = nice; t->nice = nice;
t->recent_cpu = recent_cpu; t->recent_cpu = recent_cpu;
t->priority = t->base_priority; t->magic = THREAD_MAGIC;
old_level = intr_disable (); old_level = intr_disable ();
list_push_back (&all_list, &t->allelem); list_push_back (&all_list, &t->allelem);
@@ -741,9 +632,6 @@ thread_schedule_tail (struct thread *prev)
static int static int
calculate_bsd_priority (fp32_t recent_cpu, int nice) calculate_bsd_priority (fp32_t recent_cpu, int nice)
{ {
ASSERT (thread_mlfqs);
int priority = PRI_MAX - (fp_round (recent_cpu) / 4) - (nice * 2); int priority = PRI_MAX - (fp_round (recent_cpu) / 4) - (nice * 2);
if (priority < PRI_MIN) if (priority < PRI_MIN)
return PRI_MIN; return PRI_MIN;
@@ -752,8 +640,18 @@ calculate_bsd_priority (fp32_t recent_cpu, int nice)
return priority; return priority;
} }
/* Schedules a new process. At entry, interrupts must be off and /* Returns true if thread a's priority is strictly greater than
the running process's state must have been changed from thread b's priority. */
static bool
thread_priority_less (const struct list_elem *a, const struct list_elem *b,
void *aux UNUSED)
{
struct thread *ta = list_entry (a, struct thread, elem);
struct thread *tb = list_entry (b, struct thread, elem);
return ta->priority > tb->priority;
}
/* ss's state must have been changed from
running to some other state. This function finds another running to some other state. This function finds another
thread to run and switches to it. thread to run and switches to it.

View File

@@ -95,23 +95,13 @@ struct thread
int priority; /* Priority. */ int priority; /* Priority. */
struct list_elem allelem; /* List element for all threads list. */ struct list_elem allelem; /* List element for all threads list. */
/* Donation Related */ /* Shared between thread.c and synch.c. */
int base_priority; /* Base priority of the thread. */ struct list_elem elem; /* List element. */
struct list donors_list; /* List of threads that have donated
to this thread. */
struct lock *waiting_lock; /* The lock that the current thread is
waiting for. */
struct list_elem donor_elem; /* List element so that thread can be
enlisted in other donors list. */
/* MLFQS items */ /* MLFQS items */
int nice; /* Nice value for this thread */ int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */ fp32_t recent_cpu; /* Amount of time this process received */
/* Shared between thread.c and synch.c. */
struct list_elem elem; /* List element. */
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */
@@ -150,17 +140,12 @@ void thread_yield (void);
typedef void thread_action_func (struct thread *t, void *aux); typedef void thread_action_func (struct thread *t, void *aux);
void thread_foreach (thread_action_func *, void *); void thread_foreach (thread_action_func *, void *);
bool priority_more (const struct list_elem *a_, const struct list_elem *b_,
void *aux UNUSED);
int thread_get_priority (void); int thread_get_priority (void);
void thread_set_priority (int); void thread_set_priority (int);
void thread_recalculate_priority (void);
int thread_get_nice (void); int thread_get_nice (void);
void thread_set_nice (int); void thread_set_nice (int);
int thread_get_recent_cpu (void); int thread_get_recent_cpu (void);
int thread_get_load_avg (void); int thread_get_load_avg (void);
void ready_list_reinsert (struct thread *t);
#endif /* threads/thread.h */ #endif /* threads/thread.h */