summaryrefslogtreecommitdiffstats
path: root/lib/libc_r/uthread/uthread_kern.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libc_r/uthread/uthread_kern.c')
-rw-r--r--lib/libc_r/uthread/uthread_kern.c1581
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, &current_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
OpenPOWER on IntegriCloud