Compare commits

...

108 Commits

Author SHA1 Message Date
Saleh Bubshait
74efa5e652 Merge branch 'task1/themis/thread-create-refactor' into 'master'
Refactor thread_create () to call thread_get_priority in order to retrieve priority

See merge request lab2425_autumn/pintos_22!21
2024-10-25 16:56:31 +00:00
Themis Demetriades
4066354f8a Refactor thread_create () to call thread_get_priority in order to retrieve priority 2024-10-25 17:03:18 +01:00
Demetriades, Themis
4c33653369 Merge branch 'task1/saleh/priority-donation-refactoring' into 'master'
priority donation refactoring

See merge request lab2425_autumn/pintos_22!20
2024-10-25 15:57:22 +00:00
sBubshait
c2414ec54d Add priority_less for comparing threads based on priority and Refactor sema up to use list_max for clarity 2024-10-25 16:04:55 +01:00
sBubshait
30ab3ae861 Update thread_create to only yield CPU to the new thread if necessary 2024-10-25 15:47:19 +01:00
sBubshait
81309dcda9 Refactor sema_up to follow PintOS styling of if statements 2024-10-25 15:44:56 +01:00
Demetriades, Themis
bbb62d2ee7 Merge branch 'gleb/fix-non-busy-sleep-bug' into 'master'
Remove unnecassary inter_disable from non-busy sleep

See merge request lab2425_autumn/pintos_22!19
2024-10-25 14:19:36 +00:00
Demetriades, Themis
f8bdd30f09 Merge branch 'gleb/bsd-priority-donation' into 'master'
Allow priority donation with BSD scheduler.

See merge request lab2425_autumn/pintos_22!18
2024-10-25 14:19:18 +00:00
869571108d Remove from sleeping_threads immediately, instead of when thread is scheduled 2024-10-25 14:10:59 +01:00
6e072a557f Allow priority donation with BSD scheduler.
- Recalculate priority with donations after BSD priority updates
- Remove mlfqs checks from lock_acquire
2024-10-25 14:03:55 +01:00
Saleh Bubshait
af31968e67 Merge branch 'gleb/fix-bsd-ready-list-order' into 'master'
Re-sort ready_list when priorities update + PRI_UPDATE_FREQ

See merge request lab2425_autumn/pintos_22!17
2024-10-25 08:32:08 +00:00
093c6efd30 Re-sort ready_list when priorities update + PRI_UPDATE_FREQ
- PRI_UPDATE_FREQ is a separate value from TIME_SLICE, they just
  happen to be set the same.
2024-10-25 09:13:27 +01:00
sb3923
c4cefbc2d5 Merge branch 'merged-complete' into 'master'
Merge code refactors into main branch

See merge request lab2425_autumn/pintos_22!16
2024-10-24 22:15:24 +00:00
EDiasAlberto
b88baede64 rename recent_cpu update function 2024-10-24 22:45:49 +01:00
EDiasAlberto
7d196ffc57 adjust spacing around brackets to fit styling conventions 2024-10-24 22:30:38 +01:00
EDiasAlberto
39f4edd5e6 remove unnecessary curly brace in thread_tick () 2024-10-24 22:09:28 +01:00
EDiasAlberto
60acc2e58d refactor thread_mlfqs checks in synch.c 2024-10-24 21:46:39 +01:00
EDiasAlberto
a6a0c4ad25 remove empty lines in thread.c 2024-10-24 20:47:30 +01:00
EDiasAlberto
74657bbf9c reorder init_thread for clarity 2024-10-24 20:34:44 +01:00
EDiasAlberto
0b230131f1 refactor thread struct to keep all changes together 2024-10-24 20:17:39 +01:00
EDiasAlberto
49a9284f02 refactor fixed-point.h to match style standards 2024-10-24 20:16:40 +01:00
Dias Alberto, Ethan
e749936b1f Merge branch 'merged-complete' into 'master'
Merge complete code base into master

