Clean up threads.c.
[pintos-anon] / src / threads / thread.c
1 #include "thread.h"
2 #include <stddef.h>
3 #include "debug.h"
4 #include "interrupt.h"
5 #include "intr-stubs.h"
6 #include "lib.h"
7 #include "mmu.h"
8 #include "palloc.h"
9 #include "random.h"
10 #include "switch.h"
11
12 uint32_t thread_stack_ofs = offsetof (struct thread, stack);
13
14 static struct list run_queue;
15 static struct thread *idle_thread;
16
17 static void
18 idle (void *aux UNUSED) 
19 {
20   for (;;) 
21     {
22       /* Wait for an interrupt. */
23       asm ("hlt");
24
25       /* Let someone else run. */
26       intr_disable ();
27       thread_sleep ();
28       intr_enable ();
29     }
30 }
31
32 void
33 thread_init (const char *name, void (*function) (void *aux), void *aux) 
34 {
35   struct thread *initial_thread;
36
37   ASSERT (intr_get_level () == IF_OFF);
38
39   list_init (&run_queue);
40   idle_thread = thread_create ("idle", idle, NULL);
41
42   initial_thread = thread_create (name, function, aux);
43   list_remove (&initial_thread->rq_elem);
44   initial_thread->status = THREAD_RUNNING;
45   switch_threads (NULL, initial_thread);
46
47   NOT_REACHED ();
48 }
49
50 struct kernel_thread_frame 
51   {
52     void *eip;                  /* Return address. */
53     void (*function) (void *);  /* Function to call. */
54     void *aux;                  /* Auxiliary data for function. */
55   };
56
57 static void
58 kernel_thread (void (*function) (void *aux), void *aux) 
59 {
60   ASSERT (function != NULL);
61
62   intr_enable ();
63   function (aux);
64   thread_exit ();
65 }
66
67 static struct thread *
68 new_thread (const char *name) 
69 {
70   struct thread *t;
71
72   ASSERT (name != NULL);
73   
74   t = palloc_get (PAL_ZERO);
75   if (t != NULL)
76     {
77       strlcpy (t->name, name, sizeof t->name);
78       t->stack = (uint8_t *) t + PGSIZE;
79       t->status = THREAD_BLOCKED;
80     }
81   
82   return t;
83 }
84
85 static void *
86 alloc_frame (struct thread *t, size_t size) 
87 {
88   ASSERT (size % sizeof (uint32_t) == 0);
89
90   t->stack -= size;
91   return t->stack;
92 }
93
94 struct thread *
95 thread_create (const char *name, void (*function) (void *aux), void *aux) 
96 {
97   struct thread *t;
98   struct kernel_thread_frame *kf;
99   struct switch_entry_frame *ef;
100   struct switch_threads_frame *sf;
101
102   ASSERT (function != NULL);
103
104   t = new_thread (name);
105
106   /* Stack frame for kernel_thread(). */
107   kf = alloc_frame (t, sizeof *kf);
108   kf->eip = NULL;
109   kf->function = function;
110   kf->aux = aux;
111
112   /* Stack frame for switch_entry(). */
113   ef = alloc_frame (t, sizeof *ef);
114   ef->eip = (void (*) (void)) kernel_thread;
115
116   /* Stack frame for thread_switch(). */
117   sf = alloc_frame (t, sizeof *sf);
118   sf->eip = switch_entry;
119
120   /* Add to run queue. */
121   thread_ready (t);
122
123   return t;
124 }
125
126 struct thread *
127 thread_current (void) 
128 {
129   uint32_t *esp;
130   asm ("movl %%esp, %0\n" : "=g" (esp));
131   return pg_round_down (esp);
132 }
133
134 #ifdef USERPROG
135 bool
136 thread_execute (const char *filename) 
137 {
138   struct thread *t;
139   struct intr_frame *if_;
140   struct switch_entry_frame *ef;
141   struct switch_threads_frame *sf;
142   void (*start) (void);
143
144   ASSERT (filename != NULL);
145
146   t = new_thread (filename);
147   if (t == NULL)
148     return false;
149   
150   if (!addrspace_load (&t->addrspace, filename, &start)) 
151     panic ("%s: program load failed", filename);
152
153   /* Interrupt frame. */
154   if_ = alloc_frame (t, sizeof *if_);
155   if_->es = SEL_UDSEG;
156   if_->ds = SEL_UDSEG;
157   if_->eip = start;
158   if_->cs = SEL_UCSEG;
159   if_->eflags = FLAG_IF | 2;
160   if_->esp = PHYS_BASE;
161   if_->ss = SEL_UDSEG;
162
163   /* Stack frame for switch_entry(). */
164   ef = alloc_frame (t, sizeof *ef);
165   ef->eip = intr_exit;
166
167   /* Stack frame for thread_switch(). */
168   sf = alloc_frame (t, sizeof *sf);
169   sf->eip = switch_entry;
170
171   /* Add to run queue. */
172   thread_ready (t);
173
174   return true;
175 }
176 #endif
177
178 void
179 thread_ready (struct thread *t) 
180 {
181   if (t->status != THREAD_READY) 
182     {
183       list_push_back (&run_queue, &t->rq_elem);
184       t->status = THREAD_READY;
185     }
186 }
187
188 static struct thread *
189 find_next_to_run (void) 
190 {
191   if (list_empty (&run_queue))
192     return NULL;
193   else
194     return list_entry (list_pop_front (&run_queue), struct thread, rq_elem);
195 }
196
197 void
198 thread_destroy (struct thread *t) 
199 {
200   ASSERT (t->status == THREAD_DYING);
201   ASSERT (t != thread_current ());
202
203   palloc_free (t);
204 }
205
206 void schedule_tail (struct thread *prev);
207
208 void
209 schedule_tail (struct thread *prev) 
210 {
211   ASSERT (intr_get_level () == IF_OFF);
212
213 #ifdef USERPROG
214   addrspace_activate (&thread_current ()->addrspace);
215 #endif
216
217   if (prev != NULL && prev->status == THREAD_DYING) 
218     thread_destroy (prev);
219 }
220
221 static void
222 thread_schedule (void) 
223 {
224   struct thread *cur, *next, *prev;
225
226   ASSERT (intr_get_level () == IF_OFF);
227
228   cur = thread_current ();
229   ASSERT (cur->status != THREAD_RUNNING);
230
231   next = find_next_to_run ();
232   if (next == NULL)
233     next = idle_thread;
234
235   next->status = THREAD_RUNNING;
236   if (cur != next)
237     {
238       prev = switch_threads (cur, next);
239
240       /* Prevent GCC from reordering anything around the thread
241          switch. */
242       asm volatile ("" : : : "memory");
243
244       schedule_tail (prev); 
245     }
246 }
247
248 void
249 thread_yield (void) 
250 {
251   enum if_level old_level;
252   
253   ASSERT (!intr_context ());
254
255   old_level = intr_disable ();
256   thread_ready (thread_current ());
257   thread_schedule ();
258   intr_set_level (old_level);
259 }
260
261 void
262 thread_exit (void) 
263 {
264   ASSERT (!intr_context ());
265
266   intr_disable ();
267   thread_current ()->status = THREAD_DYING;
268   thread_schedule ();
269   NOT_REACHED ();
270 }
271
272 void
273 thread_sleep (void) 
274 {
275   ASSERT (!intr_context ());
276   ASSERT (intr_get_level () == IF_OFF);
277
278   thread_current ()->status = THREAD_BLOCKED;
279   thread_schedule ();
280 }