X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fthreads%2Fthread.c;h=f24f1fe4b3777d729b8a5fdd3374946076d8cb1f;hb=f6580e9ad405b519dbe85027691bf3c66074b0a4;hp=fed6b841aded48864edee8307fec6fb6090c87ae;hpb=c9c283cb3e26a5b6d918ee47dcf8efe28522b18d;p=pintos-anon diff --git a/src/threads/thread.c b/src/threads/thread.c index fed6b84..f24f1fe 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -1,15 +1,20 @@ #include "thread.h" #include -#include "debug.h" #include "interrupt.h" #include "intr-stubs.h" -#include "lib.h" -#include "gdt.h" #include "mmu.h" #include "palloc.h" -#include "random.h" #include "switch.h" +#include "lib/debug.h" +#include "lib/lib.h" +#include "lib/random.h" +#ifdef USERPROG +#include "userprog/gdt.h" +#endif +/* 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 0x1234abcdu /* List of processes in THREAD_READY state, that is, processes @@ -24,49 +29,58 @@ static void idle (void *aux UNUSED); /* Thread function. */ struct kernel_thread_frame { void *eip; /* Return address. */ - void (*function) (void *); /* Function to call. */ + thread_func *function; /* Function to call. */ void *aux; /* Auxiliary data for function. */ }; -static void kernel_thread (void (*function) (void *aux), void *aux); +static void kernel_thread (thread_func *, void *aux); +static struct thread *running_thread (void); static struct thread *next_thread_to_run (void); static struct thread *new_thread (const char *name); -static bool is_thread (struct thread *t); -static void *alloc_frame (struct thread *t, size_t size); -static void destroy_thread (struct thread *t); +static void init_thread (struct thread *, const char *name); +static bool is_thread (struct thread *); +static void *alloc_frame (struct thread *, size_t size); +static void destroy_thread (struct thread *); static void schedule (void); void schedule_tail (struct thread *prev); -/* Initializes the threading system. After calling, create some - threads with thread_create() or thread_execute(), then start - the scheduler with thread_start(). */ +/* Initializes the threading system by transforming the code + that's currently running into a thread. Note that this is + possible only because the loader was careful to put the bottom + of the stack at a page boundary; it won't work in general. + Also initializes the run queue. + + After calling this function, be sure to initialize the page + allocator before trying to create any threads with + thread_create(). */ void thread_init (void) { + struct thread *t; + ASSERT (intr_get_level () == INTR_OFF); + /* Set up a thread structure for the running thread. */ + t = running_thread (); + init_thread (t, "main"); + t->status = THREAD_RUNNING; + /* Initialize run queue. */ list_init (&run_queue); - - /* Create idle thread. */ - idle_thread = thread_create ("idle", idle, NULL); - idle_thread->status = THREAD_BLOCKED; } -/* Starts the thread scheduler. The caller should have created - some threads with thread_create() or thread_execute(). Never - returns to the caller. */ +/* Starts preemptive thread scheduling by enabling interrupts. + Also creates the idle thread. */ void thread_start (void) { - struct thread *t = next_thread_to_run (); - if (t->status == THREAD_READY) - list_remove (&t->rq_elem); - t->status = THREAD_RUNNING; - switch_threads (NULL, t); + /* Create idle thread. */ + idle_thread = thread_create ("idle", idle, NULL); + idle_thread->status = THREAD_BLOCKED; - NOT_REACHED (); + /* Enable interrupts. */ + intr_enable (); } /* Creates a new kernel thread named NAME, which executes @@ -76,7 +90,7 @@ thread_start (void) semaphore or some other form of synchronization if you need to ensure ordering. */ struct thread * -thread_create (const char *name, void (*function) (void *aux), void *aux) +thread_create (const char *name, thread_func *function, void *aux) { struct thread *t; struct kernel_thread_frame *kf; @@ -102,7 +116,7 @@ thread_create (const char *name, void (*function) (void *aux), void *aux) sf->eip = switch_entry; /* Add to run queue. */ - thread_wake (t); + thread_unblock (t); return t; } @@ -149,25 +163,29 @@ thread_execute (const char *filename) sf->eip = switch_entry; /* Add to run queue. */ - thread_wake (t); + thread_unblock (t); return true; } #endif -/* Transitions T from its current state to THREAD_READY, the - ready-to-run state. On entry, T must be ready or blocked. +/* Transitions a blocked thread T from its current state to the + ready-to-run state. If T is not blocked, there is no effect. (Use thread_yield() to make the running thread ready.) */ void -thread_wake (struct thread *t) +thread_unblock (struct thread *t) { + enum intr_level old_level; + ASSERT (is_thread (t)); - ASSERT (t->status == THREAD_READY || t->status == THREAD_BLOCKED); - if (t->status != THREAD_READY) + + old_level = intr_disable (); + if (t->status == THREAD_BLOCKED) { - list_push_back (&run_queue, &t->rq_elem); + list_push_back (&run_queue, &t->elem); t->status = THREAD_READY; } + intr_set_level (old_level); } /* Returns the name of thread T. */ @@ -178,26 +196,21 @@ thread_name (struct thread *t) return t->name; } -/* Returns the running thread. */ +/* 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) { - uint32_t *esp; - struct thread *t; - - /* 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 ("movl %%esp, %0\n" : "=g" (esp)); - t = pg_round_down (esp); - + struct thread *t = running_thread (); + /* Make sure T is really a thread. - If this assertion fires, 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. */ + 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; } @@ -209,6 +222,8 @@ thread_exit (void) { ASSERT (!intr_context ()); + /* Just set our status to dying and schedule another process. + We will be destroyed during the call to schedule_tail(). */ intr_disable (); thread_current ()->status = THREAD_DYING; schedule (); @@ -226,16 +241,20 @@ thread_yield (void) ASSERT (!intr_context ()); old_level = intr_disable (); - list_push_back (&run_queue, &cur->rq_elem); + list_push_back (&run_queue, &cur->elem); cur->status = THREAD_READY; schedule (); intr_set_level (old_level); } /* Puts the current thread to sleep. It will not be scheduled - again until awoken by thread_wake(). */ + 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_sleep (void) +thread_block (void) { ASSERT (!intr_context ()); ASSERT (intr_get_level () == INTR_OFF); @@ -256,14 +275,14 @@ idle (void *aux UNUSED) /* Let someone else run. */ intr_disable (); - thread_sleep (); + thread_block (); intr_enable (); } } /* Function used as the basis for a kernel thread. */ static void -kernel_thread (void (*function) (void *aux), void *aux) +kernel_thread (thread_func *function, void *aux) { ASSERT (function != NULL); @@ -272,6 +291,20 @@ kernel_thread (void (*function) (void *aux), void *aux) 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 ("movl %%esp, %0\n" : "=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) @@ -291,16 +324,22 @@ new_thread (const char *name) t = palloc_get (PAL_ZERO); if (t != NULL) - { - strlcpy (t->name, name, sizeof t->name); - t->stack = (uint8_t *) t + PGSIZE; - t->status = THREAD_BLOCKED; - t->magic = THREAD_MAGIC; - } - + init_thread (t, name); + return t; } +/* Initializes T as a new, blocked thread named NAME. */ +static void +init_thread (struct thread *t, const char *name) +{ + memset (t, 0, sizeof *t); + strlcpy (t->name, name, sizeof t->name); + t->stack = (uint8_t *) t + PGSIZE; + t->status = THREAD_BLOCKED; + t->magic = THREAD_MAGIC; +} + /* Allocates a SIZE-byte frame at the top of thread T's stack and returns a pointer to the frame's base. */ static void * @@ -325,7 +364,7 @@ next_thread_to_run (void) if (list_empty (&run_queue)) return idle_thread; else - return list_entry (list_pop_front (&run_queue), struct thread, rq_elem); + return list_entry (list_pop_front (&run_queue), struct thread, elem); } /* Destroys T, which must be in the dying state and must not be @@ -337,7 +376,9 @@ destroy_thread (struct thread *t) ASSERT (t->status == THREAD_DYING); ASSERT (t != thread_current ()); +#ifdef USERPROG addrspace_destroy (t); +#endif palloc_free (t); } @@ -356,17 +397,23 @@ destroy_thread (struct thread *t) void schedule_tail (struct thread *prev) { - struct thread *cur = thread_current (); + struct thread *cur = running_thread (); ASSERT (intr_get_level () == INTR_OFF); + /* Mark us as running. */ cur->status = THREAD_RUNNING; - if (prev != NULL && prev->status == THREAD_DYING) - destroy_thread (prev); #ifdef USERPROG + /* Activate the new address space. */ addrspace_activate (cur); #endif + + /* If the thread we switched from is dying, destroy it. + This must happen late because it's not a good idea to + e.g. destroy the page table you're currently using. */ + if (prev != NULL && prev->status == THREAD_DYING) + destroy_thread (prev); } /* Schedules a new process. At entry, interrupts must be off and @@ -376,18 +423,17 @@ schedule_tail (struct thread *prev) static void schedule (void) { - struct thread *cur = thread_current (); + 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) - { - struct thread *prev = switch_threads (cur, next); - schedule_tail (prev); - } + prev = switch_threads (cur, next); + schedule_tail (prev); } /* Offset of `stack' member within `struct thread'.