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 0000000..35e8e29 --- /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 |