See merge request lab2425_autumn/pintos_22!15
2024-10-23 19:05:45 +00:00
EDiasAlberto
20be75748c replace thread_mlfqs checks in priority calculations with assertions 2024-10-23 19:57:17 +01:00
EDiasAlberto
d8c843f16a add mlfqs condition checking in synchronisation primitives 2024-10-23 19:37:27 +01:00
EDiasAlberto
1fb101c365 remove repeated comparator and reorder thread_init operations 2024-10-23 18:56:48 +01:00
EDiasAlberto
c357126cc1 fix missing curly brace 2024-10-23 17:51:48 +01:00
Dias Alberto, Ethan
176750282f Merge branch 'BSD-merged' into 'master'
BSD Scheduler implementation

See merge request lab2425_autumn/pintos_22!9
2024-10-23 16:34:39 +00:00
Dias Alberto, Ethan
ed11c61f87 Merge branch 'master' into 'BSD-merged', fixing merge conflicts
# Conflicts:
#   src/threads/thread.c
2024-10-23 16:30:24 +00:00
Dias Alberto, Ethan
0cbae2a2e5 Merge branch 'task1/priority-donation' into 'master'
Merge 'task1/priority-donation' into 'master'

See merge request lab2425_autumn/pintos_22!14
2024-10-23 16:15:44 +00:00
Themis Demetriades
95386971e2 Update lock_release to disable interrupts in critical sections w/ S 2024-10-23 16:45:42 +01:00
sBubshait
4879775d0b Update donate priority to add an assertion that intrrupts are disabled, w/ T 2024-10-23 16:32:40 +01:00
sBubshait
6223846fde Update lock_acquire to disable interrupts to eliminate race-conditions, w/ T 2024-10-23 16:30:38 +01:00
Themis Demetriades
f9d82c92de Update thread_recalculate_priority to disable interrupts preventing race conditions for access to donors and priorities w/ S 2024-10-23 16:16:25 +01:00
Demetriades, Themis
25ca7b6522 Merge branch 'task1/themis/priority-donation' into 'task1/priority-donation'
Merge task1/themis/priority-donation into task1/priority-donation

