X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fthreads%2Fthread.c;h=01e8ea42919913a8c5219221c01f52a38f5f1d3e;hb=8abbb333aea445641d967befd3ca477502ea770b;hp=1e5a7d27179e928cb462a813200ed1a8518ccbf4;hpb=546d8bf36bae548643722e1f5d55fd0fcc1c4262;p=pintos-anon diff --git a/src/threads/thread.c b/src/threads/thread.c index 1e5a7d2..01e8ea4 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -46,13 +46,17 @@ 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. */ +/* Scheduling. */ +#define TIME_SLICE 4 /* # of timer ticks to give each thread. */ +static unsigned thread_ticks; /* # of timer ticks since last yield. */ + static void kernel_thread (thread_func *, void *aux); static void idle (void *aux UNUSED); static struct thread *running_thread (void); static struct thread *next_thread_to_run (void); static void init_thread (struct thread *, const char *name, int priority); -static bool is_thread (struct thread *); +static bool is_thread (struct thread *) UNUSED; static void *alloc_frame (struct thread *, size_t size); static void schedule (void); void schedule_tail (struct thread *prev); @@ -73,7 +77,7 @@ thread_init (void) { ASSERT (intr_get_level () == INTR_OFF); - lock_init (&tid_lock, "tid"); + lock_init (&tid_lock); list_init (&ready_list); /* Set up a thread structure for the running thread. */ @@ -88,16 +92,18 @@ thread_init (void) void thread_start (void) { - thread_create ("idle", PRI_DEFAULT, idle, NULL); + thread_create ("idle", PRI_MAX, idle, NULL); intr_enable (); } -/* Called by the timer interrupt handler at each timer tick to - update statistics. */ +/* 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 @@ -106,6 +112,10 @@ thread_tick (void) #endif else kernel_ticks++; + + /* Enforce preemption. */ + if (++thread_ticks >= TIME_SLICE) + intr_yield_on_return (); } /* Prints thread statistics. */ @@ -273,11 +283,70 @@ thread_yield (void) schedule (); intr_set_level (old_level); } + +/* Sets the current thread's priority to NEW_PRIORITY. */ +void +thread_set_priority (int new_priority) +{ + thread_current ()->priority = new_priority; +} + +/* Returns the current thread's priority. */ +int +thread_get_priority (void) +{ + return thread_current ()->priority; +} + +/* Sets the current thread's nice value to NICE. */ +void +thread_set_nice (int nice UNUSED) +{ + /* Not yet implemented. */ +} + +/* Returns the current thread's nice value. */ +int +thread_get_nice (void) +{ + /* Not yet implemented. */ + return 0; +} + +/* Returns 100 times the system load average. */ +int +thread_get_load_avg (void) +{ + /* Not yet implemented. */ + return 0; +} + +/* Returns 100 times the current thread's recent_cpu value. */ +int +thread_get_recent_cpu (void) +{ + /* Not yet implemented. */ + return 0; +} -/* 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 + thread_start(). It will be scheduled once initially, at which + point it initializes idle_thread 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 *aux UNUSED) { + /* Initialize idle_thread. + + Until we run for the first time, idle_thread remains a null + pointer. That's okay because we know that, at that point, + the ready list has at least one element (the idle thread), + so next_thread_to_run() will not attempt to return the idle + thread. */ idle_thread = thread_current (); for (;;) @@ -285,11 +354,20 @@ idle (void *aux UNUSED) /* Let someone else run. */ intr_disable (); thread_block (); - intr_enable (); - /* Use CPU `hlt' instruction to wait for interrupt. - See [IA32-v2a] "HLT" and [IA32-v3] 7.7. */ - asm ("hlt"); + /* 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 ("sti; hlt"); } } @@ -314,13 +392,13 @@ running_thread (void) 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 %0, %%esp" : "=g" (esp)); + 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) +is_thread (struct thread *t) { return t != NULL && t->magic == THREAD_MAGIC; } @@ -395,6 +473,9 @@ schedule_tail (struct thread *prev) /* Mark us as running. */ cur->status = THREAD_RUNNING; + /* Start new time slice. */ + thread_ticks = 0; + #ifdef USERPROG /* Activate the new address space. */ process_activate ();