866 lines
25 KiB
C
866 lines
25 KiB
C
#include "threads/thread.h"
|
|
#include <debug.h>
|
|
#include <hash.h>
|
|
#include <stddef.h>
|
|
#include <random.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "devices/timer.h"
|
|
#include "threads/fixed-point.h"
|
|
#include "threads/flags.h"
|
|
#include "threads/interrupt.h"
|
|
#include "threads/intr-stubs.h"
|
|
#include "threads/malloc.h"
|
|
#include "threads/palloc.h"
|
|
#include "threads/switch.h"
|
|
#include "threads/synch.h"
|
|
#include "threads/vaddr.h"
|
|
#ifdef USERPROG
|
|
#include "userprog/process.h"
|
|
#include "userprog/syscall.h"
|
|
#endif
|
|
|
|
/* Random value for struct thread's `magic' member.
|
|
Used to detect stack overflow. See the big comment at the top
|
|
of thread.h for details. */
|
|
#define THREAD_MAGIC 0xcd6abf4b
|
|
|
|
/* List of processes in THREAD_READY state, that is, processes
|
|
that are ready to run but not actually running. */
|
|
static struct list ready_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;
|
|
|
|
/* Idle thread. */
|
|
static struct thread *idle_thread;
|
|
|
|
/* Initial thread, the thread running init.c:main(). */
|
|
static struct thread *initial_thread;
|
|
|
|
/* Lock used by allocate_tid(). */
|
|
static struct lock tid_lock;
|
|
|
|
/* Stack frame for kernel_thread(). */
|
|
struct kernel_thread_frame
|
|
{
|
|
void *eip; /* Return address. */
|
|
thread_func *function; /* Function to call. */
|
|
void *aux; /* Auxiliary data for function. */
|
|
};
|
|
|
|
/* Statistics. */
|
|
static long long idle_ticks; /* # of timer ticks spent idle. */
|
|
static long long kernel_ticks; /* # of timer ticks in kernel threads. */
|
|
static long long user_ticks; /* # of timer ticks in user programs. */
|
|
static fp32_t load_avg = { 0 }; /* System load average. */
|
|
|
|
/* Scheduling. */
|
|
#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. */
|
|
|
|
/* If false (default), use round-robin scheduler.
|
|
If true, use multi-level feedback queue scheduler.
|
|
Controlled by kernel command-line option "-mlfqs". */
|
|
bool thread_mlfqs;
|
|
|
|
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_process_result (struct thread *t);
|
|
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 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);
|
|
void thread_schedule_tail (struct thread *prev);
|
|
static tid_t allocate_tid (void);
|
|
static bool donor_priority_less (const struct list_elem *a_,
|
|
const struct list_elem *b_, void *aux UNUSED);
|
|
static unsigned process_result_hash (const struct hash_elem *e,
|
|
void *aux UNUSED);
|
|
static bool process_result_less (const struct hash_elem *a,
|
|
const struct hash_elem *b, void *aux UNUSED);
|
|
|
|
/* Initializes the threading system by transforming the code
|
|
that's currently running into a thread. This can't work in
|
|
general and it is possible in this case only because loader.S
|
|
was careful to put the bottom of the stack at a page boundary.
|
|
|
|
Also initializes the run queue and the tid lock.
|
|
|
|
After calling this function, be sure to initialize the page
|
|
allocator before trying to create any threads with
|
|
thread_create().
|
|
|
|
It is not safe to call thread_current() until this function
|
|
finishes. */
|
|
void
|
|
thread_init (void)
|
|
{
|
|
ASSERT (intr_get_level () == INTR_OFF);
|
|
|
|
lock_init (&tid_lock);
|
|
list_init (&ready_list);
|
|
list_init (&all_list);
|
|
|
|
/* Set up a thread structure for the running thread. */
|
|
initial_thread = running_thread ();
|
|
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->tid = allocate_tid ();
|
|
initial_thread->result = NULL; /* Main thread cannot be waited for. */
|
|
}
|
|
|
|
/* Starts preemptive thread scheduling by enabling interrupts.
|
|
Also creates the idle thread. */
|
|
void
|
|
thread_start (void)
|
|
{
|
|
/* Malloc has been initalised, we can allocate the child results table
|
|
for the main thread. */
|
|
struct thread *t = thread_current ();
|
|
if (!hash_init (&t->child_results, process_result_hash, process_result_less,
|
|
t))
|
|
PANIC ("Failed to initialise child results table for main thread.");
|
|
|
|
/* Create the idle thread. */
|
|
struct semaphore idle_started;
|
|
sema_init (&idle_started, 0);
|
|
thread_create ("idle", PRI_MIN, idle, &idle_started);
|
|
|
|
/* Start preemptive thread scheduling. */
|
|
intr_enable ();
|
|
|
|
/* Wait for the idle thread to initialize idle_thread. */
|
|
sema_down (&idle_started);
|
|
}
|
|
|
|
/* Returns the number of threads currently in the ready list.
|
|
Disables interrupts to avoid any race-conditions on the ready list. */
|
|
size_t
|
|
threads_ready (void)
|
|
{
|
|
enum intr_level old_level = intr_disable ();
|
|
size_t ready_thread_count = list_size (&ready_list);
|
|
intr_set_level (old_level);
|
|
return ready_thread_count;
|
|
}
|
|
|
|
/* Called by the timer interrupt handler at each timer tick.
|
|
Thus, this function runs in an external interrupt context. */
|
|
void
|
|
thread_tick (void)
|
|
{
|
|
struct thread *t = thread_current ();
|
|
|
|
/* Update statistics. */
|
|
if (t == idle_thread)
|
|
idle_ticks++;
|
|
#ifdef USERPROG
|
|
else if (t->pagedir != NULL)
|
|
user_ticks++;
|
|
#endif
|
|
else
|
|
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. */
|
|
if (++thread_ticks >= TIME_SLICE)
|
|
intr_yield_on_return ();
|
|
}
|
|
|
|
/* Prints thread statistics. */
|
|
void
|
|
thread_print_stats (void)
|
|
{
|
|
printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n",
|
|
idle_ticks, kernel_ticks, user_ticks);
|
|
}
|
|
|
|
/* Creates a new kernel thread named NAME with the given initial
|
|
PRIORITY, which executes FUNCTION passing AUX as the argument,
|
|
and adds it to the ready queue. Returns the thread identifier
|
|
for the new thread, or TID_ERROR if creation fails.
|
|
|
|
If thread_start() has been called, then the new thread may be
|
|
scheduled before thread_create() returns. It could even exit
|
|
before thread_create() returns. Contrariwise, the original
|
|
thread may run for any amount of time before the new thread is
|
|
scheduled. Use a semaphore or some other form of
|
|
synchronization if you need to ensure ordering.
|
|
|
|
The code provided sets the new thread's `priority' member to
|
|
PRIORITY, but no actual priority scheduling is implemented.
|
|
Priority scheduling is the goal of Problem 1-3. */
|
|
tid_t
|
|
thread_create (const char *name, int priority,
|
|
thread_func *function, void *aux)
|
|
{
|
|
struct thread *t;
|
|
struct kernel_thread_frame *kf;
|
|
struct switch_entry_frame *ef;
|
|
struct switch_threads_frame *sf;
|
|
tid_t tid;
|
|
enum intr_level old_level;
|
|
|
|
ASSERT (function != NULL);
|
|
|
|
/* Allocate thread. */
|
|
t = palloc_get_page (PAL_ZERO);
|
|
if (t == NULL)
|
|
return TID_ERROR;
|
|
|
|
/* Initialize thread. */
|
|
struct thread *parent_thread = thread_current ();
|
|
init_thread (t, name, parent_thread->nice, priority, parent_thread->recent_cpu);
|
|
tid = t->tid = allocate_tid ();
|
|
init_process_result (t);
|
|
|
|
#ifdef USERPROG
|
|
if (!hash_init (&t->open_files, fd_hash, fd_less, NULL)
|
|
|| !hash_init (&t->child_results, process_result_hash,
|
|
process_result_less, t))
|
|
{
|
|
palloc_free_page (t);
|
|
free (t->result);
|
|
return TID_ERROR;
|
|
}
|
|
#endif
|
|
|
|
/* Prepare thread for first run by initializing its stack.
|
|
Do this atomically so intermediate values for the 'stack'
|
|
member cannot be observed. */
|
|
old_level = intr_disable ();
|
|
|
|
/* Stack frame for kernel_thread(). */
|
|
kf = alloc_frame (t, sizeof *kf);
|
|
kf->eip = NULL;
|
|
kf->function = function;
|
|
kf->aux = aux;
|
|
|
|
/* Stack frame for switch_entry(). */
|
|
ef = alloc_frame (t, sizeof *ef);
|
|
ef->eip = (void (*) (void)) kernel_thread;
|
|
|
|
/* Stack frame for switch_threads(). */
|
|
sf = alloc_frame (t, sizeof *sf);
|
|
sf->eip = switch_entry;
|
|
sf->ebp = 0;
|
|
|
|
intr_set_level (old_level);
|
|
|
|
hash_insert (&parent_thread->child_results, &t->result->elem);
|
|
|
|
/* Add to run queue. */
|
|
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;
|
|
}
|
|
|
|
/* Puts the current thread to sleep. It will not be scheduled
|
|
again until awoken by thread_unblock().
|
|
|
|
This function must be called with interrupts turned off. It
|
|
is usually a better idea to use one of the synchronization
|
|
primitives in synch.h. */
|
|
void
|
|
thread_block (void)
|
|
{
|
|
ASSERT (!intr_context ());
|
|
ASSERT (intr_get_level () == INTR_OFF);
|
|
|
|
thread_current ()->status = THREAD_BLOCKED;
|
|
schedule ();
|
|
}
|
|
|
|
/* Transitions a blocked thread T to the ready-to-run state.
|
|
This is an error if T is not blocked. (Use thread_yield() to
|
|
make the running thread ready.)
|
|
|
|
This function does not preempt the running thread. This can
|
|
be important: if the caller had disabled interrupts itself,
|
|
it may expect that it can atomically unblock a thread and
|
|
update other data. */
|
|
void
|
|
thread_unblock (struct thread *t)
|
|
{
|
|
enum intr_level old_level;
|
|
|
|
ASSERT (is_thread (t));
|
|
|
|
old_level = intr_disable ();
|
|
ASSERT (t->status == THREAD_BLOCKED);
|
|
|
|
/* 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;
|
|
intr_set_level (old_level);
|
|
}
|
|
|
|
/* Returns the name of the running thread. */
|
|
const char *
|
|
thread_name (void)
|
|
{
|
|
return thread_current ()->name;
|
|
}
|
|
|
|
/* Returns the running thread.
|
|
This is running_thread() plus a couple of sanity checks.
|
|
See the big comment at the top of thread.h for details. */
|
|
struct thread *
|
|
thread_current (void)
|
|
{
|
|
struct thread *t = running_thread ();
|
|
|
|
/* Make sure T is really a thread.
|
|
If either of these assertions fire, then your thread may
|
|
have overflowed its stack. Each thread has less than 4 kB
|
|
of stack, so a few big automatic arrays or moderate
|
|
recursion can cause stack overflow. */
|
|
ASSERT (is_thread (t));
|
|
ASSERT (t->status == THREAD_RUNNING);
|
|
|
|
return t;
|
|
}
|
|
|
|
/* Returns the running thread's tid. */
|
|
tid_t
|
|
thread_tid (void)
|
|
{
|
|
return thread_current ()->tid;
|
|
}
|
|
|
|
/* Deschedules the current thread and destroys it. Never
|
|
returns to the caller. */
|
|
void
|
|
thread_exit (void)
|
|
{
|
|
ASSERT (!intr_context ());
|
|
|
|
#ifdef USERPROG
|
|
process_exit ();
|
|
#endif
|
|
|
|
/* 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(). */
|
|
intr_disable ();
|
|
struct thread *t = thread_current ();
|
|
list_remove (&t->allelem);
|
|
list_remove (&t->donor_elem);
|
|
thread_current ()->status = THREAD_DYING;
|
|
schedule ();
|
|
NOT_REACHED ();
|
|
}
|
|
|
|
/* Yields the CPU. The current thread is not put to sleep and
|
|
may be scheduled again immediately at the scheduler's whim. */
|
|
void
|
|
thread_yield (void)
|
|
{
|
|
struct thread *cur = thread_current ();
|
|
enum intr_level old_level;
|
|
|
|
ASSERT (!intr_context ());
|
|
|
|
old_level = intr_disable ();
|
|
|
|
if (cur != idle_thread)
|
|
{
|
|
/* 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;
|
|
schedule ();
|
|
intr_set_level (old_level);
|
|
}
|
|
|
|
/* Invoke function 'func' on all threads, passing along 'aux'.
|
|
This function must be called with interrupts off. */
|
|
void
|
|
thread_foreach (thread_action_func *func, void *aux)
|
|
{
|
|
struct list_elem *e;
|
|
|
|
ASSERT (intr_get_level () == INTR_OFF);
|
|
|
|
for (e = list_begin (&all_list); e != list_end (&all_list);
|
|
e = list_next (e))
|
|
{
|
|
struct thread *t = list_entry (e, struct thread, allelem);
|
|
func (t, aux);
|
|
}
|
|
}
|
|
|
|
/* 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 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
|
|
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
|
|
thread_get_priority (void)
|
|
{
|
|
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. */
|
|
void
|
|
thread_set_nice (int nice)
|
|
{
|
|
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. */
|
|
int
|
|
thread_get_nice (void)
|
|
{
|
|
return thread_current ()->nice;
|
|
}
|
|
|
|
/* Returns 100 times the system load average. */
|
|
int
|
|
thread_get_load_avg (void)
|
|
{
|
|
return fp_round (fp_mul_int (load_avg, 100));
|
|
}
|
|
|
|
/* Returns 100 times the current thread's recent_cpu value. */
|
|
int
|
|
thread_get_recent_cpu (void)
|
|
{
|
|
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.
|
|
|
|
The idle thread is initially put on the ready list by
|
|
thread_start(). It will be scheduled once initially, at which
|
|
point it initializes idle_thread, "up"s the semaphore passed
|
|
to it to enable thread_start() to continue, and immediately
|
|
blocks. After that, the idle thread never appears in the
|
|
ready list. It is returned by next_thread_to_run() as a
|
|
special case when the ready list is empty. */
|
|
static void
|
|
idle (void *idle_started_ UNUSED)
|
|
{
|
|
struct semaphore *idle_started = idle_started_;
|
|
idle_thread = thread_current ();
|
|
sema_up (idle_started);
|
|
|
|
for (;;)
|
|
{
|
|
/* Let someone else run. */
|
|
intr_disable ();
|
|
thread_block ();
|
|
|
|
/* Re-enable interrupts and wait for the next one.
|
|
|
|
The `sti' instruction disables interrupts until the
|
|
completion of the next instruction, so these two
|
|
instructions are executed atomically. This atomicity is
|
|
important; otherwise, an interrupt could be handled
|
|
between re-enabling interrupts and waiting for the next
|
|
one to occur, wasting as much as one clock tick worth of
|
|
time.
|
|
|
|
See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a]
|
|
7.11.1 "HLT Instruction". */
|
|
asm volatile ("sti; hlt" : : : "memory");
|
|
}
|
|
}
|
|
|
|
/* Function used as the basis for a kernel thread. */
|
|
static void
|
|
kernel_thread (thread_func *function, void *aux)
|
|
{
|
|
ASSERT (function != NULL);
|
|
|
|
intr_enable (); /* The scheduler runs with interrupts off. */
|
|
function (aux); /* Execute the thread function. */
|
|
thread_exit (); /* If function() returns, kill the thread. */
|
|
}
|
|
|
|
/* Returns the running thread. */
|
|
struct thread *
|
|
running_thread (void)
|
|
{
|
|
uint32_t *esp;
|
|
|
|
/* Copy the CPU's stack pointer into `esp', and then round that
|
|
down to the start of a page. Because `struct thread' is
|
|
always at the beginning of a page and the stack pointer is
|
|
somewhere in the middle, this locates the curent thread. */
|
|
asm ("mov %%esp, %0" : "=g" (esp));
|
|
return pg_round_down (esp);
|
|
}
|
|
|
|
/* Returns true if T appears to point to a valid thread. */
|
|
static bool
|
|
is_thread (struct thread *t)
|
|
{
|
|
return t != NULL && t->magic == THREAD_MAGIC;
|
|
}
|
|
|
|
/* Allocate and initialise a process result for given thread. */
|
|
static void
|
|
init_process_result (struct thread *t)
|
|
{
|
|
struct process_result *result = malloc (sizeof (struct process_result));
|
|
result->tid = t->tid;
|
|
result->exit_status = t->exit_status;
|
|
lock_init (&result->lock);
|
|
sema_init (&result->sema, 0);
|
|
t->result = result;
|
|
}
|
|
|
|
/* Does basic initialization of T as a blocked thread named
|
|
NAME. */
|
|
static void
|
|
init_thread (struct thread *t, const char *name, int nice, int priority,
|
|
fp32_t recent_cpu)
|
|
{
|
|
enum intr_level old_level;
|
|
|
|
ASSERT (t != NULL);
|
|
ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
|
|
ASSERT (name != NULL);
|
|
|
|
memset (t, 0, sizeof *t);
|
|
t->status = THREAD_BLOCKED;
|
|
strlcpy (t->name, name, sizeof t->name);
|
|
t->stack = (uint8_t *) t + PGSIZE;
|
|
t->magic = THREAD_MAGIC;
|
|
|
|
t->base_priority
|
|
= thread_mlfqs ? calculate_bsd_priority (recent_cpu, nice) : priority;
|
|
list_init (&t->donors_list);
|
|
list_push_back (&t->donors_list, &t->donor_elem);
|
|
t->waiting_lock = NULL;
|
|
|
|
t->nice = nice;
|
|
t->recent_cpu = recent_cpu;
|
|
t->priority = t->base_priority;
|
|
|
|
t->fd_counter = MINIMUM_USER_FD;
|
|
t->exit_status = -1;
|
|
|
|
old_level = intr_disable ();
|
|
list_push_back (&all_list, &t->allelem);
|
|
intr_set_level (old_level);
|
|
}
|
|
|
|
/* Allocates a SIZE-byte frame at the top of thread T's stack and
|
|
returns a pointer to the frame's base. */
|
|
static void *
|
|
alloc_frame (struct thread *t, size_t size)
|
|
{
|
|
/* Stack data is always allocated in word-size units. */
|
|
ASSERT (is_thread (t));
|
|
ASSERT (size % sizeof (uint32_t) == 0);
|
|
|
|
t->stack -= size;
|
|
return t->stack;
|
|
}
|
|
|
|
/* Chooses and returns the next thread to be scheduled. Should
|
|
return a thread from the run queue, unless the run queue is
|
|
empty. (If the running thread can continue running, then it
|
|
will be in the run queue.) If the run queue is empty, return
|
|
idle_thread. */
|
|
static struct thread *
|
|
next_thread_to_run (void)
|
|
{
|
|
if (list_empty (&ready_list))
|
|
return idle_thread;
|
|
else
|
|
return list_entry (list_pop_front (&ready_list), struct thread, elem);
|
|
}
|
|
|
|
/* Completes a thread switch by activating the new thread's page
|
|
tables, and, if the previous thread is dying, destroying it.
|
|
|
|
At this function's invocation, we just switched from thread
|
|
PREV, the new thread is already running, and interrupts are
|
|
still disabled. This function is normally invoked by
|
|
thread_schedule() as its final action before returning, but
|
|
the first time a thread is scheduled it is called by
|
|
switch_entry() (see switch.S).
|
|
|
|
It's not safe to call printf() until the thread switch is
|
|
complete. In practice that means that printf()s should be
|
|
added at the end of the function.
|
|
|
|
After this function and its caller returns, the thread switch
|
|
is complete. */
|
|
void
|
|
thread_schedule_tail (struct thread *prev)
|
|
{
|
|
struct thread *cur = running_thread ();
|
|
|
|
ASSERT (intr_get_level () == INTR_OFF);
|
|
|
|
/* Mark us as running. */
|
|
cur->status = THREAD_RUNNING;
|
|
|
|
/* Start new time slice. */
|
|
thread_ticks = 0;
|
|
|
|
#ifdef USERPROG
|
|
/* Activate the new address space. */
|
|
process_activate ();
|
|
#endif
|
|
|
|
/* If the thread we switched from is dying, destroy its struct
|
|
thread. This must happen late so that thread_exit() doesn't
|
|
pull out the rug under itself. (We don't free
|
|
initial_thread because its memory was not obtained via
|
|
palloc().) */
|
|
if (prev != NULL && prev->status == THREAD_DYING && prev != initial_thread)
|
|
{
|
|
ASSERT (prev != cur);
|
|
palloc_free_page (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
|
|
the running process's state must have been changed from
|
|
running to some other state. This function finds another
|
|
thread to run and switches to it.
|
|
|
|
It's not safe to call printf() until thread_schedule_tail()
|
|
has completed. */
|
|
static void
|
|
schedule (void)
|
|
{
|
|
struct thread *cur = running_thread ();
|
|
struct thread *next = next_thread_to_run ();
|
|
struct thread *prev = NULL;
|
|
|
|
ASSERT (intr_get_level () == INTR_OFF);
|
|
ASSERT (cur->status != THREAD_RUNNING);
|
|
ASSERT (is_thread (next));
|
|
|
|
if (cur != next)
|
|
prev = switch_threads (cur, next);
|
|
thread_schedule_tail (prev);
|
|
}
|
|
|
|
/* Returns a tid to use for a new thread. */
|
|
static tid_t
|
|
allocate_tid (void)
|
|
{
|
|
static tid_t next_tid = 1;
|
|
tid_t tid;
|
|
|
|
lock_acquire (&tid_lock);
|
|
tid = next_tid++;
|
|
lock_release (&tid_lock);
|
|
|
|
return tid;
|
|
}
|
|
|
|
/* Hashing function needed for child_results table.
|
|
Returns hash of process_result's TID. */
|
|
static unsigned
|
|
process_result_hash (const struct hash_elem *e, void *aux UNUSED)
|
|
{
|
|
const struct process_result *result
|
|
= hash_entry (e, struct process_result, elem);
|
|
return hash_int (result->tid);
|
|
}
|
|
|
|
/* Comparator function needed for child_results table.
|
|
Returns less than comparison on process_results' TIDs. */
|
|
static bool
|
|
process_result_less (const struct hash_elem *a_, const struct hash_elem *b_,
|
|
void *aux UNUSED)
|
|
{
|
|
const struct process_result *a
|
|
= hash_entry (a_, struct process_result, elem);
|
|
const struct process_result *b
|
|
= hash_entry (b_, struct process_result, elem);
|
|
return a->tid < b->tid;
|
|
}
|
|
|
|
/* Offset of `stack' member within `struct thread'.
|
|
Used by switch.S, which can't figure it out on its own. */
|
|
uint32_t thread_stack_ofs = offsetof (struct thread, stack);
|