See merge request lab2425_autumn/pintos_22!13
2024-10-23 15:03:07 +00:00
Themis Demetriades
5f8dea21be Fix donate_priority to disable interrupts for entire update of possibly-ready donatee's priority 2024-10-23 14:10:48 +01:00
Themis Demetriades
b0074c80f0 Refactor ready_list_reinsert to require being called with interrupts disabled 2024-10-23 13:51:07 +01:00
Themis Demetriades
2cd4da17a4 Refactor donate_priority to only allow for the current thread to donate its priority 2024-10-23 13:42:36 +01:00
Themis Demetriades
a875d5fcb4 Update donate_priority to only attempt to sort position of the donee that isn't waiting for a lock 2024-10-23 13:37:43 +01:00
sBubshait
d82176a2e2 Refactor lock release to follow PintOS indent style and use list functoins, w/ T 2024-10-22 22:52:29 +01:00
Themis Demetriades
7aec2e6862 Refactor donate_priority to include comments explaining its purpose and logic w/ S 2024-10-22 21:15:30 +01:00
sBubshait
78c6fd36e3 Refactor sema_up to add comments for clarity, w/ T 2024-10-22 21:04:14 +01:00
Themis Demetriades
48104b3a41 Refactor cond_signal to add comment clarifying the logic w/ S 2024-10-22 20:57:18 +01:00
sBubshait
5549b9c0cb Refactor thread recalculate priority to add comments for clarity, w/ T 2024-10-22 20:52:52 +01:00
sBubshait
bf6104200c Refactor thread set priority to remove unused variable and add comment, w/ T 2024-10-22 20:47:34 +01:00
Themis Demetriades
6983ccdd3b Update thread_get_priority description comment to be more specific w/ S 2024-10-22 20:41:44 +01:00
Themis Demetriades
fc1691f994 Refactor thread.h to remove superfluous thread priority comparison function w/ S 2024-10-22 20:36:37 +01:00
sBubshait
244db41434 Update condvar to use linear search due to changes in priority from donations, w/ T 2024-10-22 20:25:13 +01:00
Themis Demetriades
21cbfc9fe0 Implement transfer of orphaned donors when releasing lock w/ S 2024-10-22 20:06:21 +01:00
sBubshait
d9b9572631 Fix Bug in recalculating priority, uses 'elem' instead of 'donor_elem', w/ T 2024-10-22 19:49:53 +01:00
Themis Demetriades
a19d40bcca Reformat priority_more to obey code style 2024-10-22 19:01:55 +01:00
Themis Demetriades
f10514f4cc Update priority_more comment description to specify which list_elem member it is compatible with 2024-10-22 18:59:19 +01:00
Themis Demetriades
dae5b0d097 Update semaphore priority scheduling to use linear search on unordered list w/ S 2024-10-20 22:50:31 +01:00
Themis Demetriades
840df8af78 Fix bug in donate_priority that wouldn't update the list of donors w/ S 2024-10-20 22:29:20 +01:00
sBubshait
7f7b1648cd Fix indentation in recalculate priority to match pintos style, w/ T 2024-10-20 22:28:29 +01:00
sBubshait
44de31c0ff Fix Bug in Implementation of recalculate priority setting base priority, w/ T 2024-10-20 20:54:48 +01:00
Themis Demetriades
afcb12cef0 Reformat thread priority changing functions to follow style w/ S 2024-10-20 20:38:28 +01:00
sBubshait
b1dba1a0bd Add implementation for recalculate effective priority, w/ T 2024-10-20 20:37:03 +01:00
Themis Demetriades
8e20884a23 Update releasing of locks to update donation information w/ S 2024-10-20 20:17:27 +01:00
Themis Demetriades
ee0cf632b9 Fix attempt to donate priority within lock acquisition w/ S 2024-10-20 19:45:05 +01:00
sBubshait
343ac55d37 Implement priority donation helper function with propagation, w/ T 2024-10-20 19:42:35 +01:00
Themis Demetriades
f98e4bc81c Update lock_acquire to attempt donation of priorities, w/ S 2024-10-20 19:20:26 +01:00
sBubshait
5a651f1279 Update implementation of thread set priority to account for donations, w/ T 2024-10-20 18:06:07 +01:00
Themis Demetriades
fcc8cbb71e Implement initialization of new threads to track donation information, w/ S 2024-10-20 17:11:44 +01:00
sBubshait
8b1e0b9559 Add donation-related information to the thread structure, w/ T 2024-10-20 17:04:14 +01:00
cbb9d52455 Recover removed comment 2024-10-17 20:09:43 +01:00
Themis
6b9b671368 Update sema_up to properly yield during execution of an interrupt handler 2024-10-17 20:01:55 +01:00
sBubshait
bfdedc53e2 Update set priority so not to yield if no ready threads 2024-10-17 19:46:10 +01:00
Themis
711efdc78a Implement priority scheduling for condition variables 2024-10-17 19:43:35 +01:00
sBubshait
c9a9d57019 Merge branch 'task1/priority-scheduling' into task1/merged/priority-scheduling
# Conflicts:
#	.gitignore
#	src/threads/synch.c
#	src/threads/thread.c
2024-10-17 19:30:19 +01:00
044c383a0f fix mlfqs tests, complete BSD scheduling 2024-10-17 18:31:09 +01:00
c5e41db9b0 Implement BSD calculations
- load_avg, recent_cpu, priority calculations
- reduce frac bits to 14
- ignore thread_set_priority when BSD enabled
2024-10-17 18:31:09 +01:00
a7a6d0b9d4 Fix fixed-point returns 2024-10-17 18:31:09 +01:00
EDiasAlberto
d5f913de2b implement recent_cpu 2024-10-17 18:31:09 +01:00
630fbaa3ab implement thread_get_nice & thread_get_recent_cpu 2024-10-17 18:31:09 +01:00
82cc108c38 inline funcs instead of macros for fixed-point 2024-10-17 18:31:09 +01:00
EDiasAlberto
337f8828d9 implement thread_get_nice, thread_set_nice and skeleton for calculate_priority 2024-10-17 18:31:09 +01:00
EDiasAlberto
4e4b5fb83c modify gitignore to ignore vscode files 2024-10-17 18:31:09 +01:00
EDiasAlberto
112432dde0 define basic fixed-point macros 2024-10-17 18:31:09 +01:00
EDiasAlberto
f969b02630 define fixed point arithmetic constants
gitignore clion files
2024-10-17 18:31:09 +01:00
62cca87322 Merge branch 'gitlab-ci' into 'master'
GitLab CI & testing image

