Improve mmu.h.
[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
10 uint32_t thread_stack_ofs = offsetof (struct thread, stack);
11
12 static struct list run_queue;
13
14 struct thread *thread_switch (struct thread *cur, struct thread *next);
15
16 void
17 thread_init (void) 
18 {
19   list_init (&run_queue);
20 }
21
22 static void
23 thread_root (void (*function) (void *aux), void *aux) 
24 {
25   ASSERT (function != NULL);
26   
27   function (aux);
28   thread_exit ();
29 }
30
31 struct thread *
32 thread_create (const char *name, void (*function) (void *aux), void *aux) 
33 {
34   struct thread *t;
35
36   ASSERT (name != NULL);
37   ASSERT (function != NULL);
38
39   t = palloc_get (0);
40   if (t == NULL)
41     return NULL;
42
43   memset (t, 0, PGSIZE);
44   strlcpy (t->name, name, sizeof t->name);
45
46   /* Set up stack. */
47   t->stack = (uint32_t *) ((uint8_t *) t + PGSIZE);
48   *--t->stack = (uint32_t) aux;
49   *--t->stack = (uint32_t) function;
50   --t->stack;
51   *--t->stack = (uint32_t) thread_root;
52   t->stack -= 4;
53
54   /* Add to run_queue. */
55   t->status = THREAD_BLOCKED;
56   thread_ready (t);
57
58   return t;
59 }
60
61 static struct thread *
62 stack_to_thread (uint32_t *stack) 
63 {
64   return (struct thread *) ((uint32_t) (stack - 1) & ~((uint32_t) PGSIZE - 1));
65 }
66
67 struct thread *
68 thread_current (void) 
69 {
70   uint32_t *esp;
71   asm ("movl %%esp, %0\n" : "=g" (esp));
72   return stack_to_thread (esp);
73 }
74
75 #ifdef USERPROG
76 void
77 thread_execute (const char *filename) 
78 {
79   struct thread *t = thread_current ();
80   
81   if (!addrspace_load (&t->addrspace, filename)) 
82     panic ("%s: program load failed", filename);
83   panic ("%s: loaded", filename);
84 }
85 #endif
86
87 void
88 thread_ready (struct thread *t) 
89 {
90   if (t->status != THREAD_READY) 
91     {
92       list_push_back (&run_queue, &t->rq_elem);
93       t->status = THREAD_READY;
94     }
95 }
96
97 static struct thread *
98 find_next_to_run (void) 
99 {
100   if (list_empty (&run_queue))
101     return NULL;
102   else
103     return list_entry (list_pop_front (&run_queue), struct thread, rq_elem);
104 }
105
106 static void
107 idle (void) 
108 {
109   static int idle = 0;
110   if (idle++ == 0)
111     printk ("idle\n");
112 }
113
114 void
115 thread_destroy (struct thread *t) 
116 {
117   ASSERT (t->status == THREAD_DYING);
118   ASSERT (t != thread_current ());
119
120   palloc_free (t);
121 }
122
123 void
124 thread_schedule (void) 
125 {
126   struct thread *cur, *next, *prev;
127
128   ASSERT (intr_get_level () == IF_OFF);
129
130   cur = thread_current ();
131   ASSERT (cur->status != THREAD_RUNNING);
132
133   while ((next = find_next_to_run ()) == NULL)
134     idle ();
135
136   next->status = THREAD_RUNNING;
137   prev = thread_switch (cur, next);
138
139   /* Prevent GCC from reordering anything around the thread
140      switch. */
141   asm volatile ("" : : : "memory");
142
143   if (prev != NULL && prev->status == THREAD_DYING) 
144     thread_destroy (prev);
145
146   intr_enable ();
147 }
148
149 void
150 thread_yield (void) 
151 {
152   ASSERT (!intr_context ());
153
154   intr_disable ();
155   thread_ready (thread_current ());
156   thread_schedule ();
157 }
158
159 void
160 thread_start (struct thread *t) 
161 {
162   ASSERT (intr_get_level () == IF_OFF);
163
164   if (t->status == THREAD_READY) 
165     list_remove (&t->rq_elem);
166   t->status = THREAD_RUNNING;
167   thread_switch (NULL, t);
168 }
169
170 void
171 thread_exit (void) 
172 {
173   ASSERT (!intr_context ());
174
175   intr_disable ();
176   thread_current ()->status = THREAD_DYING;
177   thread_schedule ();
178 }
179
180 void
181 thread_sleep (void) 
182 {
183   ASSERT (!intr_context ());
184   ASSERT (intr_get_level () == IF_OFF);
185
186   thread_current ()->status = THREAD_BLOCKED;
187   thread_schedule ();
188 }
189
190 static void
191 tfunc (void *aux UNUSED) 
192 {
193   for (;;) 
194     {
195       size_t count, i;
196       if (random_ulong () % 5 == 0)
197         {
198           printk ("%s exiting\n", thread_current ()->name);
199           break;
200         }
201       count = random_ulong () % 25 * 10000;
202       printk ("%s waiting %zu: ", thread_current ()->name, count);
203       for (i = 0; i < count; i++);
204       printk ("%s\n", thread_current ()->name);
205     }
206 }
207
208 void
209 thread_self_test (void)
210 {
211   struct thread *t;
212   int i;
213     
214   for (i = 0; i < 4; i++) 
215     {
216       char name[2];
217       name[0] = 'a' + i;
218       name[1] = 0;
219       t = thread_create (name, tfunc, NULL); 
220     }
221   thread_start (t); 
222 }