VM: munmap used by VM for itself is no longer used
[minix.git] / lib / libmthread / scheduler.c
blob335f83669eff3daceb2e26b7004275a6eb161554
1 #include <minix/mthread.h>
2 #include "global.h"
3 #include "proto.h"
5 #define MAIN_CTX &(mainthread.m_context)
6 #define MAIN_STATE mainthread.m_state
7 #define OLD_CTX &(threads[old_thread]->m_context)
8 #define CURRENT_CTX &(threads[current_thread]->m_context)
9 #define CURRENT_STATE threads[current_thread]->m_state
10 static int yield_all;
12 /*===========================================================================*
13 * mthread_getcontext *
14 *===========================================================================*/
15 int mthread_getcontext(ctx)
16 ucontext_t *ctx;
18 /* Retrieve this process' current state.*/
20 /* We're not interested in FPU state nor signals, so ignore them.
21 * Coincidentally, this significantly speeds up performance.
23 ctx->uc_flags |= (UCF_IGNFPU | UCF_IGNSIGM);
24 return getcontext(ctx);
28 /*===========================================================================*
29 * mthread_schedule *
30 *===========================================================================*/
31 void mthread_schedule(void)
33 /* Pick a new thread to run and run it. In practice, this involves taking the
34 * first thread off the (FIFO) run queue and resuming that thread.
37 mthread_thread_t old_thread;
38 mthread_tcb_t *new_tcb, *old_tcb;
39 ucontext_t *new_ctx, *old_ctx;
41 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
43 old_thread = current_thread;
45 if (mthread_queue_isempty(&run_queue)) {
46 /* No runnable threads. Let main thread run. */
48 /* We keep track whether we're running the program's 'main' thread or
49 * a spawned thread. In case we're already running the main thread and
50 * there are no runnable threads, we can't jump back to its context.
51 * Instead, we simply return.
53 if (running_main_thread) return;
55 /* We're running the last runnable spawned thread. Return to main
56 * thread as there is no work left.
58 current_thread = MAIN_THREAD;
59 } else {
60 current_thread = mthread_queue_remove(&run_queue);
63 /* Find thread entries in tcb... */
64 new_tcb = mthread_find_tcb(current_thread);
65 old_tcb = mthread_find_tcb(old_thread);
67 /* ...and subsequently their contexts */
68 new_ctx = &(new_tcb->m_context);
69 old_ctx = &(old_tcb->m_context);
71 /* Are we running the 'main' thread after swap? */
72 running_main_thread = (current_thread == MAIN_THREAD);
74 if (swapcontext(old_ctx, new_ctx) == -1)
75 mthread_panic("Could not swap context");
80 /*===========================================================================*
81 * mthread_init_scheduler *
82 *===========================================================================*/
83 void mthread_init_scheduler(void)
85 /* Initialize the scheduler */
86 mthread_queue_init(&run_queue);
87 yield_all = 0;
92 /*===========================================================================*
93 * mthread_suspend *
94 *===========================================================================*/
95 void mthread_suspend(state)
96 mthread_state_t state;
98 /* Stop the current thread from running. There can be multiple reasons for
99 * this; the process tries to lock a locked mutex (i.e., has to wait for it to
100 * become unlocked), the process has to wait for a condition, the thread
101 * volunteered to let another thread to run (i.e., it called yield and remains
102 * runnable itself), or the thread is dead.
105 int continue_thread = 0;
106 mthread_tcb_t *tcb;
107 ucontext_t *ctx;
109 if (state == MS_DEAD) mthread_panic("Shouldn't suspend with MS_DEAD state");
110 tcb = mthread_find_tcb(current_thread);
111 tcb->m_state = state;
112 ctx = &(tcb->m_context);
114 /* Save current thread's context */
115 if (mthread_getcontext(ctx) != 0)
116 mthread_panic("Couldn't save current thread's context");
118 /* We return execution here with setcontext/swapcontext, but also when we
119 * simply return from the getcontext call. If continue_thread is non-zero, we
120 * are continuing the execution of this thread after a call from setcontext
121 * or swapcontext.
124 if(!continue_thread) {
125 continue_thread = 1;
126 mthread_schedule(); /* Let other thread run. */
131 /*===========================================================================*
132 * mthread_unsuspend *
133 *===========================================================================*/
134 void mthread_unsuspend(thread)
135 mthread_thread_t thread; /* Thread to make runnable */
137 /* Mark the state of a thread runnable and add it to the run queue */
138 mthread_tcb_t *tcb;
140 if (!isokthreadid(thread)) mthread_panic("Invalid thread id");
142 tcb = mthread_find_tcb(thread);
143 tcb->m_state = MS_RUNNABLE;
144 mthread_queue_add(&run_queue, thread);
148 /*===========================================================================*
149 * mthread_yield *
150 *===========================================================================*/
151 int mthread_yield(void)
153 /* Defer further execution of the current thread and let another thread run. */
154 mthread_tcb_t *tcb;
155 mthread_thread_t t;
157 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
159 /* Detached threads cannot clean themselves up. This is a perfect moment to
160 * do it */
161 for (t = (mthread_thread_t) 0; need_reset > 0 && t < no_threads; t++) {
162 tcb = mthread_find_tcb(t);
163 if (tcb->m_state == MS_NEEDRESET) {
164 mthread_thread_reset(t);
165 used_threads--;
166 need_reset--;
167 mthread_queue_add(&free_threads, t);
171 if (mthread_queue_isempty(&run_queue)) { /* No point in yielding. */
172 return(-1);
173 } else if (current_thread == NO_THREAD) {
174 /* Can't yield this thread */
175 return(-1);
178 mthread_queue_add(&run_queue, current_thread);
179 mthread_suspend(MS_RUNNABLE); /* We're still runnable, but we're just kind
180 * enough to let someone else run.
182 return(0);
186 /*===========================================================================*
187 * mthread_yield_all *
188 *===========================================================================*/
189 void mthread_yield_all(void)
191 /* Yield until there are no more runnable threads left. Two threads calling
192 * this function will lead to a deadlock.
195 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
197 if (yield_all) mthread_panic("Deadlock: two threads trying to yield_all");
198 yield_all = 1;
200 /* This works as follows. Thread A is running and threads B, C, and D are
201 * runnable. As A is running, it is NOT on the run_queue (see
202 * mthread_schedule). It calls mthread_yield and will be added to the run
203 * queue, allowing B to run. B runs and suspends eventually, possibly still
204 * in a runnable state. Then C and D run. Eventually A will run again (and is
205 * thus not on the list). If B, C, and D are dead, waiting for a condition,
206 * or waiting for a lock, they are not on the run queue either. At that
207 * point A is the only runnable thread left.
209 while (!mthread_queue_isempty(&run_queue)) {
210 (void) mthread_yield();
213 /* Done yielding all threads. */
214 yield_all = 0;