See merge request lab2425_autumn/pintos_22!2
2024-10-17 17:30:41 +00:00
sBubshait
88967acdaa Update cond_wait to insert into waiters list in sorted order of priority 2024-10-17 08:53:53 +01:00
sBubshait
e74ee59e17 Update sema priority compare to take aux optionally as priority of first sema 2024-10-17 08:46:14 +01:00
sBubshait
53b296d6c4 Add function to compare the priority of two semaphores 2024-10-17 08:36:29 +01:00
sBubshait
0d6fb2f167 Update sema_up to yield CPU if unblocked has higher priority 2024-10-17 07:15:10 +01:00
sBubshait
24900545d4 Update sema_down to insert into waiters list in priority sorted order 2024-10-17 06:54:10 +01:00
sBubshait
fb268cdef0 Update thread make priority_more public 2024-10-17 06:47:58 +01:00
sBubshait
dbb7fd56e3 Update set priority to reorder the list if priority is changed 2024-10-17 06:38:27 +01:00
sBubshait
949455aae5 Update set priority to eliminate race condition when accessing ready list 2024-10-17 06:35:39 +01:00
sBubshait
5c09ff0149 Updated set priority to yield if priority is lowered 2024-10-17 06:27:59 +01:00
sBubshait
9f53040d27 Update thread_set_priority to not do anything if priority is unchanged 2024-10-17 06:00:34 +01:00
sBubshait
62c8818c05 Update thread_yield to add thread back in sorted order of priority 2024-10-16 08:18:51 +01:00
sBubshait
fda79173c0 Update thread_unblock to maintain priority order in ready_list 2024-10-16 08:14:00 +01:00
sBubshait
1c53790ca7 Update set_priority to ensure that the new priority is within bounds 2024-10-16 08:13:56 +01:00
sBubshait
9bb0b758c8 Fix Error missing semicolon after function signature 2024-10-16 07:40:41 +01:00
sBubshait
8b3f9e353f Update creating thread to yield if the new thread has higher priority 2024-10-16 07:33:24 +01:00
sBubshait
83910f945c Add priority comparator function for thread list sorting 2024-10-16 07:26:02 +01:00
sBubshait
9f71c989a9 Update gitignore to ignore temporary Mac OS files and code editor folders 2024-10-16 07:10:31 +01:00
Themis Demetriades
c32a69a368 Update repository settings to ignore shell scripts 2024-10-15 17:00:02 +01:00
Themis Demetriades
163b7f9016 Update setting of priority of threads to only yield when current thread's priority has been lowered 2024-10-15 15:38:31 +01:00
Themis Demetriades
3c1a26b668 Update setting of thread priorities to yield in case a higher priority thread requires control 2024-10-15 15:14:05 +01:00
Themis Demetriades
54b46806ba Update thread creation to yield thread in case it's no longer the highest priority 2024-10-15 15:12:01 +01:00
Themis Demetriades
6855a48603 Update semaphore code to wake highest priority thread, and run if required 2024-10-15 13:53:48 +01:00
Themis Demetriades
79061fcb9b Update thread module to maintain list of ready threads in descending order of priority 2024-10-15 13:51:41 +01:00
Themis Demetriades
d4d5a7a937 Update thread_priority_greater function signature to explicitly signal unused parameter 2024-10-15 12:01:03 +01:00
Themis Demetriades
f1fa7d2ffb Reformat thread_priority_greater to fit code style 2024-10-15 11:51:40 +01:00
Themis Demetriades
1821d73b09 Add comparison function for thread list elements based on thread priority 2024-10-15 11:50:04 +01:00
Themis Demetriades
4ed64cf173 Add guard in thread_set_priority ensuring priority values are within correct range 2024-10-15 10:47:38 +01:00
d51648d7dc GitLab CI & testing image 2024-10-10 17:34:33 +01:00
8 changed files with 567 additions and 32 deletions

10
.gitignore vendored
View File

@@ -4,6 +4,13 @@
#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
@@ -24,3 +31,6 @@
*.nav *.nav
*.toc *.toc
#ignore files from CLion/VSCode IDEs
.idea
.vscode

44
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,44 @@
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

3
Dockerfile.devel Normal file
View File

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

View File

@@ -119,9 +119,6 @@ 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
@@ -204,7 +201,10 @@ 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)
{
list_remove (&st->elem);
sema_up (&st->semaphore); sema_up (&st->semaphore);
}
else else
break; break;
} }

