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