thread: Do not disable interrupts unnecessarily while initializing stack.
[pintos-anon] / src / threads / thread.c
index ba1cae365f711f44474d6206bffb4e4ca25ccddc..87f22b80607b4faba8ae2b1e9f93dc5d6659d8cc 100644 (file)
    that are ready to run but not actually running. */
 static struct list ready_list;
 
    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;
 
 /* Idle thread. */
 static struct thread *idle_thread;
 
@@ -52,9 +56,7 @@ 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.
 
 /* 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. */
+   Controlled by kernel command-line option "-o mlfqs". */
 bool thread_mlfqs;
 
 static void kernel_thread (thread_func *, void *aux);
 bool thread_mlfqs;
 
 static void kernel_thread (thread_func *, void *aux);
@@ -66,7 +68,7 @@ static void init_thread (struct thread *, const char *name, int priority);
 static bool is_thread (struct thread *) UNUSED;
 static void *alloc_frame (struct thread *, size_t size);
 static void schedule (void);
 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);
+void thread_schedule_tail (struct thread *prev);
 static tid_t allocate_tid (void);
 
 /* Initializes the threading system by transforming the code
 static tid_t allocate_tid (void);
 
 /* Initializes the threading system by transforming the code
@@ -80,10 +82,6 @@ static tid_t allocate_tid (void);
    allocator before trying to create any threads with
    thread_create().
 
    allocator before trying to create any threads with
    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
    It is not safe to call thread_current() until this function
    finishes. */
 void
@@ -93,6 +91,7 @@ thread_init (void)
 
   lock_init (&tid_lock);
   list_init (&ready_list);
 
   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 ();
 
   /* Set up a thread structure for the running thread. */
   initial_thread = running_thread ();
@@ -102,22 +101,14 @@ thread_init (void)
 }
 
 /* Starts preemptive thread scheduling by enabling interrupts.
 }
 
 /* Starts preemptive thread scheduling by enabling interrupts.
-   Also creates the idle thread.
-
-   By the time this function runs, thread_mlfqs has been properly
-   initialized to its final value. */
+   Also creates the idle thread. */
 void
 thread_start (void) 
 {
 void
 thread_start (void) 
 {
-  /* Create the idle thread with maximum priority.  This ensures
-     that it will be scheduled soon after interrupts are enabled.
-     The idle thread will block almost immediately upon
-     scheduling, and subsequently it will never appear on the
-     ready list, so the priority here is otherwise
-     unimportant. */
+  /* Create the idle thread. */
   struct semaphore idle_started;
   sema_init (&idle_started, 0);
   struct semaphore idle_started;
   sema_init (&idle_started, 0);
-  thread_create ("idle", PRI_MAX, idle, &idle_started);
+  thread_create ("idle", PRI_MIN, idle, &idle_started);
 
   /* Start preemptive thread scheduling. */
   intr_enable ();
 
   /* Start preemptive thread scheduling. */
   intr_enable ();
@@ -205,6 +196,7 @@ thread_create (const char *name, int priority,
   /* Stack frame for switch_threads(). */
   sf = alloc_frame (t, sizeof *sf);
   sf->eip = switch_entry;
   /* Stack frame for switch_threads(). */
   sf = alloc_frame (t, sizeof *sf);
   sf->eip = switch_entry;
+  sf->ebp = 0;
 
   /* Add to run queue. */
   thread_unblock (t);
 
   /* Add to run queue. */
   thread_unblock (t);
@@ -230,7 +222,12 @@ thread_block (void)
 
 /* Transitions a blocked thread T to the ready-to-run state.
    This is an error if T is not blocked.  (Use thread_yield() to
 
 /* 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.) */
+   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) 
 {
 void
 thread_unblock (struct thread *t) 
 {
@@ -289,9 +286,11 @@ thread_exit (void)
   process_exit ();
 #endif
 
   process_exit ();
 #endif
 
-  /* Just set our status to dying and schedule another process.
-     We will be destroyed during the call to schedule_tail(). */
+  /* 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 ();
   intr_disable ();
+  list_remove (&thread_current()->allelem);
   thread_current ()->status = THREAD_DYING;
   schedule ();
   NOT_REACHED ();
   thread_current ()->status = THREAD_DYING;
   schedule ();
   NOT_REACHED ();
@@ -315,6 +314,23 @@ thread_yield (void)
   intr_set_level (old_level);
 }
 
   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);
+    }
+}
+
 /* Sets the current thread's priority to NEW_PRIORITY. */
 void
 thread_set_priority (int new_priority) 
 /* Sets the current thread's priority to NEW_PRIORITY. */
 void
 thread_set_priority (int new_priority) 
@@ -394,7 +410,7 @@ idle (void *idle_started_ 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");
     }
 }
 
     }
 }
 
@@ -435,6 +451,8 @@ is_thread (struct thread *t)
 static void
 init_thread (struct thread *t, const char *name, int priority)
 {
 static void
 init_thread (struct thread *t, const char *name, int priority)
 {
+  enum intr_level old_level;
+
   ASSERT (t != NULL);
   ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
   ASSERT (name != NULL);
   ASSERT (t != NULL);
   ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
   ASSERT (name != NULL);
@@ -445,6 +463,10 @@ init_thread (struct thread *t, const char *name, int priority)
   t->stack = (uint8_t *) t + PGSIZE;
   t->priority = priority;
   t->magic = THREAD_MAGIC;
   t->stack = (uint8_t *) t + PGSIZE;
   t->priority = priority;
   t->magic = THREAD_MAGIC;
+
+  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
 }
 
 /* Allocates a SIZE-byte frame at the top of thread T's stack and
@@ -491,7 +513,7 @@ next_thread_to_run (void)
    After this function and its caller returns, the thread switch
    is complete. */
 void
    After this function and its caller returns, the thread switch
    is complete. */
 void
-schedule_tail (struct thread *prev) 
+thread_schedule_tail (struct thread *prev)
 {
   struct thread *cur = running_thread ();
   
 {
   struct thread *cur = running_thread ();
   
@@ -525,8 +547,8 @@ schedule_tail (struct thread *prev)
    running to some other state.  This function finds another
    thread to run and switches to it.
 
    running to some other state.  This function finds another
    thread to run and switches to it.
 
-   It's not safe to call printf() until schedule_tail() has
-   completed. */
+   It's not safe to call printf() until thread_schedule_tail()
+   has completed. */
 static void
 schedule (void) 
 {
 static void
 schedule (void) 
 {
@@ -540,7 +562,7 @@ schedule (void)
 
   if (cur != next)
     prev = switch_threads (cur, next);
 
   if (cur != next)
     prev = switch_threads (cur, next);
-  schedule_tail (prev); 
+  thread_schedule_tail (prev);
 }
 
 /* Returns a tid to use for a new thread. */
 }
 
 /* Returns a tid to use for a new thread. */