100
src/threads/fixed-point.h Normal file
View File

@@ -0,0 +1,100 @@
#include <stdint.h>
#ifndef FIXED_POINT_H
#define FIXED_POINT_H
typedef struct
{
int32_t raw;
} fp32_t;
/* Fixed Point Arithmetic bit count constants */
#define NUM_FRAC_BITS 14
#define NUM_INT_BITS (31 - NUM_FRAC_BITS)
#define CONVERSION_FACTOR (1 << NUM_FRAC_BITS) /* f = 2^q, (2^14) */
/* Fixed Point Arithmetic conversion operations */
/* Converts an integer n to a fixed point number */
inline fp32_t
fp_from_int (int32_t n)
{
return (fp32_t){ n * CONVERSION_FACTOR };
}
/* Handles conversion of fixed point to integer,
with truncation */
inline int32_t
fp_floor (fp32_t x)
{
return x.raw / CONVERSION_FACTOR;
}
/* Handles conversion of fixed point to integer,
with rounding */
inline int32_t
fp_round (fp32_t x)
{
if (x.raw >= 0)
return (x.raw + CONVERSION_FACTOR / 2) / CONVERSION_FACTOR;
else
return (x.raw - CONVERSION_FACTOR / 2) / CONVERSION_FACTOR;
}
/* Add two fixed points */
inline fp32_t
fp_add (fp32_t x, fp32_t y)
{
return (fp32_t){ x.raw + y.raw };
}
/* Subtract two fixed points */
inline fp32_t
fp_sub (fp32_t x, fp32_t y)
{
return (fp32_t){ x.raw - y.raw };
}
/* Multiple two fixed points */
inline fp32_t
fp_mul (fp32_t x, fp32_t y)
{
return (fp32_t){ ((int64_t)x.raw) * y.raw / CONVERSION_FACTOR };
}
/* Divide two fixed points */
inline fp32_t
fp_div (fp32_t x, fp32_t y)
{
return (fp32_t){ ((int64_t)x.raw) * CONVERSION_FACTOR / y.raw };
}
/* Multiply fixed point and integer */
inline fp32_t
fp_mul_int (fp32_t x, int32_t n)
{
return (fp32_t){ x.raw * n };
}
/* Divide fixed point by integer */
inline fp32_t
fp_div_int (fp32_t x, int32_t 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

View File

@@ -32,6 +32,10 @@
#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:
@@ -114,10 +118,23 @@ sema_up (struct semaphore *sema)
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), {
struct thread, elem)); /* Enforces wake-up of the highest priority thread waiting for the
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_);
@@ -181,6 +198,47 @@ 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.
@@ -196,8 +254,19 @@ 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
@@ -231,8 +300,54 @@ 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 ();
} }
/* Returns true if the current thread holds LOCK, false /* Returns true if the current thread holds LOCK, false
@@ -253,6 +368,49 @@ 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. */
@@ -317,8 +475,13 @@ cond_signal (struct condition *cond, struct lock *lock UNUSED)
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), {
struct semaphore_elem, elem)->semaphore); /* Enforce wake-up of highest priority thread within the singleton
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

@@ -4,6 +4,8 @@
#include <random.h> #include <random.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "devices/timer.h"
#include "threads/fixed-point.h"
#include "threads/flags.h" #include "threads/flags.h"
#include "threads/interrupt.h" #include "threads/interrupt.h"
#include "threads/intr-stubs.h" #include "threads/intr-stubs.h"
@@ -49,9 +51,11 @@ struct kernel_thread_frame
static long long idle_ticks; /* # of timer ticks spent idle. */ static long long idle_ticks; /* # of timer ticks spent idle. */
static long long kernel_ticks; /* # of timer ticks in kernel threads. */ static long long kernel_ticks; /* # of timer ticks in kernel threads. */
static long long user_ticks; /* # of timer ticks in user programs. */ static long long user_ticks; /* # of timer ticks in user programs. */
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.
@@ -64,12 +68,18 @@ 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_thread (struct thread *, const char *name, int priority); static void init_thread (struct thread *, const char *name, int nice,
int priority, fp32_t recent_cpu);
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 void update_recent_cpu (struct thread *t, void *aux UNUSED);
static void recalculate_priority (struct thread *t);
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
@@ -95,7 +105,9 @@ thread_init (void)
/* Set up a thread structure for the running thread. */ /* Set up a thread structure for the running thread. */
initial_thread = running_thread (); initial_thread = running_thread ();
init_thread (initial_thread, "main", PRI_DEFAULT); fp32_t initial_thread_recent_cpu = { 0 };
init_thread (initial_thread, "main", NICE_DEFAULT, PRI_DEFAULT,
initial_thread_recent_cpu);
initial_thread->status = THREAD_RUNNING; initial_thread->status = THREAD_RUNNING;
initial_thread->tid = allocate_tid (); initial_thread->tid = allocate_tid ();
} }
@@ -145,6 +157,35 @@ thread_tick (void)
else else
kernel_ticks++; kernel_ticks++;
/* Update system load_avg and all threads recent_cpu every second. */
int64_t ticks = timer_ticks ();
if (thread_mlfqs)
{
if (t != idle_thread)
t->recent_cpu = fp_add_int (t->recent_cpu, 1);
if (ticks % TIMER_FREQ == 0)
{
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);
/* Priorities have been updated, need to re-sort. */
list_sort (&ready_list, priority_more, NULL);
}
/* Recent cpu was updated, update priority. */
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. */
if (++thread_ticks >= TIME_SLICE) if (++thread_ticks >= TIME_SLICE)
intr_yield_on_return (); intr_yield_on_return ();
@@ -192,7 +233,8 @@ thread_create (const char *name, int priority,
return TID_ERROR; return TID_ERROR;
/* Initialize thread. */ /* Initialize thread. */
init_thread (t, name, priority); struct thread *parent_thread = thread_current ();
init_thread (t, name, parent_thread->nice, priority, parent_thread->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.
@@ -220,6 +262,11 @@ 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;
} }
@@ -256,7 +303,10 @@ thread_unblock (struct thread *t)
old_level = intr_disable (); old_level = intr_disable ();
ASSERT (t->status == THREAD_BLOCKED); ASSERT (t->status == THREAD_BLOCKED);
list_push_back (&ready_list, &t->elem);
/* Insert the thread back into the ready list in priority order. */
list_insert_ordered (&ready_list, &t->elem, priority_more, NULL);
t->status = THREAD_READY; t->status = THREAD_READY;
intr_set_level (old_level); intr_set_level (old_level);
} }
@@ -326,8 +376,13 @@ 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)
list_push_back (&ready_list, &cur->elem); {
/* Insert the thread back into the ready list in priority order. */
list_insert_ordered (&ready_list, &cur->elem, priority_more, NULL);
}
cur->status = THREAD_READY; cur->status = THREAD_READY;
schedule (); schedule ();
intr_set_level (old_level); intr_set_level (old_level);
@@ -350,49 +405,161 @@ thread_foreach (thread_action_func *func, void *aux)
} }
} }
/* Sets the current thread's priority to NEW_PRIORITY. */ /* Function that compares the two threads associated with the provided
void pointers to their 'elem' member. Returns true if the thread associated
thread_set_priority (int new_priority) 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)
{ {
thread_current ()->priority = new_priority; struct thread *a = list_entry (a_, struct thread, elem);
struct thread *b = list_entry (b_, struct thread, elem);
return a->priority > b->priority;
} }
/* Returns the current thread's 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
thread_set_priority (int new_base_priority)
{
if (thread_mlfqs)
return;
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. */
int int
thread_get_priority (void) thread_get_priority (void)
{ {
return thread_current ()->priority; return thread_current ()->priority;
} }
/* Updates recent_cpu for a thread. */
static void
update_recent_cpu (struct thread *t, void *aux UNUSED)
{
fp32_t curr_recent_cpu = t->recent_cpu;
fp32_t dbl_load_avg = fp_mul_int (load_avg, 2);
fp32_t recent_cpu_coeff
= fp_div (dbl_load_avg, fp_add_int (dbl_load_avg, 1));
t->recent_cpu
= fp_add_int (fp_mul (recent_cpu_coeff, curr_recent_cpu), t->nice);
// recent_cpu was updated, update priority.
t->base_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. */
void void
thread_set_nice (int nice UNUSED) thread_set_nice (int nice)
{ {
/* Not yet implemented. */ ASSERT (NICE_MIN <= nice && nice <= NICE_MAX);
struct thread *t = thread_current ();
t->nice = nice;
t->base_priority = calculate_bsd_priority (t->recent_cpu, t->nice);
recalculate_priority (t);
struct thread *next_t
= list_entry (list_begin (&ready_list), struct thread, elem);
if (t->priority < next_t->priority)
thread_yield ();
} }
/* Returns the current thread's nice value. */ /* Returns the current thread's nice value. */
int int
thread_get_nice (void) thread_get_nice (void)
{ {
/* Not yet implemented. */ return thread_current ()->nice;
return 0;
} }
/* Returns 100 times the system load average. */ /* Returns 100 times the system load average. */
int int
thread_get_load_avg (void) thread_get_load_avg (void)
{ {
/* Not yet implemented. */ return fp_round (fp_mul_int (load_avg, 100));
return 0;
} }
/* Returns 100 times the current thread's recent_cpu value. */ /* Returns 100 times the current thread's recent_cpu value. */
int int
thread_get_recent_cpu (void) thread_get_recent_cpu (void)
{ {
/* Not yet implemented. */ return fp_round (fp_mul_int (thread_current ()->recent_cpu, 100));
return 0; }
/* 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.
@@ -468,7 +635,8 @@ is_thread (struct thread *t)
/* Does basic initialization of T as a blocked thread named /* Does basic initialization of T as a blocked thread named
NAME. */ NAME. */
static void static void
init_thread (struct thread *t, const char *name, int priority) init_thread (struct thread *t, const char *name, int nice, int priority,
fp32_t recent_cpu)
{ {
enum intr_level old_level; enum intr_level old_level;
@@ -480,9 +648,17 @@ init_thread (struct thread *t, const char *name, 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->priority = priority;
t->magic = THREAD_MAGIC; t->magic = THREAD_MAGIC;
t->base_priority
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
list_init (&t->donors_list);
t->waiting_lock = NULL;
t->nice = nice;
t->recent_cpu = recent_cpu;
t->priority = t->base_priority;
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);
@@ -561,6 +737,21 @@ thread_schedule_tail (struct thread *prev)
} }
} }
/* Calculates BSD priority for a thread */
static int
calculate_bsd_priority (fp32_t recent_cpu, int nice)
{
ASSERT (thread_mlfqs);
int priority = PRI_MAX - (fp_round (recent_cpu) / 4) - (nice * 2);
if (priority < PRI_MIN)
return PRI_MIN;
if (priority > PRI_MAX)
return PRI_MAX;
return priority;
}
/* Schedules a new process. At entry, interrupts must be off and /* Schedules a new process. At entry, interrupts must be off and
the running process's state must have been changed from the running process'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

View File

@@ -4,6 +4,7 @@
#include <debug.h> #include <debug.h>
#include <list.h> #include <list.h>
#include <stdint.h> #include <stdint.h>
#include "threads/fixed-point.h"
/* States in a thread's life cycle. */ /* States in a thread's life cycle. */
enum thread_status enum thread_status
@@ -24,6 +25,10 @@ typedef int tid_t;
#define PRI_DEFAULT 31 /* Default priority. */ #define PRI_DEFAULT 31 /* Default priority. */
#define PRI_MAX 63 /* Highest priority. */ #define PRI_MAX 63 /* Highest priority. */
#define NICE_MIN -20 /* Lowest niceness. */
#define NICE_DEFAULT 0 /* Default niceness. */
#define NICE_MAX 20 /* Highest niceness. */
/* A kernel thread or user process. /* A kernel thread or user process.
Each thread structure is stored in its own 4 kB page. The Each thread structure is stored in its own 4 kB page. The
@@ -90,9 +95,23 @@ 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 */
int base_priority; /* Base priority of the thread. */
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 */
int nice; /* Nice value for this thread */
fp32_t recent_cpu; /* Amount of time this process received */
/* 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. */
#ifdef USERPROG #ifdef USERPROG
/* Owned by userprog/process.c. */ /* Owned by userprog/process.c. */
uint32_t *pagedir; /* Page directory. */ uint32_t *pagedir; /* Page directory. */
@@ -131,12 +150,17 @@ 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 */