Add memory clobbers to several asm statements,
[pintos-anon] / src / threads / thread.c
index 01e8ea42919913a8c5219221c01f52a38f5f1d3e..1dc34adac7770f6bc53b5e5a1fb6b533ec0043f4 100644 (file)
@@ -7,10 +7,10 @@
 #include "threads/flags.h"
 #include "threads/interrupt.h"
 #include "threads/intr-stubs.h"
 #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/palloc.h"
 #include "threads/switch.h"
 #include "threads/synch.h"
+#include "threads/vaddr.h"
 #ifdef USERPROG
 #include "userprog/process.h"
 #endif
 #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. */
 
 #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);
 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
 
    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) 
 {
 void
 thread_init (void) 
 {
@@ -88,12 +102,23 @@ thread_init (void)
 }
 
 /* Starts preemptive thread scheduling by enabling interrupts.
 }
 
 /* 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) 
 {
 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 ();
   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.
@@ -278,7 +303,8 @@ thread_yield (void)
   ASSERT (!intr_context ());
 
   old_level = intr_disable ();
   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);
   cur->status = THREAD_READY;
   schedule ();
   intr_set_level (old_level);
@@ -333,21 +359,17 @@ thread_get_recent_cpu (void)
 
    The idle thread is initially put on the ready list by
    thread_start().  It will be scheduled once initially, at which
 
    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. */
+   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
 static void
-idle (void *aux UNUSED) 
+idle (void *idle_started_ 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. */
+  struct semaphore *idle_started = idle_started_;
   idle_thread = thread_current ();
   idle_thread = thread_current ();
+  sema_up (idle_started);
 
   for (;;) 
     {
 
   for (;;) 
     {
@@ -367,7 +389,7 @@ idle (void *aux UNUSED)
 
          See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a]
          7.11.1 "HLT Instruction". */
 
          See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a]
          7.11.1 "HLT Instruction". */
-      asm ("sti; hlt");
+      asm volatile ("sti; hlt" : : : "memory");
     }
 }
 
     }
 }
 
@@ -483,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
 
   /* 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);
     {
       ASSERT (prev != cur);
-      if (prev != initial_thread)
-        palloc_free_page (prev);
+      palloc_free_page (prev);
     }
 }
 
     }
 }