thr_kern.c revision 103419
1/*
2 * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by John Birrell.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $FreeBSD: head/lib/libkse/thread/thr_kern.c 103419 2002-09-16 19:52:52Z mini $
33 *
34 */
35#include <errno.h>
36#include <poll.h>
37#include <stdlib.h>
38#include <stdarg.h>
39#include <string.h>
40#include <unistd.h>
41#include <sys/param.h>
42#include <sys/types.h>
43#include <sys/signalvar.h>
44#include <sys/stat.h>
45#include <sys/time.h>
46#include <sys/socket.h>
47#include <sys/uio.h>
48#include <sys/syscall.h>
49#include <fcntl.h>
50#include <pthread.h>
51#include "thr_private.h"
52
53/* #define DEBUG_THREAD_KERN */
54#ifdef DEBUG_THREAD_KERN
55#define DBG_MSG		stdout_debug
56#else
57#define DBG_MSG(x...)
58#endif
59
60/* Static function prototype definitions: */
61static void
62thread_kern_idle(void);
63
64static void
65dequeue_signals(void);
66
67static inline void
68thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in);
69
70/* Static variables: */
71static int	last_tick = 0;
72
73void
74_thread_kern_sched(void)
75{
76	struct pthread	*curthread = _get_curthread();
77
78	/*
79	 * Flag the pthread kernel as executing scheduler code
80	 * to avoid a scheduler signal from interrupting this
81	 * execution and calling the scheduler again.
82	 */
83	_thread_kern_in_sched = 1;
84
85	/* Switch into the scheduler's context. */
86	swapcontext(&curthread->ctx, &_thread_kern_sched_ctx);
87	DBG_MSG("Returned from swapcontext, thread %p\n", curthread);
88
89	/*
90	 * This point is reached when swapcontext() is called
91	 * to restore the state of a thread.
92	 *
93	 * This is the normal way out of the scheduler.
94	 */
95	_thread_kern_in_sched = 0;
96
97	if (curthread->sig_defer_count == 0) {
98		if (((curthread->cancelflags &
99		    PTHREAD_AT_CANCEL_POINT) == 0) &&
100		    ((curthread->cancelflags &
101		    PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
102			/*
103			 * Stick a cancellation point at the
104			 * start of each async-cancellable
105			 * thread's resumption.
106			 *
107			 * We allow threads woken at cancel
108			 * points to do their own checks.
109			 */
110			pthread_testcancel();
111	}
112
113	if (_sched_switch_hook != NULL) {
114		/* Run the installed switch hook: */
115		thread_run_switch_hook(_last_user_thread, curthread);
116	}
117}
118
119void
120_thread_kern_scheduler(void)
121{
122	struct timespec	ts;
123	struct timeval	tv;
124	struct pthread	*curthread = _get_curthread();
125	pthread_t	pthread, pthread_h;
126	unsigned int	current_tick;
127	int		add_to_prioq;
128
129	/*
130	 * Enter a scheduling loop that finds the next thread that is
131	 * ready to run. This loop completes when there are no more threads
132	 * in the global list. It is interrupted each time a thread is
133	 * scheduled, but will continue when we return.
134	 */
135	while (!(TAILQ_EMPTY(&_thread_list))) {
136
137		/* If the currently running thread is a user thread, save it: */
138		if ((curthread->flags & PTHREAD_FLAGS_PRIVATE) == 0)
139			_last_user_thread = curthread;
140
141		/* Get the current time of day: */
142		GET_CURRENT_TOD(tv);
143		TIMEVAL_TO_TIMESPEC(&tv, &ts);
144		current_tick = _sched_ticks;
145
146		add_to_prioq = 0;
147		if (curthread != &_thread_kern_thread) {
148			/*
149			 * This thread no longer needs to yield the CPU.
150			 */
151			if (curthread->state != PS_RUNNING) {
152				/*
153				 * Save the current time as the time that the
154				 * thread became inactive:
155				 */
156				curthread->last_inactive = (long)current_tick;
157				if (curthread->last_inactive <
158				    curthread->last_active) {
159					/* Account for a rollover: */
160					curthread->last_inactive =+
161					    UINT_MAX + 1;
162				}
163			}
164
165			/*
166			 * Place the currently running thread into the
167			 * appropriate queue(s).
168			 */
169			switch (curthread->state) {
170			case PS_DEAD:
171			case PS_STATE_MAX: /* to silence -Wall */
172			case PS_SUSPENDED:
173				/*
174				 * Dead and suspended threads are not placed
175				 * in any queue:
176				 */
177				break;
178
179			case PS_RUNNING:
180				/*
181				 * Runnable threads can't be placed in the
182				 * priority queue until after waiting threads
183				 * are polled (to preserve round-robin
184				 * scheduling).
185				 */
186				add_to_prioq = 1;
187				break;
188
189			/*
190			 * States which do not depend on file descriptor I/O
191			 * operations or timeouts:
192			 */
193			case PS_DEADLOCK:
194			case PS_JOIN:
195			case PS_MUTEX_WAIT:
196			case PS_WAIT_WAIT:
197				/* No timeouts for these states: */
198				curthread->wakeup_time.tv_sec = -1;
199				curthread->wakeup_time.tv_nsec = -1;
200
201				/* Restart the time slice: */
202				curthread->slice_usec = -1;
203
204				/* Insert into the waiting queue: */
205				PTHREAD_WAITQ_INSERT(curthread);
206				break;
207
208			/* States which can timeout: */
209			case PS_COND_WAIT:
210			case PS_SLEEP_WAIT:
211				/* Restart the time slice: */
212				curthread->slice_usec = -1;
213
214				/* Insert into the waiting queue: */
215				PTHREAD_WAITQ_INSERT(curthread);
216				break;
217
218			/* States that require periodic work: */
219			case PS_SPINBLOCK:
220				/* No timeouts for this state: */
221				curthread->wakeup_time.tv_sec = -1;
222				curthread->wakeup_time.tv_nsec = -1;
223
224				/* Increment spinblock count: */
225				_spinblock_count++;
226
227				/* FALLTHROUGH */
228			}
229		}
230
231		last_tick = current_tick;
232
233		/*
234		 * Wake up threads that have timedout.  This has to be
235		 * done after polling in case a thread does a poll or
236		 * select with zero time.
237		 */
238		PTHREAD_WAITQ_SETACTIVE();
239		while (((pthread = TAILQ_FIRST(&_waitingq)) != NULL) &&
240		    (pthread->wakeup_time.tv_sec != -1) &&
241		    (((pthread->wakeup_time.tv_sec == 0) &&
242		    (pthread->wakeup_time.tv_nsec == 0)) ||
243		    (pthread->wakeup_time.tv_sec < ts.tv_sec) ||
244		    ((pthread->wakeup_time.tv_sec == ts.tv_sec) &&
245		    (pthread->wakeup_time.tv_nsec <= ts.tv_nsec)))) {
246			/*
247			 * Remove this thread from the waiting queue
248			 * (and work queue if necessary) and place it
249			 * in the ready queue.
250			 */
251			PTHREAD_WAITQ_CLEARACTIVE();
252			if (pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
253				PTHREAD_WORKQ_REMOVE(pthread);
254			PTHREAD_NEW_STATE(pthread, PS_RUNNING);
255			PTHREAD_WAITQ_SETACTIVE();
256			/*
257			 * Flag the timeout in the thread structure:
258			 */
259			pthread->timeout = 1;
260		}
261		PTHREAD_WAITQ_CLEARACTIVE();
262
263		/*
264		 * Check to see if the current thread needs to be added
265		 * to the priority queue:
266		 */
267		if (add_to_prioq != 0) {
268			/*
269			 * Save the current time as the time that the
270			 * thread became inactive:
271			 */
272			current_tick = _sched_ticks;
273			curthread->last_inactive = (long)current_tick;
274			if (curthread->last_inactive <
275			    curthread->last_active) {
276				/* Account for a rollover: */
277				curthread->last_inactive =+ UINT_MAX + 1;
278			}
279
280			if ((curthread->slice_usec != -1) &&
281		 	   (curthread->attr.sched_policy != SCHED_FIFO)) {
282				/*
283				 * Accumulate the number of microseconds for
284				 * which the current thread has run:
285				 */
286				curthread->slice_usec +=
287				    (curthread->last_inactive -
288				    curthread->last_active) *
289				    (long)_clock_res_usec;
290				/* Check for time quantum exceeded: */
291				if (curthread->slice_usec > TIMESLICE_USEC)
292					curthread->slice_usec = -1;
293			}
294
295			if (curthread->slice_usec == -1) {
296				/*
297				 * The thread exceeded its time
298				 * quantum or it yielded the CPU;
299				 * place it at the tail of the
300				 * queue for its priority.
301				 */
302				PTHREAD_PRIOQ_INSERT_TAIL(curthread);
303			} else {
304				/*
305				 * The thread hasn't exceeded its
306				 * interval.  Place it at the head
307				 * of the queue for its priority.
308				 */
309				PTHREAD_PRIOQ_INSERT_HEAD(curthread);
310			}
311		}
312
313		/*
314		 * Get the highest priority thread in the ready queue.
315		 */
316		pthread_h = PTHREAD_PRIOQ_FIRST();
317
318		/* Check if there are no threads ready to run: */
319		if (pthread_h == NULL) {
320			/*
321			 * Lock the pthread kernel by changing the pointer to
322			 * the running thread to point to the global kernel
323			 * thread structure:
324			 */
325			_set_curthread(&_thread_kern_thread);
326			curthread = &_thread_kern_thread;
327
328			DBG_MSG("No runnable threads, using kernel thread %p\n",
329			    curthread);
330
331			/*
332			 * There are no threads ready to run, so wait until
333			 * something happens that changes this condition:
334			 */
335			thread_kern_idle();
336
337			/*
338			 * This process' usage will likely be very small
339			 * while waiting in a poll.  Since the scheduling
340			 * clock is based on the profiling timer, it is
341			 * unlikely that the profiling timer will fire
342			 * and update the time of day.  To account for this,
343			 * get the time of day after polling with a timeout.
344			 */
345			gettimeofday((struct timeval *) &_sched_tod, NULL);
346
347			/* Check once more for a runnable thread: */
348			pthread_h = PTHREAD_PRIOQ_FIRST();
349		}
350
351		if (pthread_h != NULL) {
352			/* Remove the thread from the ready queue: */
353			PTHREAD_PRIOQ_REMOVE(pthread_h);
354
355			/* Make the selected thread the current thread: */
356			_set_curthread(pthread_h);
357			curthread = pthread_h;
358
359			/*
360			 * Save the current time as the time that the thread
361			 * became active:
362			 */
363			current_tick = _sched_ticks;
364			curthread->last_active = (long) current_tick;
365
366			/*
367			 * Check if this thread is running for the first time
368			 * or running again after using its full time slice
369			 * allocation:
370			 */
371			if (curthread->slice_usec == -1) {
372				/* Reset the accumulated time slice period: */
373				curthread->slice_usec = 0;
374			}
375
376			/*
377			 * If we had a context switch, run any
378			 * installed switch hooks.
379			 */
380			if ((_sched_switch_hook != NULL) &&
381			    (_last_user_thread != curthread)) {
382				thread_run_switch_hook(_last_user_thread,
383				    curthread);
384			}
385			/*
386			 * Continue the thread at its current frame:
387			 */
388			swapcontext(&_thread_kern_sched_ctx, &curthread->ctx);
389		}
390	}
391
392	/* There are no more threads, so exit this process: */
393	exit(0);
394}
395
396void
397_thread_kern_sched_state(enum pthread_state state, char *fname, int lineno)
398{
399	struct pthread	*curthread = _get_curthread();
400
401	/*
402	 * Flag the pthread kernel as executing scheduler code
403	 * to avoid a scheduler signal from interrupting this
404	 * execution and calling the scheduler again.
405	 */
406	_thread_kern_in_sched = 1;
407
408	/* Change the state of the current thread: */
409	curthread->state = state;
410	curthread->fname = fname;
411	curthread->lineno = lineno;
412
413	/* Schedule the next thread that is ready: */
414	_thread_kern_sched();
415}
416
417void
418_thread_kern_sched_state_unlock(enum pthread_state state,
419    spinlock_t *lock, char *fname, int lineno)
420{
421	struct pthread	*curthread = _get_curthread();
422
423	/*
424	 * Flag the pthread kernel as executing scheduler code
425	 * to avoid a scheduler signal from interrupting this
426	 * execution and calling the scheduler again.
427	 */
428	_thread_kern_in_sched = 1;
429
430	/* Change the state of the current thread: */
431	curthread->state = state;
432	curthread->fname = fname;
433	curthread->lineno = lineno;
434
435	_SPINUNLOCK(lock);
436
437	/* Schedule the next thread that is ready: */
438	_thread_kern_sched();
439}
440
441static void
442thread_kern_idle()
443{
444	int             i, found;
445	int		kern_pipe_added = 0;
446	int             nfds = 0;
447	int		timeout_ms = 0;
448	struct pthread	*pthread;
449	struct timespec ts;
450	struct timeval  tv;
451
452	/* Get the current time of day: */
453	GET_CURRENT_TOD(tv);
454	TIMEVAL_TO_TIMESPEC(&tv, &ts);
455
456	pthread = TAILQ_FIRST(&_waitingq);
457
458	if ((pthread == NULL) || (pthread->wakeup_time.tv_sec == -1)) {
459		/*
460		 * Either there are no threads in the waiting queue,
461		 * or there are no threads that can timeout.
462		 */
463		PANIC("Would idle forever");
464	}
465	else if (pthread->wakeup_time.tv_sec - ts.tv_sec > 60000)
466		/* Limit maximum timeout to prevent rollover. */
467		timeout_ms = 60000;
468	else {
469		/*
470		 * Calculate the time left for the next thread to
471		 * timeout:
472		 */
473		timeout_ms = ((pthread->wakeup_time.tv_sec - ts.tv_sec) *
474		    1000) + ((pthread->wakeup_time.tv_nsec - ts.tv_nsec) /
475		    1000000);
476		/*
477		 * Only idle if we would be.
478		 */
479		if (timeout_ms <= 0)
480			return;
481	}
482
483	/*
484	 * Check for a thread that became runnable due to a signal:
485	 */
486	if (PTHREAD_PRIOQ_FIRST() != NULL) {
487		/*
488		 * Since there is at least one runnable thread,
489		 * disable the wait.
490		 */
491		timeout_ms = 0;
492	}
493
494	/*
495	 * Idle.
496	 */
497	__sys_poll(NULL, 0, timeout_ms);
498
499	if (_spinblock_count != 0) {
500		/*
501		 * Enter a loop to look for threads waiting on a spinlock
502		 * that is now available.
503		 */
504		PTHREAD_WAITQ_SETACTIVE();
505		TAILQ_FOREACH(pthread, &_workq, qe) {
506			if (pthread->state == PS_SPINBLOCK) {
507				/*
508				 * If the lock is available, let the thread run.
509				 */
510				if (pthread->data.spinlock->access_lock == 0) {
511					PTHREAD_WAITQ_CLEARACTIVE();
512					PTHREAD_WORKQ_REMOVE(pthread);
513					PTHREAD_NEW_STATE(pthread,PS_RUNNING);
514					PTHREAD_WAITQ_SETACTIVE();
515
516					/*
517					 * One less thread in a spinblock state:
518					 */
519					_spinblock_count--;
520				}
521			}
522		}
523		PTHREAD_WAITQ_CLEARACTIVE();
524	}
525}
526
527void
528_thread_kern_set_timeout(const struct timespec * timeout)
529{
530	struct pthread	*curthread = _get_curthread();
531	struct timespec current_time;
532	struct timeval  tv;
533
534	/* Reset the timeout flag for the running thread: */
535	curthread->timeout = 0;
536
537	/* Check if the thread is to wait forever: */
538	if (timeout == NULL) {
539		/*
540		 * Set the wakeup time to something that can be recognised as
541		 * different to an actual time of day:
542		 */
543		curthread->wakeup_time.tv_sec = -1;
544		curthread->wakeup_time.tv_nsec = -1;
545	}
546	/* Check if no waiting is required: */
547	else if (timeout->tv_sec == 0 && timeout->tv_nsec == 0) {
548		/* Set the wake up time to 'immediately': */
549		curthread->wakeup_time.tv_sec = 0;
550		curthread->wakeup_time.tv_nsec = 0;
551	} else {
552		/* Get the current time: */
553		GET_CURRENT_TOD(tv);
554		TIMEVAL_TO_TIMESPEC(&tv, &current_time);
555
556		/* Calculate the time for the current thread to wake up: */
557		curthread->wakeup_time.tv_sec = current_time.tv_sec + timeout->tv_sec;
558		curthread->wakeup_time.tv_nsec = current_time.tv_nsec + timeout->tv_nsec;
559
560		/* Check if the nanosecond field needs to wrap: */
561		if (curthread->wakeup_time.tv_nsec >= 1000000000) {
562			/* Wrap the nanosecond field: */
563			curthread->wakeup_time.tv_sec += 1;
564			curthread->wakeup_time.tv_nsec -= 1000000000;
565		}
566	}
567}
568
569void
570_thread_kern_sig_defer(void)
571{
572	struct pthread	*curthread = _get_curthread();
573
574	/* Allow signal deferral to be recursive. */
575	curthread->sig_defer_count++;
576}
577
578void
579_thread_kern_sig_undefer(void)
580{
581	struct pthread	*curthread = _get_curthread();
582
583	/*
584	 * Perform checks to yield only if we are about to undefer
585	 * signals.
586	 */
587	if (curthread->sig_defer_count > 1) {
588		/* Decrement the signal deferral count. */
589		curthread->sig_defer_count--;
590	}
591	else if (curthread->sig_defer_count == 1) {
592		/* Reenable signals: */
593		curthread->sig_defer_count = 0;
594
595		/*
596		 * Check for asynchronous cancellation before delivering any
597		 * pending signals:
598		 */
599		if (((curthread->cancelflags & PTHREAD_AT_CANCEL_POINT) == 0) &&
600		    ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))
601			pthread_testcancel();
602	}
603}
604
605static inline void
606thread_run_switch_hook(pthread_t thread_out, pthread_t thread_in)
607{
608	pthread_t tid_out = thread_out;
609	pthread_t tid_in = thread_in;
610
611	if ((tid_out != NULL) &&
612	    (tid_out->flags & PTHREAD_FLAGS_PRIVATE) != 0)
613		tid_out = NULL;
614	if ((tid_in != NULL) &&
615	    (tid_in->flags & PTHREAD_FLAGS_PRIVATE) != 0)
616		tid_in = NULL;
617
618	if ((_sched_switch_hook != NULL) && (tid_out != tid_in)) {
619		/* Run the scheduler switch hook: */
620		_sched_switch_hook(tid_out, tid_in);
621	}
622}
623
624struct pthread *
625_get_curthread(void)
626{
627	if (_thread_initial == NULL)
628		_thread_init();
629
630	return (_thread_run);
631}
632
633void
634_set_curthread(struct pthread *newthread)
635{
636	_thread_run = newthread;
637}
638