Comments.
[pintos-anon] / src / threads / thread.c
index 8a55699ded46cf7165715966950641aa7a2aca49..b1d25db83da7ecbae5e14d1a8c8451f2932b5f8d 100644 (file)
@@ -2,6 +2,7 @@
 #include <debug.h>
 #include <stddef.h>
 #include <random.h>
 #include <debug.h>
 #include <stddef.h>
 #include <random.h>
+#include <stdio.h>
 #include <string.h>
 #include "threads/flags.h"
 #include "threads/interrupt.h"
 #include <string.h>
 #include "threads/flags.h"
 #include "threads/interrupt.h"
@@ -11,7 +12,7 @@
 #include "threads/switch.h"
 #include "threads/synch.h"
 #ifdef USERPROG
 #include "threads/switch.h"
 #include "threads/synch.h"
 #ifdef USERPROG
-#include "userprog/gdt.h"
+#include "userprog/process.h"
 #endif
 
 /* Random value for struct thread's `magic' member.
 #endif
 
 /* Random value for struct thread's `magic' member.
@@ -40,25 +41,29 @@ struct kernel_thread_frame
     void *aux;                  /* Auxiliary data for function. */
   };
 
     void *aux;                  /* Auxiliary data for function. */
   };
 
+/* Statistics. */
+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. */
+
 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 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 struct thread *new_thread (const char *name, int priority);
 static void init_thread (struct thread *, const char *name, int priority);
 static bool is_thread (struct thread *);
 static void *alloc_frame (struct thread *, size_t size);
 static void init_thread (struct thread *, const char *name, int priority);
 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);
 static tid_t allocate_tid (void);
 
 /* Initializes the threading system by transforming the code
 static void schedule (void);
 void schedule_tail (struct thread *prev);
 static tid_t allocate_tid (void);
 
 /* 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.
+   that's currently running into a thread.  This can't work in
+   general and it is possible in this case only because loader.S
+   was careful to put the bottom of the stack at a page boundary.
+
+   Also initializes the run queue and the tid lock.
 
    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
@@ -69,15 +74,13 @@ thread_init (void)
   ASSERT (intr_get_level () == INTR_OFF);
 
   lock_init (&tid_lock, "tid");
   ASSERT (intr_get_level () == INTR_OFF);
 
   lock_init (&tid_lock, "tid");
+  list_init (&ready_list);
 
   /* Set up a thread structure for the running thread. */
   initial_thread = running_thread ();
   init_thread (initial_thread, "main", PRI_DEFAULT);
   initial_thread->status = THREAD_RUNNING;
   initial_thread->tid = allocate_tid ();
 
   /* Set up a thread structure for the running thread. */
   initial_thread = running_thread ();
   init_thread (initial_thread, "main", PRI_DEFAULT);
   initial_thread->status = THREAD_RUNNING;
   initial_thread->tid = allocate_tid ();
-
-  /* Initialize run queue. */
-  list_init (&ready_list);
 }
 
 /* Starts preemptive thread scheduling by enabling interrupts.
 }
 
 /* Starts preemptive thread scheduling by enabling interrupts.
@@ -89,6 +92,30 @@ thread_start (void)
   intr_enable ();
 }
 
   intr_enable ();
 }
 
+/* Called by the timer interrupt handler at each timer tick to
+   update statistics. */
+void
+thread_tick (void) 
+{
+  struct thread *t = thread_current ();
+  if (t == idle_thread)
+    idle_ticks++;
+#ifdef USERPROG
+  else if (t->pagedir != NULL)
+    user_ticks++;
+#endif
+  else
+    kernel_ticks++;
+}
+
+/* Prints thread statistics. */
+void
+thread_print_stats (void) 
+{
+  printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n",
+          idle_ticks, kernel_ticks, user_ticks);
+}
+
 /* Creates a new kernel thread named NAME with the given initial
    PRIORITY, which executes FUNCTION passing AUX as the argument,
    and adds it to the ready queue.  If thread_start() has been
 /* Creates a new kernel thread named NAME with the given initial
    PRIORITY, which executes FUNCTION passing AUX as the argument,
    and adds it to the ready queue.  If thread_start() has been
@@ -114,10 +141,14 @@ thread_create (const char *name, int priority,
 
   ASSERT (function != NULL);
 
 
   ASSERT (function != NULL);
 
-  t = new_thread (name, priority);
+  /* Allocate thread. */
+  t = palloc_get_page (PAL_ZERO);
   if (t == NULL)
     return TID_ERROR;
   if (t == NULL)
     return TID_ERROR;
-  tid = t->tid;
+
+  /* Initialize thread. */
+  init_thread (t, name, priority);
+  tid = t->tid = allocate_tid ();
 
   /* Stack frame for kernel_thread(). */
   kf = alloc_frame (t, sizeof *kf);
 
   /* Stack frame for kernel_thread(). */
   kf = alloc_frame (t, sizeof *kf);
@@ -139,56 +170,6 @@ thread_create (const char *name, int priority,
   return tid;
 }
 
   return tid;
 }
 
