Add memory clobbers to several asm statements,
[pintos-anon] / src / threads / thread.c
index 7a5c726a096c2c36c38652308ae02359678141e3..1dc34adac7770f6bc53b5e5a1fb6b533ec0043f4 100644 (file)
@@ -7,10 +7,10 @@
 #include "threads/flags.h"
 #include "threads/interrupt.h"
 #include "threads/intr-stubs.h"
-#include "threads/mmu.h"
 #include "threads/palloc.h"
 #include "threads/switch.h"
 #include "threads/synch.h"
+#include "threads/vaddr.h"
 #ifdef USERPROG
 #include "userprog/process.h"
 #endif
@@ -50,6 +50,13 @@ static long long user_ticks;    /* # of timer ticks in user programs. */
 #define TIME_SLICE 4            /* # of timer ticks to give each thread. */
 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 options "-o mlfqs".
+   Note that the command line is not parsed until well after
+   thread_init() is called. */
+bool thread_mlfqs;
+
 static void kernel_thread (thread_func *, void *aux);
 
 static void idle (void *aux UNUSED);
@@ -71,7 +78,14 @@ static tid_t allocate_tid (void);
 
    After calling this function, be sure to initialize the page
    allocator before trying to create any threads with
-   thread_create(). */
+   thread_create().
+
+   The kernel command line is not parsed until *after* this
+   function returns, so that when this function runs,
+   thread_mlfqs is always false.
+
+   It is not safe to call thread_current() until this function
+   finishes. */
 void
 thread_init (void) 
 {
@@ -88,15 +102,27 @@ thread_init (void)
 }
 
 /* Starts preemptive thread scheduling by enabling interrupts.
-   Also creates the idle thread. */
+   Also creates the idle thread.
+
+   By the time this function runs, thread_mlfqs has been properly
+   initialized to its final value. */
 void
 thread_start (void) 
 {
-  thread_create ("idle", PRI_MAX, idle, NULL);
+  /* 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);
 }
 
-/* Called by the timer interrupt handler at each timer tick. */
+/* Called by the timer interrupt handler at each timer tick.
+   Thus, this function runs in an external interrupt context. */
 void
 thread_tick (void) 
 {
@@ -277,7 +303,8 @@ thread_yield (void)
   ASSERT (!intr_context ());
 
   old_level = intr_disable ();
-  list_push_back (&ready_list, &cur->elem);
+  if (cur != idle_thread) 
+    list_push_back (&ready_list, &cur->elem);
   cur->status = THREAD_READY;
   schedule ();
   intr_set_level (old_level);
@@ -328,11 +355,21 @@ thread_get_recent_cpu (void)
   return 0;
 }
 \f
-/* 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, "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 *aux UNUSED) 
+idle (void *idle_started_ UNUSED) 
 {
+  struct semaphore *idle_started = idle_started_;
   idle_thread = thread_current ();
+  sema_up (idle_started);
 
   for (;;) 
     {
@@ -350,8 +387,9 @@ idle (void *aux UNUSED)
          one to occur, wasting as much as one clock tick worth of
          time.
 
-         See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3] 7.7. */
-      asm ("sti; hlt");
+         See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a]
+         7.11.1 "HLT Instruction". */
+      asm volatile ("sti; hlt" : : : "memory");
     }
 }
 
@@ -376,7 +414,7 @@ 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);
 }
 
@@ -467,12 +505,13 @@ schedule_tail (struct thread *prev)
 
   /* 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. */
-  if (prev != NULL && prev->status == THREAD_DYING) 
+     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);
-      if (prev != initial_thread)
-        palloc_free_page (prev);
+      palloc_free_page (prev);
     }
 }