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