-#ifdef USERPROG
-/* Starts a new thread running a user program loaded from
-   FILENAME, and adds it to the ready queue.  If thread_start()
-   has been called, then new thread may be scheduled before
-   thread_execute() returns.*/
-tid_t
-thread_execute (const char *filename) 
-{
-  struct thread *t;
-  struct intr_frame *if_;
-  struct switch_entry_frame *ef;
-  struct switch_threads_frame *sf;
-  void (*start) (void);
-  tid_t tid;
-
-  ASSERT (filename != NULL);
-
-  t = new_thread (filename, PRI_DEFAULT);
-  if (t == NULL)
-    return TID_ERROR;
-  tid = t->tid;
-  
-  if (!addrspace_load (t, filename, &start)) 
-    PANIC ("%s: program load failed", filename);
-
-  /* Interrupt frame. */
-  if_ = alloc_frame (t, sizeof *if_);
-  if_->es = SEL_UDSEG;
-  if_->ds = SEL_UDSEG;
-  if_->eip = start;
-  if_->cs = SEL_UCSEG;
-  if_->eflags = FLAG_IF | FLAG_MBS;
-  if_->esp = PHYS_BASE;
-  if_->ss = SEL_UDSEG;
-
-  /* Stack frame for switch_entry(). */
-  ef = alloc_frame (t, sizeof *ef);
-  ef->eip = intr_exit;
-
-  /* Stack frame for switch_threads(). */
-  sf = alloc_frame (t, sizeof *sf);
-  sf->eip = switch_entry;
-
-  /* Add to run queue. */
-  thread_unblock (t);
-
-  return tid;
-}
-#endif
-
 /* Transitions a blocked thread T from its current state to the
    ready-to-run state.  This is an error if T is not blocked.
    (Use thread_yield() to make the running thread ready.) */
 /* Transitions a blocked thread T from its current state to the
    ready-to-run state.  This is an error if T is not blocked.
    (Use thread_yield() to make the running thread ready.) */
@@ -246,6 +227,10 @@ thread_exit (void)
 {
   ASSERT (!intr_context ());
 
 {
   ASSERT (!intr_context ());
 
+#ifdef USERPROG
+  process_exit ();
+#endif
+
   /* Just set our status to dying and schedule another process.
      We will be destroyed during the call to schedule_tail(). */
   intr_disable ();
   /* Just set our status to dying and schedule another process.
      We will be destroyed during the call to schedule_tail(). */
   intr_disable ();
@@ -337,23 +322,7 @@ is_thread (struct thread *t)
   return t != NULL && t->magic == THREAD_MAGIC;
 }
 
   return t != NULL && t->magic == THREAD_MAGIC;
 }
 
-/* Creates a new thread named NAME as a child of the running
-   thread.  Returns the new thread if successful or a null
-   pointer on failure. */
-static struct thread *
-new_thread (const char *name, int priority)
-{
-  struct thread *t = palloc_get (PAL_ZERO);
-  if (t != NULL) 
-    {
-      init_thread (t, name, priority);
-      t->tid = allocate_tid ();
-    }
-
-  return t;
-}
-
-/* Does basic initialization of T as a new, blocked thread named
+/* Does basic initialization of T as a blocked thread named
    NAME. */
 static void
 init_thread (struct thread *t, const char *name, int priority)
    NAME. */
 static void
 init_thread (struct thread *t, const char *name, int priority)
@@ -397,22 +366,6 @@ next_thread_to_run (void)
     return list_entry (list_pop_front (&ready_list), struct thread, elem);
 }
 
     return list_entry (list_pop_front (&ready_list), struct thread, elem);
 }
 
-/* Destroys T, which must be in the dying state and must not be
-   the running thread. */
-static void
-destroy_thread (struct thread *t) 
-{
-  ASSERT (is_thread (t));
-  ASSERT (t->status == THREAD_DYING);
-  ASSERT (t != thread_current ());
-
-#ifdef USERPROG
-  addrspace_destroy (t);
-#endif
-  if (t != initial_thread)
-    palloc_free (t);
-}
-
 /* Completes a thread switch by activating the new thread's page
    tables, and, if the previous thread is dying, destroying it.
 
 /* Completes a thread switch by activating the new thread's page
    tables, and, if the previous thread is dying, destroying it.
 
@@ -437,14 +390,18 @@ schedule_tail (struct thread *prev)
 
 #ifdef USERPROG
   /* Activate the new address space. */
 
 #ifdef USERPROG
   /* Activate the new address space. */
-  addrspace_activate (cur);
+  process_activate ();
 #endif
 
 #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 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) 
   if (prev != NULL && prev->status == THREAD_DYING) 
-    destroy_thread (prev);
+    {
+      ASSERT (prev != cur);
+      if (prev != initial_thread)
+        palloc_free_page (prev);
+    }
 }
 
 /* Schedules a new process.  At entry, interrupts must be off and
 }
 
 /* Schedules a new process.  At entry, interrupts must be off and