1 #include <minix/mthread.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
12 /*===========================================================================*
13 * mthread_getcontext *
14 *===========================================================================*/
15 int mthread_getcontext(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 /*===========================================================================*
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
;
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
);
92 /*===========================================================================*
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;
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
124 if(!continue_thread
) {
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 */
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 /*===========================================================================*
150 *===========================================================================*/
151 int mthread_yield(void)
153 /* Defer further execution of the current thread and let another thread run. */
157 MTHREAD_CHECK_INIT(); /* Make sure libmthread is initialized */
159 /* Detached threads cannot clean themselves up. This is a perfect moment to
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
);
167 mthread_queue_add(&free_threads
, t
);
171 if (mthread_queue_isempty(&run_queue
)) { /* No point in yielding. */
173 } else if (current_thread
== NO_THREAD
) {
174 /* Can't yield this thread */
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.
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");
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. */