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