diff options
Diffstat (limited to 'lib/libc_r/uthread/uthread_kern.c')
| -rw-r--r-- | lib/libc_r/uthread/uthread_kern.c | 1581 | 
1 files changed, 1581 insertions, 0 deletions
diff --git a/lib/libc_r/uthread/uthread_kern.c b/lib/libc_r/uthread/uthread_kern.c new file mode 100644 index 000000000000..35e8e29b9711 --- /dev/null +++ b/lib/libc_r/uthread/uthread_kern.c @@ -0,0 +1,1581 @@ +/* + * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + *    must display the following acknowledgement: + *	This product includes software developed by John Birrell. + * 4. Neither the name of the author nor the names of any co-contributors + *    may be used to endorse or promote products derived from this software + *    without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JOHN BIRRELL AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ +#include <errno.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <setjmp.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <sys/syscall.h> +#include <fcntl.h> +#ifdef _THREAD_SAFE +#include <pthread.h> +#include "pthread_private.h" + +/* Static variables: */ +static sigset_t sig_to_block = 0xffffffff; +static sigset_t sig_to_unblock = 0; + +/* Static function prototype definitions: */ +static void  +_thread_kern_select(int wait_reqd); +static void  +_thread_signal(pthread_t pthread, int sig); + +void +_thread_kern_sched(struct sigcontext * scp) +{ +#ifndef	__alpha +	char           *fdata; +#endif +	int             i; +	int             prio = -1; +	pthread_t       pthread; +	pthread_t       pthread_h = NULL; +	pthread_t       pthread_nxt = NULL; +	pthread_t       pthread_prv = NULL; +	pthread_t       pthread_s = NULL; +	struct itimerval itimer; +	struct timespec ts; +	struct timespec ts1; +	struct timeval  tv; +	struct timeval  tv1; + +	/* Block signals: */ +	_thread_kern_sig_block(NULL); + +	/* Check if this function was called from the signal handler: */ +	if (scp != NULL) { +		/* +		 * Copy the signal context to the current thread's jump +		 * buffer:  +		 */ +		memcpy(&_thread_run->saved_sigcontext, scp, sizeof(_thread_run->saved_sigcontext)); + +#ifndef	__alpha +		/* Point to the floating point data in the running thread: */ +		fdata = _thread_run->saved_fp; + +		/* Save the floating point data: */ +__asm__("fnsave %0": :"m"(*fdata)); +#endif + +		/* Flag the signal context as the last state saved: */ +		_thread_run->sig_saved = 1; +	} +	/* Save the state of the current thread: */ +	else if (_thread_sys_setjmp(_thread_run->saved_jmp_buf) != 0) { +		/* Unblock signals (just in case): */ +		_thread_kern_sig_unblock(0); + +		/* +		 * This point is reached when a longjmp() is called to +		 * restore the state of a thread.  +		 */ +		return; +	} else { +		/* Flag the jump buffer was the last state saved: */ +		_thread_run->sig_saved = 0; +	} + +	/* Point to the first dead thread (if there are any): */ +	pthread = _thread_dead; + +	/* There is no previous dead thread: */ +	pthread_prv = NULL; + +	/* Enter a loop to cleanup after dead threads: */ +	while (pthread != NULL) { +		/* Save a pointer to the next thread: */ +		pthread_nxt = pthread->nxt; + +		/* Check if this thread is one which is running: */ +		if (pthread == _thread_run || pthread == _thread_initial) { +			/* +			 * Don't destroy the running thread or the initial +			 * thread.  +			 */ +			pthread_prv = pthread; +		} +		/* +		 * Check if this thread has detached or if it is a signal +		 * handler thread:  +		 */ +		else if (((pthread->attr.flags & PTHREAD_DETACHED) != 0) || pthread->parent_thread != NULL) { +			/* Check if there is no previous dead thread: */ +			if (pthread_prv == NULL) { +				/* +				 * The dead thread is at the head of the +				 * list:  +				 */ +				_thread_dead = pthread_nxt; +			} else { +				/* +				 * The dead thread is not at the head of the +				 * list:  +				 */ +				pthread_prv->nxt = pthread->nxt; +			} + +			/* +			 * Check if the stack was not specified by the caller +			 * to pthread_create and has not been destroyed yet:  +			 */ +			if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { +				/* Free the stack of the dead thread: */ +				free(pthread->stack); +			} +			/* Free the memory allocated to the thread structure: */ +			free(pthread); +		} else { +			/* +			 * This thread has not detached, so do not destroy +			 * it:  +			 */ +			pthread_prv = pthread; + +			/* +			 * Check if the stack was not specified by the caller +			 * to pthread_create and has not been destroyed yet:  +			 */ +			if (pthread->attr.stackaddr_attr == NULL && pthread->stack != NULL) { +				/* Free the stack of the dead thread: */ +				free(pthread->stack); + +				/* +				 * NULL the stack pointer now that the memory +				 * has been freed:  +				 */ +				pthread->stack = NULL; +			} +		} + +		/* Point to the next thread: */ +		pthread = pthread_nxt; +	} + +	/* +	 * Enter a the scheduling loop that finds the next thread that is +	 * ready to run. This loop completes when there are no more threads +	 * in the global list or when a thread has its state restored by +	 * either a sigreturn (if the state was saved as a sigcontext) or a +	 * longjmp (if the state was saved by a setjmp).  +	 */ +	while (_thread_link_list != NULL) { +		/* Get the current time of day: */ +		gettimeofday(&tv, NULL); +		TIMEVAL_TO_TIMESPEC(&tv, &ts); + +		/* +		 * Poll file descriptors to update the state of threads +		 * waiting on file I/O where data may be available:  +		 */ +		_thread_kern_select(0); + +		/* +		 * Enter a loop to look for sleeping threads that are ready +		 * or threads with pending signals that are no longer +		 * blocked:  +		 */ +		for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +			/* Enter a loop to process the sending signals: */ +			for (i = 1; i < NSIG; i++) { +				/* +				 * Check if there are no pending signals of +				 * this type:  +				 */ +				if (pthread->sigpend[i] == 0) { +				} +				/* Check if this signal type is not masked: */ +				else if (sigismember(&pthread->sigmask, i) == 0) { +					/* +					 * Delete the signal from the set of +					 * pending signals for this thread:  +					 */ +					pthread->sigpend[i] -= 1; + +					/* +					 * Act on the signal for the current +					 * thread:  +					 */ +					_thread_signal(pthread, i); +				} else { +					/* +					 * This signal is masked, so make +					 * sure the count does not exceed 1:  +					 */ +					pthread->sigpend[i] = 1; +				} +			} + +			/* Check if this thread is to timeout: */ +			if (pthread->state == PS_COND_WAIT || +			    pthread->state == PS_SLEEP_WAIT || +			    pthread->state == PS_FDR_WAIT || +			    pthread->state == PS_FDW_WAIT || +			    pthread->state == PS_SELECT_WAIT) { +				/* Check if this thread is to wait forever: */ +				if (pthread->wakeup_time.ts_sec == -1) { +				} +				/* +				 * Check if this thread is to wakeup +				 * immediately or if it is past its wakeup +				 * time:  +				 */ +				else if ((pthread->wakeup_time.ts_sec == 0 && +					pthread->wakeup_time.ts_nsec == 0) || +					 (ts.ts_sec > pthread->wakeup_time.ts_sec) || +					 ((ts.ts_sec == pthread->wakeup_time.ts_sec) && +					  (ts.ts_nsec >= pthread->wakeup_time.ts_nsec))) { +					/* +					 * Check if this thread is waiting on +					 * select:  +					 */ +					if (pthread->state == PS_SELECT_WAIT) { +						/* +						 * The select has timed out, +						 * so zero the file +						 * descriptor sets:  +						 */ +						FD_ZERO(&pthread->data.select_data->readfds); +						FD_ZERO(&pthread->data.select_data->writefds); +						FD_ZERO(&pthread->data.select_data->exceptfds); +						pthread->data.select_data->nfds = 0; +					} +					/* +					 * Return an error as an interrupted +					 * wait:  +					 */ +					_thread_seterrno(pthread, EINTR); + +					/* +					 * Flag the timeout in the thread +					 * structure:  +					 */ +					pthread->timeout = 1; + +					/* +					 * Change the threads state to allow +					 * it to be restarted:  +					 */ +					pthread->state = PS_RUNNING; +				} +			} +		} + +		/* Check if there is a current thread: */ +		if (_thread_run != &_thread_kern_thread) { +			/* +			 * Save the current time as the time that the thread +			 * became inactive:  +			 */ +			_thread_run->last_inactive.tv_sec = tv.tv_sec; +			_thread_run->last_inactive.tv_usec = tv.tv_usec; + +			/* +			 * Accumulate the number of microseconds that this +			 * thread has run for:  +			 */ +			_thread_run->slice_usec += (_thread_run->last_inactive.tv_sec - +				_thread_run->last_active.tv_sec) * 1000000 + +				_thread_run->last_inactive.tv_usec - +				_thread_run->last_active.tv_usec; + +			/* +			 * Check if this thread has reached its allocated +			 * time slice period:  +			 */ +			if (_thread_run->slice_usec > TIMESLICE_USEC) { +				/* +				 * Flag the allocated time slice period as +				 * up:  +				 */ +				_thread_run->slice_usec = -1; +			} +		} +		/* Check if an incremental priority update is required: */ +		if (((tv.tv_sec - kern_inc_prio_time.tv_sec) * 1000000 + +		 tv.tv_usec - kern_inc_prio_time.tv_usec) > INC_PRIO_USEC) { +			/* +			 * Enter a loop to look for run-enabled threads that +			 * have not run since the last time that an +			 * incremental priority update was performed:  +			 */ +			for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +				/* Check if this thread is unable to run: */ +				if (pthread->state != PS_RUNNING) { +				} +				/* +				 * Check if the last time that this thread +				 * was run (as indicated by the last time it +				 * became inactive) is before the time that +				 * the last incremental priority check was +				 * made:  +				 */ +				else if (timercmp(&_thread_run->last_inactive, &kern_inc_prio_time, >)) { +					/* +					 * Increment the incremental priority +					 * for this thread in the hope that +					 * it will eventually get a chance to +					 * run:  +					 */ +					(pthread->inc_prio)++; +				} +			} + +			/* Save the new incremental priority update time: */ +			kern_inc_prio_time.tv_sec = tv.tv_sec; +			kern_inc_prio_time.tv_usec = tv.tv_usec; +		} +		/* +		 * Enter a loop to look for the first thread of the highest +		 * priority:  +		 */ +		for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +			/* Check if the current thread is unable to run: */ +			if (pthread->state != PS_RUNNING) { +			} +			/* +			 * Check if no run-enabled thread has been seen or if +			 * the current thread has a priority higher than the +			 * highest seen so far:  +			 */ +			else if (pthread_h == NULL || (pthread->pthread_priority + pthread->inc_prio) > prio) { +				/* +				 * Save this thread as the highest priority +				 * thread seen so far:  +				 */ +				pthread_h = pthread; +				prio = pthread->pthread_priority + pthread->inc_prio; +			} +		} + +		/* +		 * Enter a loop to look for a thread that: 1. Is run-enabled. +		 * 2. Has the required agregate priority. 3. Has not been +		 * allocated its allocated time slice. 4. Became inactive +		 * least recently.  +		 */ +		for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +			/* Check if the current thread is unable to run: */ +			if (pthread->state != PS_RUNNING) { +				/* Ignore threads that are not ready to run. */ +			} +			/* +			 * Check if the current thread as an agregate +			 * priority not equal to the highest priority found +			 * above:  +			 */ +			else if ((pthread->pthread_priority + pthread->inc_prio) != prio) { +				/* +				 * Ignore threads which have lower agregate +				 * priority.  +				 */ +			} +			/* +			 * Check if the current thread reached its time slice +			 * allocation last time it ran (or if it has not run +			 * yet):  +			 */ +			else if (pthread->slice_usec == -1) { +			} +			/* +			 * Check if an eligible thread has not been found +			 * yet, or if the current thread has an inactive time +			 * earlier than the last one seen:  +			 */ +			else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) { +				/* +				 * Save the pointer to the current thread as +				 * the most eligible thread seen so far:  +				 */ +				pthread_s = pthread; + +				/* +				 * Save the time that the selected thread +				 * became inactive:  +				 */ +				tv1.tv_sec = pthread->last_inactive.tv_sec; +				tv1.tv_usec = pthread->last_inactive.tv_usec; +			} +		} + +		/* +		 * Check if no thread was selected according to incomplete +		 * time slice allocation:  +		 */ +		if (pthread_s == NULL) { +			/* +			 * Enter a loop to look for any other thread that: 1. +			 * Is run-enabled. 2. Has the required agregate +			 * priority. 3. Became inactive least recently.  +			 */ +			for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +				/* +				 * Check if the current thread is unable to +				 * run:  +				 */ +				if (pthread->state != PS_RUNNING) { +					/* +					 * Ignore threads that are not ready +					 * to run.  +					 */ +				} +				/* +				 * Check if the current thread as an agregate +				 * priority not equal to the highest priority +				 * found above:  +				 */ +				else if ((pthread->pthread_priority + pthread->inc_prio) != prio) { +					/* +					 * Ignore threads which have lower +					 * agregate priority.    +					 */ +				} +				/* +				 * Check if an eligible thread has not been +				 * found yet, or if the current thread has an +				 * inactive time earlier than the last one +				 * seen:  +				 */ +				else if (pthread_s == NULL || timercmp(&pthread->last_inactive, &tv1, <)) { +					/* +					 * Save the pointer to the current +					 * thread as the most eligible thread +					 * seen so far:  +					 */ +					pthread_s = pthread; + +					/* +					 * Save the time that the selected +					 * thread became inactive:  +					 */ +					tv1.tv_sec = pthread->last_inactive.tv_sec; +					tv1.tv_usec = pthread->last_inactive.tv_usec; +				} +			} +		} +		/* Check if there are no threads ready to run: */ +		if (pthread_s == NULL) { +			/* +			 * Lock the pthread kernel by changing the pointer to +			 * the running thread to point to the global kernel +			 * thread structure:  +			 */ +			_thread_run = &_thread_kern_thread; + +			/* +			 * There are no threads ready to run, so wait until +			 * something happens that changes this condition:  +			 */ +			_thread_kern_select(1); +		} else { +			/* Make the selected thread the current thread: */ +			_thread_run = pthread_s; + +			/* +			 * Save the current time as the time that the thread +			 * became active:  +			 */ +			_thread_run->last_active.tv_sec = tv.tv_sec; +			_thread_run->last_active.tv_usec = tv.tv_usec; + +			/* +			 * Check if this thread is running for the first time +			 * or running again after using its full time slice +			 * allocation:  +			 */ +			if (_thread_run->slice_usec == -1) { +				/* Reset the accumulated time slice period: */ +				_thread_run->slice_usec = 0; +			} +			/* +			 * Reset the incremental priority now that this +			 * thread has been given the chance to run:  +			 */ +			_thread_run->inc_prio = 0; + +			/* Check if there is more than one thread: */ +			if (_thread_run != _thread_link_list || _thread_run->nxt != NULL) { +				/* +				 * Define the maximum time before a SIGVTALRM +				 * is required:  +				 */ +				itimer.it_value.tv_sec = 0; +				itimer.it_value.tv_usec = TIMESLICE_USEC; + +				/* +				 * The interval timer is not reloaded when it +				 * times out. The interval time needs to be +				 * calculated every time.  +				 */ +				itimer.it_interval.tv_sec = 0; +				itimer.it_interval.tv_usec = 0; + +				/* +				 * Enter a loop to look for threads waiting +				 * for a time:  +				 */ +				for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +					/* +					 * Check if this thread is to +					 * timeout:  +					 */ +					if (pthread->state == PS_COND_WAIT || +					  pthread->state == PS_SLEEP_WAIT || +					    pthread->state == PS_FDR_WAIT || +					    pthread->state == PS_FDW_WAIT || +					 pthread->state == PS_SELECT_WAIT) { +						/* +						 * Check if this thread is to +						 * wait forever:  +						 */ +						if (pthread->wakeup_time.ts_sec == -1) { +						} +						/* +						 * Check if this thread is to +						 * wakeup immediately:  +						 */ +						else if (pthread->wakeup_time.ts_sec == 0 && +							 pthread->wakeup_time.ts_nsec == 0) { +						} +						/* +						 * Check if the current time +						 * is after the wakeup time:  +						 */ +						else if ((ts.ts_sec > pthread->wakeup_time.ts_sec) || +							 ((ts.ts_sec == pthread->wakeup_time.ts_sec) && +							  (ts.ts_nsec > pthread->wakeup_time.ts_nsec))) { +						} else { +							/* +							 * Calculate the time +							 * until this thread +							 * is ready, allowing +							 * for the clock +							 * resolution:  +							 */ +							ts1.ts_sec = pthread->wakeup_time.ts_sec - ts.ts_sec; +							ts1.ts_nsec = pthread->wakeup_time.ts_nsec - ts.ts_nsec + +								CLOCK_RES_NSEC; + +							/* +							 * Check for +							 * underflow of the +							 * nanosecond field:  +							 */ +							if (ts1.ts_nsec < 0) { +								/* +								 * Allow for +								 * the +								 * underflow +								 * of the +								 * nanosecond +								 * field:  +								 */ +								ts1.ts_sec--; +								ts1.ts_nsec += 1000000000; +							} +							/* +							 * Check for overflow +							 * of the nanosecond +							 * field:  +							 */ +							if (ts1.ts_nsec >= 1000000000) { +								/* +								 * Allow for +								 * the +								 * overflow +								 * of the +								 * nanosecond +								 * field:  +								 */ +								ts1.ts_sec++; +								ts1.ts_nsec -= 1000000000; +							} +							/* +							 * Convert the +							 * timespec structure +							 * to a timeval +							 * structure:  +							 */ +							TIMESPEC_TO_TIMEVAL(&tv, &ts1); + +							/* +							 * Check if the +							 * thread will be +							 * ready sooner than +							 * the earliest one +							 * found so far:  +							 */ +							if (timercmp(&tv, &itimer.it_value, <)) { +								/* +								 * Update the +								 * time +								 * value:  +								 */ +								itimer.it_value.tv_sec = tv.tv_sec; +								itimer.it_value.tv_usec = tv.tv_usec; +							} +						} +					} +				} + +				/* +				 * Start the interval timer for the +				 * calculated time interval:  +				 */ +				if (setitimer(ITIMER_VIRTUAL, &itimer, NULL) != 0) { +					/* +					 * Cannot initialise the timer, so +					 * abort this process:  +					 */ +					PANIC("Cannot set virtual timer"); +				} +			} +			/* Check if a signal context was saved: */ +			if (_thread_run->sig_saved == 1) { +#ifndef	__alpha +				/* +				 * Point to the floating point data in the +				 * running thread:  +				 */ +				fdata = _thread_run->saved_fp; + +				/* Restore the floating point state: */ +		__asm__("frstor %0": :"m"(*fdata)); +#endif + +				/* +				 * Do a sigreturn to restart the thread that +				 * was interrupted by a signal:  +				 */ +				_thread_sys_sigreturn(&_thread_run->saved_sigcontext); +			} else { +				/* +				 * Do a longjmp to restart the thread that +				 * was context switched out (by a longjmp to +				 * a different thread):  +				 */ +				_thread_sys_longjmp(_thread_run->saved_jmp_buf, 1); +			} + +			/* This point should not be reached. */ +			PANIC("Thread has returned from sigreturn or longjmp"); +		} +	} + +	/* There are no more threads, so exit this process: */ +	exit(0); +} + +static void +_thread_signal(pthread_t pthread, int sig) +{ +	long            l; +	pthread_t       new_pthread; +	struct sigaction act; +	void           *arg; + +	/* Process according to thread state: */ +	switch (pthread->state) { +		/* States which do not change when a signal is trapped: */ +	case PS_COND_WAIT: +	case PS_DEAD: +	case PS_FDLR_WAIT: +	case PS_FDLW_WAIT: +	case PS_JOIN: +	case PS_MUTEX_WAIT: +	case PS_RUNNING: +	case PS_STATE_MAX: +	case PS_SIGTHREAD: +	case PS_SUSPENDED: +		/* Nothing to do here. */ +		break; + +		/* Wait for child: */ +	case PS_WAIT_WAIT: +		/* Check if the signal is from a child exiting: */ +		if (sig == SIGCHLD) { +			/* Reset the error: */ +			_thread_seterrno(pthread, 0); + +			/* Change the state of the thread to run: */ +			pthread->state = PS_RUNNING; +		} else { +			/* Return the 'interrupted' error: */ +			_thread_seterrno(pthread, EINTR); + +			/* Change the state of the thread to run: */ +			pthread->state = PS_RUNNING; +		} +		break; + +		/* +		 * States that are interrupted by the occurrence of a signal +		 * other than the scheduling alarm:  +		 */ +	case PS_FDR_WAIT: +	case PS_FDW_WAIT: +	case PS_SELECT_WAIT: +	case PS_SLEEP_WAIT: +	case PS_SIGWAIT: +		/* Return the 'interrupted' error: */ +		_thread_seterrno(pthread, EINTR); + +		/* Change the state of the thread to run: */ +		pthread->state = PS_RUNNING; +		break; +	} + +	/* Check if this signal is being ignored: */ +	if (pthread->act[sig - 1].sa_handler == SIG_IGN) { +		/* Ignore the signal for this thread. */ +	} +	/* Check if this signal is to use the default handler: */ +	else if (pthread->act[sig - 1].sa_handler == SIG_DFL) { +		/* Process according to signal type: */ +		switch (sig) { +			/* Signals which cause core dumps: */ +		case SIGQUIT: +		case SIGILL: +		case SIGTRAP: +		case SIGABRT: +		case SIGEMT: +		case SIGFPE: +		case SIGBUS: +		case SIGSEGV: +		case SIGSYS: +			/* Clear the signal action: */ +			sigfillset(&act.sa_mask); +			act.sa_handler = SIG_DFL; +			act.sa_flags = SA_RESTART; +			_thread_sys_sigaction(sig, &act, NULL); + +			/* +			 * Do a sigreturn back to where the signal was +			 * detected and a core dump should occur:  +			 */ +			_thread_sys_sigreturn(&pthread->saved_sigcontext); +			break; + +			/* Default processing for other signals: */ +		default: +			/* +			 * ### Default processing is a problem to resolve!      +			 * ###  +			 */ +			break; +		} +	} else { +		/* +		 * Cast the signal number as a long and then to a void +		 * pointer. Sigh. This is POSIX.  +		 */ +		l = (long) sig; +		arg = (void *) l; + +		/* Create a signal handler thread, but don't run it yet: */ +		if (_thread_create(&new_pthread, NULL, (void *) pthread->act[sig - 1].sa_handler, arg, pthread) != 0) { +			/* +			 * Error creating signal handler thread, so abort +			 * this process:  +			 */ +			PANIC("Cannot create signal handler thread"); +		} +	} + +	/* Nothing to return. */ +	return; +} + +void +_thread_kern_sig_block(int *status) +{ +	sigset_t        oset; + +	/* +	 * Block all signals so that the process will not be interrupted by +	 * signals:  +	 */ +	_thread_sys_sigprocmask(SIG_SETMASK, &sig_to_block, &oset); + +	/* Check if the caller wants the current block status returned: */ +	if (status != NULL) { +		/* Return the previous signal block status: */ +		*status = (oset != 0); +	} +	return; +} + +void +_thread_kern_sig_unblock(int status) +{ +	sigset_t        oset; + +	/* +	 * Check if the caller thinks that signals weren't blocked when it +	 * called _thread_kern_sig_block:  +	 */ +	if (status == 0) { +		/* +		 * Unblock all signals so that the process will be +		 * interrupted when a signal occurs:  +		 */ +		_thread_sys_sigprocmask(SIG_SETMASK, &sig_to_unblock, &oset); +	} +	return; +} + +void +_thread_kern_sched_state(enum pthread_state state, char *fname, int lineno) +{ +	/* Change the state of the current thread: */ +	_thread_run->state = state; + +	/* Schedule the next thread that is ready: */ +	_thread_kern_sched(NULL); +	return; +} + +static void +_thread_kern_select(int wait_reqd) +{ +	char            bufr[128]; +	fd_set          fd_set_except; +	fd_set          fd_set_read; +	fd_set          fd_set_write; +	int             count = 0; +	int             count_dec; +	int             found_one; +	int             i; +	int             nfds = -1; +	int             settimeout; +	pthread_t       pthread; +	ssize_t         num; +	struct timespec ts; +	struct timespec ts1; +	struct timeval *p_tv; +	struct timeval  tv; +	struct timeval  tv1; + +	/* Zero the file descriptor sets: */ +	FD_ZERO(&fd_set_read); +	FD_ZERO(&fd_set_write); +	FD_ZERO(&fd_set_except); + +	/* Check if the caller wants to wait: */ +	if (wait_reqd) { +		/* +		 * Add the pthread kernel pipe file descriptor to the read +		 * set:  +		 */ +		FD_SET(_thread_kern_pipe[0], &fd_set_read); +		nfds = _thread_kern_pipe[0]; + +		/* Get the current time of day: */ +		gettimeofday(&tv, NULL); +		TIMEVAL_TO_TIMESPEC(&tv, &ts); +	} +	/* Initialise the time value structure: */ +	tv.tv_sec = 0; +	tv.tv_usec = 0; + +	/* +	 * Enter a loop to process threads waiting on either file descriptors +	 * or times:  +	 */ +	for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +		/* Assume that this state does not time out: */ +		settimeout = 0; + +		/* Process according to thread state: */ +		switch (pthread->state) { +			/* +			 * States which do not depend on file descriptor I/O +			 * operations or timeouts:  +			 */ +		case PS_DEAD: +		case PS_FDLR_WAIT: +		case PS_FDLW_WAIT: +		case PS_JOIN: +		case PS_MUTEX_WAIT: +		case PS_RUNNING: +		case PS_SIGTHREAD: +		case PS_SIGWAIT: +		case PS_STATE_MAX: +		case PS_WAIT_WAIT: +		case PS_SUSPENDED: +			/* Nothing to do here. */ +			break; + +			/* File descriptor read wait: */ +		case PS_FDR_WAIT: +			/* Add the file descriptor to the read set: */ +			FD_SET(pthread->data.fd.fd, &fd_set_read); + +			/* +			 * Check if this file descriptor is greater than any +			 * of those seen so far:  +			 */ +			if (pthread->data.fd.fd > nfds) { +				/* Remember this file descriptor: */ +				nfds = pthread->data.fd.fd; +			} +			/* Increment the file descriptor count: */ +			count++; + +			/* This state can time out: */ +			settimeout = 1; +			break; + +			/* File descriptor write wait: */ +		case PS_FDW_WAIT: +			/* Add the file descriptor to the write set: */ +			FD_SET(pthread->data.fd.fd, &fd_set_write); + +			/* +			 * Check if this file descriptor is greater than any +			 * of those seen so far:  +			 */ +			if (pthread->data.fd.fd > nfds) { +				/* Remember this file descriptor: */ +				nfds = pthread->data.fd.fd; +			} +			/* Increment the file descriptor count: */ +			count++; + +			/* This state can time out: */ +			settimeout = 1; +			break; + +			/* States that time out: */ +		case PS_SLEEP_WAIT: +		case PS_COND_WAIT: +			/* Flag a timeout as required: */ +			settimeout = 1; +			break; + +			/* Select wait: */ +		case PS_SELECT_WAIT: +			/* +			 * Enter a loop to process each file descriptor in +			 * the thread-specific file descriptor sets:  +			 */ +			for (i = 0; i < pthread->data.select_data->nfds; i++) { +				/* +				 * Check if this file descriptor is set for +				 * exceptions:  +				 */ +				if (FD_ISSET(i, &pthread->data.select_data->exceptfds)) { +					/* +					 * Add the file descriptor to the +					 * exception set:  +					 */ +					FD_SET(i, &fd_set_except); + +					/* +					 * Increment the file descriptor +					 * count:  +					 */ +					count++; + +					/* +					 * Check if this file descriptor is +					 * greater than any of those seen so +					 * far:  +					 */ +					if (i > nfds) { +						/* +						 * Remember this file +						 * descriptor:  +						 */ +						nfds = i; +					} +				} +				/* +				 * Check if this file descriptor is set for +				 * write:  +				 */ +				if (FD_ISSET(i, &pthread->data.select_data->writefds)) { +					/* +					 * Add the file descriptor to the +					 * write set:  +					 */ +					FD_SET(i, &fd_set_write); + +					/* +					 * Increment the file descriptor +					 * count:  +					 */ +					count++; + +					/* +					 * Check if this file descriptor is +					 * greater than any of those seen so +					 * far:  +					 */ +					if (i > nfds) { +						/* +						 * Remember this file +						 * descriptor:  +						 */ +						nfds = i; +					} +				} +				/* +				 * Check if this file descriptor is set for +				 * read:  +				 */ +				if (FD_ISSET(i, &pthread->data.select_data->readfds)) { +					/* +					 * Add the file descriptor to the +					 * read set:  +					 */ +					FD_SET(i, &fd_set_read); + +					/* +					 * Increment the file descriptor +					 * count:  +					 */ +					count++; + +					/* +					 * Check if this file descriptor is +					 * greater than any of those seen so +					 * far:  +					 */ +					if (i > nfds) { +						/* +						 * Remember this file +						 * descriptor:  +						 */ +						nfds = i; +					} +				} +			} + +			/* This state can time out: */ +			settimeout = 1; +			break; +		} + +		/* +		 * Check if the caller wants to wait and if the thread state +		 * is one that times out:  +		 */ +		if (wait_reqd && settimeout) { +			/* Check if this thread wants to wait forever: */ +			if (pthread->wakeup_time.ts_sec == -1) { +			} +			/* Check if this thread doesn't want to wait at all: */ +			else if (pthread->wakeup_time.ts_sec == 0 && +				 pthread->wakeup_time.ts_nsec == 0) { +				/* Override the caller's request to wait: */ +				wait_reqd = 0; +			} else { +				/* +				 * Calculate the time until this thread is +				 * ready, allowing for the clock resolution:  +				 */ +				ts1.ts_sec = pthread->wakeup_time.ts_sec - ts.ts_sec; +				ts1.ts_nsec = pthread->wakeup_time.ts_nsec - ts.ts_nsec + +					CLOCK_RES_NSEC; + +				/* +				 * Check for underflow of the nanosecond +				 * field:  +				 */ +				if (ts1.ts_nsec < 0) { +					/* +					 * Allow for the underflow of the +					 * nanosecond field:  +					 */ +					ts1.ts_sec--; +					ts1.ts_nsec += 1000000000; +				} +				/* +				 * Check for overflow of the nanosecond +				 * field:  +				 */ +				if (ts1.ts_nsec >= 1000000000) { +					/* +					 * Allow for the overflow of the +					 * nanosecond field:  +					 */ +					ts1.ts_sec++; +					ts1.ts_nsec -= 1000000000; +				} +				/* +				 * Convert the timespec structure to a +				 * timeval structure:  +				 */ +				TIMESPEC_TO_TIMEVAL(&tv1, &ts1); + +				/* +				 * Check if no time value has been found yet, +				 * or if the thread will be ready sooner that +				 * the earliest one found so far:  +				 */ +				if ((tv.tv_sec == 0 && tv.tv_usec == 0) || timercmp(&tv1, &tv, <)) { +					/* Update the time value: */ +					tv.tv_sec = tv1.tv_sec; +					tv.tv_usec = tv1.tv_usec; +				} +			} +		} +	} + +	/* Check if the caller wants to wait: */ +	if (wait_reqd) { +		/* Check if no threads were found with timeouts: */ +		if (tv.tv_sec == 0 && tv.tv_usec == 0) { +			/* Wait forever: */ +			p_tv = NULL; +		} else { +			/* +			 * Point to the time value structure which contains +			 * the earliest time that a thread will be ready:  +			 */ +			p_tv = &tv; +		} + +		/* +		 * Flag the pthread kernel as in a select. This is to avoid +		 * the window between the next statement that unblocks +		 * signals and the select statement which follows.  +		 */ +		_thread_kern_in_select = 1; + +		/* Unblock all signals: */ +		_thread_kern_sig_unblock(0); + +		/* +		 * Wait for a file descriptor to be ready for read, write, or +		 * an exception, or a timeout to occur:  +		 */ +		count = _thread_sys_select(nfds + 1, &fd_set_read, &fd_set_write, &fd_set_except, p_tv); + +		/* Block all signals again: */ +		_thread_kern_sig_block(NULL); + +		/* Reset the kernel in select flag: */ +		_thread_kern_in_select = 0; + +		/* +		 * Check if it is possible that there are bytes in the kernel +		 * read pipe waiting to be read:  +		 */ +		if (count < 0 || FD_ISSET(_thread_kern_pipe[0], &fd_set_read)) { +			/* +			 * Check if the kernel read pipe was included in the +			 * count:  +			 */ +			if (count > 0) { +				/* +				 * Remove the kernel read pipe from the +				 * count:  +				 */ +				FD_CLR(_thread_kern_pipe[0], &fd_set_read); + +				/* Decrement the count of file descriptors: */ +				count--; +			} +			/* +			 * Enter a loop to read (and trash) bytes from the +			 * pthread kernel pipe:  +			 */ +			while ((num = _thread_sys_read(_thread_kern_pipe[0], bufr, sizeof(bufr))) > 0) { +				/* +				 * The buffer read contains one byte per +				 * signal and each byte is the signal number. +				 * This data is not used, but the fact that +				 * the signal handler wrote to the pipe *is* +				 * used to cause the _thread_sys_select call +				 * to complete if the signal occurred between +				 * the time when signals were unblocked and +				 * the _thread_sys_select select call being +				 * made.  +				 */ +			} +		} +	} +	/* Check if there are file descriptors to poll: */ +	else if (count > 0) { +		/* +		 * Point to the time value structure which has been zeroed so +		 * that the call to _thread_sys_select will not wait:  +		 */ +		p_tv = &tv; + +		/* Poll file descrptors without wait: */ +		count = _thread_sys_select(nfds + 1, &fd_set_read, &fd_set_write, &fd_set_except, p_tv); +	} +	/* +	 * Check if the select call was interrupted, or some other error +	 * occurred:  +	 */ +	if (count < 0) { +		/* Check if the select call was interrupted: */ +		if (errno == EINTR) { +			/* +			 * Interrupted calls are expected. The interrupting +			 * signal will be in the sigpend array.  +			 */ +		} else { +			/* This should not occur: */ +		} +	} +	/* Check if no file descriptors are ready: */ +	else if (count == 0) { +		/* Nothing to do here.                                              */ +	} else { +		/* +		 * Enter a loop to look for threads waiting on file +		 * descriptors that are flagged as available by the +		 * _thread_sys_select syscall:  +		 */ +		for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { +			/* Process according to thread state: */ +			switch (pthread->state) { +				/* +				 * States which do not depend on file +				 * descriptor I/O operations:  +				 */ +			case PS_RUNNING: +			case PS_COND_WAIT: +			case PS_DEAD: +			case PS_FDLR_WAIT: +			case PS_FDLW_WAIT: +			case PS_JOIN: +			case PS_MUTEX_WAIT: +			case PS_SIGWAIT: +			case PS_SLEEP_WAIT: +			case PS_WAIT_WAIT: +			case PS_SIGTHREAD: +			case PS_STATE_MAX: +			case PS_SUSPENDED: +				/* Nothing to do here. */ +				break; + +				/* File descriptor read wait: */ +			case PS_FDR_WAIT: +				/* +				 * Check if the file descriptor is available +				 * for read:  +				 */ +				if (FD_ISSET(pthread->data.fd.fd, &fd_set_read)) { +					/* +					 * Change the thread state to allow +					 * it to read from the file when it +					 * is scheduled next:  +					 */ +					pthread->state = PS_RUNNING; +				} +				break; + +				/* File descriptor write wait: */ +			case PS_FDW_WAIT: +				/* +				 * Check if the file descriptor is available +				 * for write:  +				 */ +				if (FD_ISSET(pthread->data.fd.fd, &fd_set_write)) { +					/* +					 * Change the thread state to allow +					 * it to write to the file when it is +					 * scheduled next:  +					 */ +					pthread->state = PS_RUNNING; +				} +				break; + +				/* Select wait: */ +			case PS_SELECT_WAIT: +				/* +				 * Reset the flag that indicates if a file +				 * descriptor is ready for some type of +				 * operation:  +				 */ +				count_dec = 0; + +				/* +				 * Enter a loop to search though the +				 * thread-specific select file descriptors +				 * for the first descriptor that is ready:  +				 */ +				for (i = 0; i < pthread->data.select_data->nfds && count_dec == 0; i++) { +					/* +					 * Check if this file descriptor does +					 * not have an exception:  +					 */ +					if (FD_ISSET(i, &pthread->data.select_data->exceptfds) && FD_ISSET(i, &fd_set_except)) { +						/* +						 * Flag this file descriptor +						 * as ready:  +						 */ +						count_dec = 1; +					} +					/* +					 * Check if this file descriptor is +					 * not ready for write:  +					 */ +					if (FD_ISSET(i, &pthread->data.select_data->writefds) && FD_ISSET(i, &fd_set_write)) { +						/* +						 * Flag this file descriptor +						 * as ready:  +						 */ +						count_dec = 1; +					} +					/* +					 * Check if this file descriptor is +					 * not ready for read:  +					 */ +					if (FD_ISSET(i, &pthread->data.select_data->readfds) && FD_ISSET(i, &fd_set_read)) { +						/* +						 * Flag this file descriptor +						 * as ready:  +						 */ +						count_dec = 1; +					} +				} + +				/* +				 * Check if any file descriptors are ready +				 * for the current thread:  +				 */ +				if (count_dec) { +					/* +					 * Reset the count of file +					 * descriptors that are ready for +					 * this thread:  +					 */ +					found_one = 0; + +					/* +					 * Enter a loop to search though the +					 * thread-specific select file +					 * descriptors:  +					 */ +					for (i = 0; i < pthread->data.select_data->nfds; i++) { +						/* +						 * Reset the count of +						 * operations for which the +						 * current file descriptor is +						 * ready:  +						 */ +						count_dec = 0; + +						/* +						 * Check if this file +						 * descriptor is selected for +						 * exceptions:  +						 */ +						if (FD_ISSET(i, &pthread->data.select_data->exceptfds)) { +							/* +							 * Check if this file +							 * descriptor has an +							 * exception:  +							 */ +							if (FD_ISSET(i, &fd_set_except)) { +								/* +								 * Increment +								 * the count +								 * for this +								 * file:  +								 */ +								count_dec++; +							} else { +								/* +								 * Clear the +								 * file +								 * descriptor +								 * in the +								 * thread-spec +								 * ific file +								 * descriptor +								 * set:  +								 */ +								FD_CLR(i, &pthread->data.select_data->exceptfds); +							} +						} +						/* +						 * Check if this file +						 * descriptor is selected for +						 * write:  +						 */ +						if (FD_ISSET(i, &pthread->data.select_data->writefds)) { +							/* +							 * Check if this file +							 * descriptor is +							 * ready for write:  +							 */ +							if (FD_ISSET(i, &fd_set_write)) { +								/* +								 * Increment +								 * the count +								 * for this +								 * file:  +								 */ +								count_dec++; +							} else { +								/* +								 * Clear the +								 * file +								 * descriptor +								 * in the +								 * thread-spec +								 * ific file +								 * descriptor +								 * set:  +								 */ +								FD_CLR(i, &pthread->data.select_data->writefds); +							} +						} +						/* +						 * Check if this file +						 * descriptor is selected for +						 * read:  +						 */ +						if (FD_ISSET(i, &pthread->data.select_data->readfds)) { +							/* +							 * Check if this file +							 * descriptor is +							 * ready for read:  +							 */ +							if (FD_ISSET(i, &fd_set_read)) { +								/* +								 * Increment +								 * the count +								 * for this +								 * file:  +								 */ +								count_dec++; +							} else { +								/* +								 * Clear the +								 * file +								 * descriptor +								 * in the +								 * thread-spec +								 * ific file +								 * descriptor +								 * set:  +								 */ +								FD_CLR(i, &pthread->data.select_data->readfds); +							} +						} +						/* +						 * Check if the current file +						 * descriptor is ready for +						 * any one of the operations:  +						 */ +						if (count_dec > 0) { +							/* +							 * Increment the +							 * count of file +							 * descriptors that +							 * are ready for the +							 * current thread:  +							 */ +							found_one++; +						} +					} + +					/* +					 * Return the number of file +					 * descriptors that are ready:  +					 */ +					pthread->data.select_data->nfds = found_one; + +					/* +					 * Change the state of the current +					 * thread to run:  +					 */ +					pthread->state = PS_RUNNING; +				} +				break; +			} +		} +	} + +	/* Nothing to return. */ +	return; +} + +void +_thread_kern_set_timeout(struct timespec * timeout) +{ +	struct timespec current_time; +	struct timeval  tv; + +	/* Reset the timeout flag for the running thread: */ +	_thread_run->timeout = 0; + +	/* Check if the thread is to wait forever: */ +	if (timeout == NULL) { +		/* +		 * Set the wakeup time to something that can be recognised as +		 * different to an actual time of day:  +		 */ +		_thread_run->wakeup_time.ts_sec = -1; +		_thread_run->wakeup_time.ts_nsec = -1; +	} +	/* Check if no waiting is required: */ +	else if (timeout->ts_sec == 0 && timeout->ts_nsec == 0) { +		/* Set the wake up time to 'immediately': */ +		_thread_run->wakeup_time.ts_sec = 0; +		_thread_run->wakeup_time.ts_nsec = 0; +	} else { +		/* Get the current time: */ +		gettimeofday(&tv, NULL); +		TIMEVAL_TO_TIMESPEC(&tv, ¤t_time); + +		/* Calculate the time for the current thread to wake up: */ +		_thread_run->wakeup_time.ts_sec = current_time.ts_sec + timeout->ts_sec; +		_thread_run->wakeup_time.ts_nsec = current_time.ts_nsec + timeout->ts_nsec; + +		/* Check if the nanosecond field needs to wrap: */ +		if (_thread_run->wakeup_time.ts_nsec >= 1000000000) { +			/* Wrap the nanosecond field: */ +			_thread_run->wakeup_time.ts_sec += 1; +			_thread_run->wakeup_time.ts_nsec -= 1000000000; +		} +	} +	return; +} +#endif  | 
