5 #include "intr-stubs.h"
12 /* Offset of `stack' member within `struct thread'.
13 Used by switch.S, which can't figure it out on its own. */
14 uint32_t thread_stack_ofs = offsetof (struct thread, stack);
16 /* List of processes in THREAD_READY state, that is, processes
17 that are ready to run but not actually running. */
18 static struct list run_queue;
20 /* Thread to run when nothing else is ready. */
21 static struct thread *idle_thread;
23 static struct thread *find_next_to_run (void);
25 /* Idle thread. Executes when no other thread is ready to run. */
27 idle (void *aux UNUSED)
31 /* Wait for an interrupt. */
35 /* Let someone else run. */
42 /* Initializes the threading system and starts an initial thread
43 which is immediately scheduled. Never returns to the caller.
44 The initial thread is named NAME and executes FUNCTION passing
45 AUX as the argument. */
49 ASSERT (intr_get_level () == IF_OFF);
51 /* Initialize run queue. */
52 list_init (&run_queue);
54 /* Create idle thread. */
55 idle_thread = thread_create ("idle", idle, NULL);
56 idle_thread->status = THREAD_BLOCKED;
62 struct thread *t = find_next_to_run ();
63 if (t->status == THREAD_READY)
64 list_remove (&t->rq_elem);
65 t->status = THREAD_RUNNING;
66 switch_threads (NULL, t);
71 /* Stack frame for kernel_thread(). */
72 struct kernel_thread_frame
74 void *eip; /* Return address. */
75 void (*function) (void *); /* Function to call. */
76 void *aux; /* Auxiliary data for function. */
79 /* Function used as the basis for a kernel thread. */
81 kernel_thread (void (*function) (void *aux), void *aux)
83 ASSERT (function != NULL);
85 intr_enable (); /* The scheduler runs with interrupts off. */
86 function (aux); /* Execute the thread function. */
87 thread_exit (); /* If function() returns, kill the thread. */
90 /* Creates a new thread named NAME and initializes its fields.
91 Returns the new thread if successful or a null pointer on
93 static struct thread *
94 new_thread (const char *name)
98 ASSERT (name != NULL);
100 t = palloc_get (PAL_ZERO);
103 strlcpy (t->name, name, sizeof t->name);
104 t->stack = (uint8_t *) t + PGSIZE;
105 t->status = THREAD_INITIALIZING;
111 /* Allocates a SIZE-byte frame within thread T's stack and
112 returns a pointer to the frame's base. */
114 alloc_frame (struct thread *t, size_t size)
116 /* Stack data is always allocated in word-size units. */
117 ASSERT (size % sizeof (uint32_t) == 0);
123 /* Creates a new kernel thread named NAME, which executes
124 FUNCTION passing AUX as the argument. The thread is added to
125 the ready queue. Thus, it may be scheduled even before
126 thread_create() returns. If you need to ensure ordering, then
127 use synchronization, such as a semaphore. */
129 thread_create (const char *name, void (*function) (void *aux), void *aux)
132 struct kernel_thread_frame *kf;
133 struct switch_entry_frame *ef;
134 struct switch_threads_frame *sf;
136 ASSERT (function != NULL);
138 t = new_thread (name);
140 /* Stack frame for kernel_thread(). */
141 kf = alloc_frame (t, sizeof *kf);
143 kf->function = function;
146 /* Stack frame for switch_entry(). */
147 ef = alloc_frame (t, sizeof *ef);
148 ef->eip = (void (*) (void)) kernel_thread;
150 /* Stack frame for switch_threads(). */
151 sf = alloc_frame (t, sizeof *sf);
152 sf->eip = switch_entry;
154 /* Add to run queue. */
161 thread_current (void)
164 asm ("movl %%esp, %0\n" : "=g" (esp));
165 return pg_round_down (esp);
170 thread_execute (const char *filename)
173 struct intr_frame *if_;
174 struct switch_entry_frame *ef;
175 struct switch_threads_frame *sf;
176 void (*start) (void);
178 ASSERT (filename != NULL);
180 t = new_thread (filename);
184 if (!addrspace_load (&t->addrspace, filename, &start))
185 PANIC ("%s: program load failed", filename);
187 /* Interrupt frame. */
188 if_ = alloc_frame (t, sizeof *if_);
193 if_->eflags = FLAG_IF | 2;
194 if_->esp = PHYS_BASE;
197 /* Stack frame for switch_entry(). */
198 ef = alloc_frame (t, sizeof *ef);
201 /* Stack frame for switch_threads(). */
202 sf = alloc_frame (t, sizeof *sf);
203 sf->eip = switch_entry;
205 /* Add to run queue. */
213 thread_ready (struct thread *t)
215 if (t->status != THREAD_READY)
217 list_push_back (&run_queue, &t->rq_elem);
218 t->status = THREAD_READY;
222 static struct thread *
223 find_next_to_run (void)
225 if (list_empty (&run_queue))
228 return list_entry (list_pop_front (&run_queue), struct thread, rq_elem);
232 thread_destroy (struct thread *t)
234 ASSERT (t->status == THREAD_DYING);
235 ASSERT (t != thread_current ());
240 void schedule_tail (struct thread *prev);
243 schedule_tail (struct thread *prev)
245 ASSERT (intr_get_level () == IF_OFF);
248 addrspace_activate (&thread_current ()->addrspace);
251 if (prev != NULL && prev->status == THREAD_DYING)
252 thread_destroy (prev);
256 thread_schedule (void)
258 struct thread *cur, *next, *prev;
260 ASSERT (intr_get_level () == IF_OFF);
262 cur = thread_current ();
263 ASSERT (cur->status != THREAD_RUNNING);
265 next = find_next_to_run ();
267 next->status = THREAD_RUNNING;
270 prev = switch_threads (cur, next);
272 /* Prevent GCC from reordering anything around the thread
274 asm volatile ("" : : : "memory");
276 schedule_tail (prev);
283 enum if_level old_level;
285 ASSERT (!intr_context ());
287 old_level = intr_disable ();
288 thread_ready (thread_current ());
290 intr_set_level (old_level);
296 ASSERT (!intr_context ());
299 thread_current ()->status = THREAD_DYING;
307 ASSERT (!intr_context ());
308 ASSERT (intr_get_level () == IF_OFF);
310 thread_current ()->status = THREAD_BLOCKED;