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
|= _UC_IGNSIGM
| _UC_IGNFPU
;
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 old_thread
= current_thread
;
43 if (mthread_queue_isempty(&run_queue
)) {
44 /* No runnable threads. Let main thread run. */
46 /* We keep track whether we're running the program's 'main' thread or
47 * a spawned thread. In case we're already running the main thread and
48 * there are no runnable threads, we can't jump back to its context.
49 * Instead, we simply return.
51 if (running_main_thread
) return;
53 /* We're running the last runnable spawned thread. Return to main
54 * thread as there is no work left.
56 current_thread
= MAIN_THREAD
;
58 current_thread
= mthread_queue_remove(&run_queue
);
61 /* Find thread entries in tcb... */
62 new_tcb
= mthread_find_tcb(current_thread
);
63 old_tcb
= mthread_find_tcb(old_thread
);
65 /* ...and subsequently their contexts */
66 new_ctx
= &(new_tcb
->m_context
);
67 old_ctx
= &(old_tcb
->m_context
);
69 /* Are we running the 'main' thread after swap? */
70 running_main_thread
= (current_thread
== MAIN_THREAD
);
72 if (swapcontext(old_ctx
, new_ctx
) == -1)
73 mthread_panic("Could not swap context");
78 /*===========================================================================*
79 * mthread_init_scheduler *
80 *===========================================================================*/
81 void mthread_init_scheduler(void)
83 /* Initialize the scheduler */
84 mthread_queue_init(&run_queue
);
90 /*===========================================================================*
92 *===========================================================================*/
93 void mthread_suspend(state
)
94 mthread_state_t state
;
96 /* Stop the current thread from running. There can be multiple reasons for
97 * this; the process tries to lock a locked mutex (i.e., has to wait for it to
98 * become unlocked), the process has to wait for a condition, the thread
99 * volunteered to let another thread to run (i.e., it called yield and remains
100 * runnable itself), or the thread is dead.
103 int continue_thread
= 0;
107 if (state
== MS_DEAD
) mthread_panic("Shouldn't suspend with MS_DEAD state");
108 tcb
= mthread_find_tcb(current_thread
);
109 tcb
->m_state
= state
;
110 ctx
= &(tcb
->m_context
);
112 /* Save current thread's context */
113 if (mthread_getcontext(ctx
) != 0)
114 mthread_panic("Couldn't save current thread's context");
116 /* We return execution here with setcontext/swapcontext, but also when we
117 * simply return from the getcontext call. If continue_thread is non-zero, we
118 * are continuing the execution of this thread after a call from setcontext
122 if(!continue_thread
) {
124 mthread_schedule(); /* Let other thread run. */
129 /*===========================================================================*
130 * mthread_unsuspend *
131 *===========================================================================*/
132 void mthread_unsuspend(thread
)
133 mthread_thread_t thread
; /* Thread to make runnable */
135 /* Mark the state of a thread runnable and add it to the run queue */
138 if (!isokthreadid(thread
)) mthread_panic("Invalid thread id");
140 tcb
= mthread_find_tcb(thread
);
141 tcb
->m_state
= MS_RUNNABLE
;
142 mthread_queue_add(&run_queue
, thread
);
146 /*===========================================================================*
148 *===========================================================================*/
149 int mthread_yield(void)
151 /* Defer further execution of the current thread and let another thread run. */
155 /* Detached threads cannot clean themselves up. This is a perfect moment to
157 for (t
= (mthread_thread_t
) 0; need_reset
> 0 && t
< no_threads
; t
++) {
158 tcb
= mthread_find_tcb(t
);
159 if (tcb
->m_state
== MS_NEEDRESET
) {
160 mthread_thread_reset(t
);
163 mthread_queue_add(&free_threads
, t
);
167 if (mthread_queue_isempty(&run_queue
)) { /* No point in yielding. */
169 } else if (current_thread
== NO_THREAD
) {
170 /* Can't yield this thread */
174 mthread_queue_add(&run_queue
, current_thread
);
175 mthread_suspend(MS_RUNNABLE
); /* We're still runnable, but we're just kind
176 * enough to let someone else run.
182 /*===========================================================================*
183 * mthread_yield_all *
184 *===========================================================================*/
185 void mthread_yield_all(void)
187 /* Yield until there are no more runnable threads left. Two threads calling
188 * this function will lead to a deadlock.
191 if (yield_all
) mthread_panic("Deadlock: two threads trying to yield_all");
194 /* This works as follows. Thread A is running and threads B, C, and D are
195 * runnable. As A is running, it is NOT on the run_queue (see
196 * mthread_schedule). It calls mthread_yield and will be added to the run
197 * queue, allowing B to run. B runs and suspends eventually, possibly still
198 * in a runnable state. Then C and D run. Eventually A will run again (and is
199 * thus not on the list). If B, C, and D are dead, waiting for a condition,
200 * or waiting for a lock, they are not on the run queue either. At that
201 * point A is the only runnable thread left.
203 while (!mthread_queue_isempty(&run_queue
)) {
204 (void) mthread_yield();
207 /* Done yielding all threads. */
211 /* pthread compatibility layer. */
212 __weak_alias(pthread_yield
, mthread_yield
)
213 __weak_alias(sched_yield
, mthread_yield
)
214 __weak_alias(pthread_yield_all
, mthread_yield_all
)