diff options
author | jb <jb@FreeBSD.org> | 1998-04-29 09:59:34 +0000 |
---|---|---|
committer | jb <jb@FreeBSD.org> | 1998-04-29 09:59:34 +0000 |
commit | 6c9ee23acc144ec10f869bbd9b872379224a8938 (patch) | |
tree | b0024d273ef0465a33cf00f2b90524d004b9c0b1 /lib/libkse/thread | |
parent | a44088edc8056e79e7c0b3b27ea2c5c3355368e9 (diff) | |
download | FreeBSD-src-6c9ee23acc144ec10f869bbd9b872379224a8938.zip FreeBSD-src-6c9ee23acc144ec10f869bbd9b872379224a8938.tar.gz |
Change signal model to match POSIX (i.e. one set of signal handlers
for the process, not a separate set for each thread). By default, the
process now only has signal handlers installed for SIGVTALRM, SIGINFO
and SIGCHLD. The thread kernel signal handler is installed for other
signals on demand. This means that SIG_IGN and SIG_DFL processing is now
left to the kernel, not the thread kernel.
Change the signal dispatch to no longer use a signal thread, and
call the signal handler using the stack of the thread that has the
signal pending.
Change the atomic lock method to use test-and-set asm code with
a yield if blocked. This introduces separate locks for each type
of object instead of blocking signals to prevent a context
switch. It was this blocking of signals that caused the performance
degradation the people have noted.
This is a *big* change!
Diffstat (limited to 'lib/libkse/thread')
38 files changed, 865 insertions, 1224 deletions
diff --git a/lib/libkse/thread/Makefile.inc b/lib/libkse/thread/Makefile.inc index 4f1b308..924453f 100644 --- a/lib/libkse/thread/Makefile.inc +++ b/lib/libkse/thread/Makefile.inc @@ -1,4 +1,4 @@ -# $Id: Makefile.inc,v 1.9 1997/05/06 00:49:36 jdp Exp $ +# $Id: Makefile.inc,v 1.10 1997/11/24 23:04:29 alex Exp $ # uthread sources .PATH: ${.CURDIR}/uthread @@ -34,6 +34,7 @@ SRCS+= \ uthread_fcntl.c \ uthread_fd.c \ uthread_file.c \ + uthread_find_thread.c \ uthread_flock.c \ uthread_fork.c \ uthread_fstat.c \ @@ -51,7 +52,6 @@ SRCS+= \ uthread_kern.c \ uthread_kill.c \ uthread_listen.c \ - uthread_longjmp.c \ uthread_mattr_init.c \ uthread_mattr_kind_np.c \ uthread_multi_np.c \ @@ -70,23 +70,22 @@ SRCS+= \ uthread_self.c \ uthread_sendto.c \ uthread_seterrno.c \ - uthread_setjmp.c \ uthread_setprio.c \ uthread_setsockopt.c \ uthread_shutdown.c \ uthread_sig.c \ uthread_sigaction.c \ uthread_sigblock.c \ - uthread_single_np.c \ uthread_sigmask.c \ - uthread_signal.c \ uthread_sigprocmask.c \ uthread_sigsetmask.c \ uthread_sigsuspend.c \ uthread_sigwait.c \ + uthread_single_np.c \ uthread_socket.c \ uthread_socketpair.c \ uthread_spec.c \ + uthread_spinlock.c \ uthread_suspend_np.c \ uthread_wait4.c \ uthread_write.c \ diff --git a/lib/libkse/thread/thr_close.c b/lib/libkse/thread/thr_close.c index 26a8fbf..5678e2e 100644 --- a/lib/libkse/thread/thr_close.c +++ b/lib/libkse/thread/thr_close.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -48,9 +48,6 @@ close(int fd) /* Lock the file descriptor while the file is closed: */ if ((ret = _thread_fd_lock(fd, FD_RDWR, NULL, __FILE__, __LINE__)) == 0) { - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Get file descriptor status. */ fstat(fd, &sb); @@ -84,12 +81,8 @@ close(int fd) /* Close the file descriptor: */ ret = _thread_sys_close(fd); - /* Free the file descriptor table entry: */ free(_thread_fd_table[fd]); _thread_fd_table[fd] = NULL; - - /* Unblock signals again: */ - _thread_kern_sig_unblock(status); } return (ret); } diff --git a/lib/libkse/thread/thr_cond.c b/lib/libkse/thread/thr_cond.c index e7fcc62..cb08853 100644 --- a/lib/libkse/thread/thr_cond.c +++ b/lib/libkse/thread/thr_cond.c @@ -43,9 +43,9 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr) pthread_cond_t pcond; int rval = 0; - if (cond == NULL) { + if (cond == NULL) rval = EINVAL; - } else { + else { /* * Check if a pointer to a condition variable attribute * structure was passed by the caller: @@ -85,6 +85,7 @@ pthread_cond_init(pthread_cond_t * cond, const pthread_condattr_t * cond_attr) _thread_queue_init(&pcond->c_queue); pcond->c_flags |= COND_FLAGS_INITED; pcond->c_type = type; + pcond->access_lock = 0; *cond = pcond; } } @@ -98,31 +99,23 @@ pthread_cond_destroy(pthread_cond_t * cond) { int rval = 0; - if (cond == NULL || *cond == NULL) { + if (cond == NULL || *cond == NULL) rval = EINVAL; - } else { - /* Process according to condition variable type: */ - switch ((*cond)->c_type) { - /* Fast condition variable: */ - case COND_TYPE_FAST: - /* Nothing to do here. */ - break; + else { + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); - /* Trap invalid condition variable types: */ - default: - /* Return an invalid argument error: */ - rval = EINVAL; - break; - } + /* + * Free the memory allocated for the condition + * variable structure: + */ + free(*cond); - /* Check for errors: */ - if (rval == 0) { - /* Destroy the contents of the condition structure: */ - _thread_queue_init(&(*cond)->c_queue); - (*cond)->c_flags = 0; - free(*cond); - *cond = NULL; - } + /* + * NULL the caller's pointer now that the condition + * variable has been destroyed: + */ + *cond = NULL; } /* Return the completion status: */ return (rval); @@ -143,8 +136,8 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) */ else if (*cond != NULL || (rval = pthread_cond_init(cond,NULL)) == 0) { - /* Block signals: */ - _thread_kern_sig_block(&status); + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); /* Process according to condition variable type: */ switch ((*cond)->c_type) { @@ -162,12 +155,15 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) /* Wait forever: */ _thread_run->wakeup_time.tv_sec = -1; + /* Unlock the condition variable structure: */ + _atomic_unlock(&(*cond)->access_lock); + /* Schedule the next thread: */ _thread_kern_sched_state(PS_COND_WAIT, __FILE__, __LINE__); - /* Block signals: */ - _thread_kern_sig_block(NULL); + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); /* Lock the mutex: */ rval = pthread_mutex_lock(mutex); @@ -180,8 +176,8 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex) break; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the condition variable structure: */ + _atomic_unlock(&(*cond)->access_lock); } /* Return the completion status: */ @@ -204,8 +200,8 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, */ else if (*cond != NULL || (rval = pthread_cond_init(cond,NULL)) == 0) { - /* Block signals: */ - _thread_kern_sig_block(&status); + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); /* Process according to condition variable type: */ switch ((*cond)->c_type) { @@ -233,12 +229,15 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, */ _thread_queue_deq(&(*cond)->c_queue); } else { + /* Unlock the condition variable structure: */ + _atomic_unlock(&(*cond)->access_lock); + /* Schedule the next thread: */ _thread_kern_sched_state(PS_COND_WAIT, __FILE__, __LINE__); - /* Block signals: */ - _thread_kern_sig_block(NULL); + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); /* Lock the mutex: */ if ((rval = pthread_mutex_lock(mutex)) != 0) { @@ -258,8 +257,8 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex, break; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the condition variable structure: */ + _atomic_unlock(&(*cond)->access_lock); } /* Return the completion status: */ @@ -273,11 +272,11 @@ pthread_cond_signal(pthread_cond_t * cond) int status; pthread_t pthread; - if (cond == NULL || *cond == NULL) { + if (cond == NULL || *cond == NULL) rval = EINVAL; - } else { - /* Block signals: */ - _thread_kern_sig_block(&status); + else { + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); /* Process according to condition variable type: */ switch ((*cond)->c_type) { @@ -297,8 +296,8 @@ pthread_cond_signal(pthread_cond_t * cond) break; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the condition variable structure: */ + _atomic_unlock(&(*cond)->access_lock); } /* Return the completion status: */ @@ -312,34 +311,38 @@ pthread_cond_broadcast(pthread_cond_t * cond) int status; pthread_t pthread; - /* Block signals: */ - _thread_kern_sig_block(&status); + if (cond == NULL || *cond == NULL) + rval = EINVAL; + else { + /* Lock the condition variable structure: */ + _spinlock(&(*cond)->access_lock); - /* Process according to condition variable type: */ - switch ((*cond)->c_type) { - /* Fast condition variable: */ - case COND_TYPE_FAST: - /* - * Enter a loop to bring all threads off the - * condition queue: - */ - while ((pthread = - _thread_queue_deq(&(*cond)->c_queue)) != NULL) { - /* Allow the thread to run: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); + /* Process according to condition variable type: */ + switch ((*cond)->c_type) { + /* Fast condition variable: */ + case COND_TYPE_FAST: + /* + * Enter a loop to bring all threads off the + * condition queue: + */ + while ((pthread = + _thread_queue_deq(&(*cond)->c_queue)) != NULL) { + /* Allow the thread to run: */ + PTHREAD_NEW_STATE(pthread,PS_RUNNING); + } + break; + + /* Trap invalid condition variable types: */ + default: + /* Return an invalid argument error: */ + rval = EINVAL; + break; } - break; - /* Trap invalid condition variable types: */ - default: - /* Return an invalid argument error: */ - rval = EINVAL; - break; + /* Unlock the condition variable structure: */ + _atomic_unlock(&(*cond)->access_lock); } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - /* Return the completion status: */ return (rval); } diff --git a/lib/libkse/thread/thr_create.c b/lib/libkse/thread/thr_create.c index c9c6c9f..7603dd3 100644 --- a/lib/libkse/thread/thr_create.c +++ b/lib/libkse/thread/thr_create.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -43,8 +43,8 @@ #include "libc_private.h" int -_thread_create(pthread_t * thread, const pthread_attr_t * attr, - void *(*start_routine) (void *), void *arg, pthread_t parent) +pthread_create(pthread_t * thread, const pthread_attr_t * attr, + void *(*start_routine) (void *), void *arg) { int i; int ret = 0; @@ -53,8 +53,11 @@ _thread_create(pthread_t * thread, const pthread_attr_t * attr, pthread_attr_t pattr; void *stack; - /* Block signals: */ - _thread_kern_sig_block(&status); + /* + * Locking functions in libc are required when there are + * threads other than the initial thread. + */ + __isthreaded = 1; /* Allocate memory for the thread structure: */ if ((new_thread = (pthread_t) malloc(sizeof(struct pthread))) == NULL) { @@ -103,71 +106,33 @@ _thread_create(pthread_t * thread, const pthread_attr_t * attr, /* Initialise the thread for signals: */ new_thread->sigmask = _thread_run->sigmask; - /* - * Enter a loop to initialise the signal handler - * array: - */ - for (i = 1; i < NSIG; i++) { - /* Default the signal handler: */ - sigfillset(&new_thread->act[i - 1].sa_mask); - new_thread->act[i - 1].sa_handler = _thread_run->act[i - 1].sa_handler; - new_thread->act[i - 1].sa_flags = _thread_run->act[i - 1].sa_flags; - } - /* Initialise the jump buffer: */ - _thread_sys_setjmp(new_thread->saved_jmp_buf); + setjmp(new_thread->saved_jmp_buf); /* * Set up new stack frame so that it looks like it * returned from a longjmp() to the beginning of - * _thread_start(). Check if this is a user thread: + * _thread_start(). */ - if (parent == NULL) { - /* Use the user start function: */ #if defined(__FreeBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start; - new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0; - new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0; + new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start; #else - new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0]._jb[0] = (long) _thread_start; #endif #elif defined(__NetBSD__) #if defined(__alpha__) - new_thread->saved_jmp_buf[2] = (long) _thread_start; - new_thread->saved_jmp_buf[4 + R_RA] = 0; - new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start; + new_thread->saved_jmp_buf[2] = (long) _thread_start; + new_thread->saved_jmp_buf[4 + R_RA] = 0; + new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start; #else - new_thread->saved_jmp_buf[0] = (long) _thread_start; + new_thread->saved_jmp_buf[0] = (long) _thread_start; #endif #else #error "Don't recognize this operating system!" #endif - } else { - /* - * Use the (funny) signal handler start - * function: - */ -#if defined(__FreeBSD__) -#if defined(__alpha__) - new_thread->saved_jmp_buf[0]._jb[2] = (long) _thread_start_sig_handler; - new_thread->saved_jmp_buf[0]._jb[4 + R_RA] = 0; - new_thread->saved_jmp_buf[0]._jb[4 + R_T12] = (long) _thread_start_sig_handler; -#else - new_thread->saved_jmp_buf[0]._jb[0] = (int) _thread_start_sig_handler; -#endif -#elif defined(__NetBSD__) -#if defined(__alpha__) - new_thread->saved_jmp_buf[2] = (long) _thread_start_sig_handler; - new_thread->saved_jmp_buf[4 + R_RA] = 0; - new_thread->saved_jmp_buf[4 + R_T12] = (long) _thread_start_sig_handler; -#else - new_thread->saved_jmp_buf[0] = (long) _thread_start_sig_handler; -#endif -#else -#error "Don't recognize this operating system!" -#endif - } /* The stack starts high and builds down: */ #if defined(__FreeBSD__) @@ -215,57 +180,26 @@ _thread_create(pthread_t * thread, const pthread_attr_t * attr, new_thread->cleanup = NULL; new_thread->queue = NULL; new_thread->qnxt = NULL; - new_thread->parent_thread = parent; new_thread->flags = 0; + /* Lock the thread list: */ + _lock_thread_list(); + /* Add the thread to the linked list of all threads: */ new_thread->nxt = _thread_link_list; _thread_link_list = new_thread; + /* Unlock the thread list: */ + _unlock_thread_list(); + /* Return a pointer to the thread structure: */ - if(thread) - (*thread) = new_thread; + (*thread) = new_thread; - /* Check if a parent thread was specified: */ - if (parent != NULL) { - /* - * A parent thread was specified, so this is - * a signal handler thread which must now - * wait for the signal handler to complete: - */ - PTHREAD_NEW_STATE(parent,PS_SIGTHREAD); - } else { - /* Schedule the new user thread: */ - _thread_kern_sched(NULL); - } + /* Schedule the new user thread: */ + _thread_kern_sched(NULL); } } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - - /* Return the status: */ - return (ret); -} - -int -pthread_create(pthread_t * thread, const pthread_attr_t * attr, - void *(*start_routine) (void *), void *arg) -{ - int ret = 0; - - /* - * Locking functions in libc are required when there are - * threads other than the initial thread. - */ - __isthreaded = 1; - - /* - * Call the low level thread creation function which allows a parent - * thread to be specified: - */ - ret = _thread_create(thread, attr, start_routine, arg, NULL); - /* Return the status: */ return (ret); } @@ -279,33 +213,4 @@ _thread_start(void) /* This point should never be reached. */ PANIC("Thread has resumed after exit"); } - -void -_thread_start_sig_handler(void) -{ - int sig; - long arg; - void (*sig_routine) (int); - - /* - * Cast the argument from 'void *' to a variable that is NO SMALLER - * than a pointer (otherwise gcc under NetBSD/Alpha will complain): - */ - arg = (long) _thread_run->arg; - - /* Cast the argument as a signal number: */ - sig = (int) arg; - - /* Cast a pointer to the signal handler function: */ - sig_routine = (void (*) (int)) _thread_run->start_routine; - - /* Call the signal handler function: */ - (*sig_routine) (sig); - - /* Exit the signal handler thread: */ - pthread_exit(&arg); - - /* This point should never be reached. */ - PANIC("Signal handler thread has resumed after exit"); -} #endif diff --git a/lib/libkse/thread/thr_detach.c b/lib/libkse/thread/thr_detach.c index 57c073a..da456bf 100644 --- a/lib/libkse/thread/thr_detach.c +++ b/lib/libkse/thread/thr_detach.c @@ -42,14 +42,11 @@ pthread_detach(pthread_t pthread) int status; pthread_t next_thread; - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Check for invalid calling parameters: */ - if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) { + if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) /* Return an invalid argument error: */ rval = EINVAL; - } + /* Check if the thread has not been detached: */ else if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { /* Flag the thread as detached: */ @@ -60,13 +57,9 @@ pthread_detach(pthread_t pthread) /* Make the thread run: */ PTHREAD_NEW_STATE(next_thread,PS_RUNNING); } - } else { + } else /* Return an error: */ rval = EINVAL; - } - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); /* Return the completion status: */ return (rval); diff --git a/lib/libkse/thread/thr_exit.c b/lib/libkse/thread/thr_exit.c index 15bcfa3..4bc7da9 100644 --- a/lib/libkse/thread/thr_exit.c +++ b/lib/libkse/thread/thr_exit.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -102,9 +102,6 @@ pthread_exit(void *status) long l; pthread_t pthread; - /* Block signals: */ - _thread_kern_sig_block(NULL); - /* Save the return value: */ _thread_run->ret = status; @@ -126,6 +123,9 @@ pthread_exit(void *status) PTHREAD_NEW_STATE(pthread,PS_RUNNING); } + /* Lock the thread list: */ + _lock_thread_list(); + /* Check if the running thread is at the head of the linked list: */ if (_thread_link_list == _thread_run) { /* There is no previous thread: */ @@ -153,39 +153,12 @@ pthread_exit(void *status) } } - /* Check if this is a signal handler thread: */ - if (_thread_run->parent_thread != NULL) { - /* - * Enter a loop to search for other threads with the same - * parent: - */ - for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { - /* Compare the parent thread pointers: */ - if (pthread->parent_thread == _thread_run->parent_thread) { - /* - * The parent thread is waiting on at least - * one other signal handler. Exit the loop - * now that this is known. - */ - break; - } - } + /* Unlock the thread list: */ + _unlock_thread_list(); - /* - * Check if the parent is not waiting on any other signal - * handler threads and if it hasn't died in the meantime: - */ - if (pthread == NULL && _thread_run->parent_thread->state != PS_DEAD) { - /* Allow the parent thread to run again: */ - PTHREAD_NEW_STATE(_thread_run->parent_thread,PS_RUNNING); - } - /* Get the signal number: */ - l = (long) _thread_run->arg; - sig = (int) l; + /* Lock the dead thread list: */ + _lock_dead_thread_list(); - /* Unblock the signal from the parent thread: */ - sigdelset(&_thread_run->parent_thread->sigmask, sig); - } /* * This thread will never run again. Add it to the list of dead * threads: @@ -193,6 +166,9 @@ pthread_exit(void *status) _thread_run->nxt = _thread_dead; _thread_dead = _thread_run; + /* Unlock the dead thread list: */ + _unlock_dead_thread_list(); + /* * The running thread is no longer in the thread link list so it will * now die: diff --git a/lib/libkse/thread/thr_fcntl.c b/lib/libkse/thread/thr_fcntl.c index f83ee50..9a50c1c 100644 --- a/lib/libkse/thread/thr_fcntl.c +++ b/lib/libkse/thread/thr_fcntl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,9 +46,6 @@ fcntl(int fd, int cmd,...) int status; va_list ap; - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Lock the file descriptor: */ if ((ret = _thread_fd_lock(fd, FD_RDWR, NULL, __FILE__, __LINE__)) == 0) { /* Initialise the variable argument list: */ @@ -56,7 +53,7 @@ fcntl(int fd, int cmd,...) /* Process according to file control command type: */ switch (cmd) { - /* Duplicate a file descriptor: */ + /* Duplicate a file descriptor: */ case F_DUPFD: /* * Get the file descriptor that the caller wants to @@ -107,8 +104,6 @@ fcntl(int fd, int cmd,...) /* Unlock the file descriptor: */ _thread_fd_unlock(fd, FD_RDWR); } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); /* Return the completion status: */ return (ret); diff --git a/lib/libkse/thread/thr_find_thread.c b/lib/libkse/thread/thr_find_thread.c new file mode 100644 index 0000000..99e3023 --- /dev/null +++ b/lib/libkse/thread/thr_find_thread.c @@ -0,0 +1,97 @@ +/* + * Copyright (c) 1998 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> +#ifdef _THREAD_SAFE +#include <pthread.h> +#include "pthread_private.h" + +/* Find a thread in the linked list of active threads: */ +int +_find_thread(pthread_t pthread) +{ + pthread_t pthread1; + + /* Check if the caller has specified an invalid thread: */ + if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) + /* Invalid thread: */ + return(EINVAL); + + /* Lock the thread list: */ + _lock_thread_list(); + + /* Point to the first thread in the list: */ + pthread1 = _thread_link_list; + + /* Search for the thread to join to: */ + while (pthread1 != NULL && pthread1 != pthread) { + /* Point to the next thread: */ + pthread1 = pthread1->nxt; + } + + /* Unlock the thread list: */ + _unlock_thread_list(); + + /* Return zero if the thread exists: */ + return ((pthread1 != NULL) ? 0:ESRCH); +} + +/* Find a thread in the linked list of dead threads: */ +int +_find_dead_thread(pthread_t pthread) +{ + pthread_t pthread1; + + /* Check if the caller has specified an invalid thread: */ + if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) + /* Invalid thread: */ + return(EINVAL); + + /* Lock the dead thread list: */ + _lock_dead_thread_list(); + + /* Point to the first thread in the list: */ + pthread1 = _thread_dead; + + /* Search for the thread to join to: */ + while (pthread1 != NULL && pthread1 != pthread) { + /* Point to the next thread: */ + pthread1 = pthread1->nxt; + } + + /* Unlock the dead thread list: */ + _unlock_dead_thread_list(); + + /* Return zero if the thread exists: */ + return ((pthread1 != NULL) ? 0:ESRCH); +} +#endif diff --git a/lib/libkse/thread/thr_fork.c b/lib/libkse/thread/thr_fork.c index 29d6090..25d6703 100644 --- a/lib/libkse/thread/thr_fork.c +++ b/lib/libkse/thread/thr_fork.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,8 +47,8 @@ fork(void) pthread_t pthread; pthread_t pthread_next; - /* Block signals to avoid being interrupted at a bad time: */ - _thread_kern_sig_block(&status); + /* Lock the thread list: */ + _lock_thread_list(); /* Fork a new process: */ if ((ret = _thread_sys_fork()) <= 0) { @@ -59,12 +59,12 @@ fork(void) _thread_sys_close(_thread_kern_pipe[1]); /* Reset signals pending for the running thread: */ - memset(_thread_run->sigpend, 0, sizeof(_thread_run->sigpend)); + _thread_run->sigpend = 0; /* * Create a pipe that is written to by the signal handler to * prevent signals being missed in calls to - * _thread_sys_select: + * _select: */ if (_thread_sys_pipe(_thread_kern_pipe) != 0) { /* Cannot create pipe, so abort: */ @@ -122,8 +122,8 @@ fork(void) } } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unock the thread list: */ + _unlock_thread_list(); /* Return the process ID: */ return (ret); diff --git a/lib/libkse/thread/thr_fsync.c b/lib/libkse/thread/thr_fsync.c index 51078e5..5658c73 100644 --- a/lib/libkse/thread/thr_fsync.c +++ b/lib/libkse/thread/thr_fsync.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/lib/libkse/thread/thr_getprio.c b/lib/libkse/thread/thr_getprio.c index 85bd261..708b8f1 100644 --- a/lib/libkse/thread/thr_getprio.c +++ b/lib/libkse/thread/thr_getprio.c @@ -38,36 +38,19 @@ int pthread_getprio(pthread_t pthread) { - int rval = 0; - int status; - pthread_t pthread_p; + int ret; - /* Block signals: */ - _thread_kern_sig_block(&status); - - /* Point to the first thread in the list: */ - pthread_p = _thread_link_list; - - /* Enter a loop to search for the thread: */ - while (pthread_p != NULL && pthread_p != pthread) { - /* Point to the next thread: */ - pthread_p = pthread_p->nxt; - } - - /* Check if the thread pointer is NULL: */ - if (pthread == NULL || pthread_p == NULL) { - /* Return an invalid argument error: */ - errno = EINVAL; - rval = -1; - } else { + /* Find the thread in the list of active threads: */ + if ((ret = _find_thread(pthread)) == 0) /* Get the thread priority: */ - rval = pthread->pthread_priority; + ret = pthread->pthread_priority; + else { + /* Invalid thread: */ + errno = ret; + ret = -1; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - /* Return the thread priority or an error status: */ - return (rval); + return (ret); } #endif diff --git a/lib/libkse/thread/thr_info.c b/lib/libkse/thread/thr_info.c index 2909989..c2c3426 100644 --- a/lib/libkse/thread/thr_info.c +++ b/lib/libkse/thread/thr_info.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c index 78967f4..c267a2c 100644 --- a/lib/libkse/thread/thr_init.c +++ b/lib/libkse/thread/thr_init.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -44,7 +44,6 @@ #include <machine/reg.h> #include <pthread.h> #include "pthread_private.h" -extern int _thread_autoinit_dummy_decl; #ifdef GCC_2_8_MADE_THREAD_AWARE typedef void *** (*dynamic_handler_allocator)(); @@ -80,8 +79,6 @@ _thread_init(void) int flags; int i; struct sigaction act; - /* Ensure that the auto-initialization routine is linked in: */ - _thread_autoinit_dummy_decl = 1; /* Check if this function has already been called: */ if (_thread_initial) @@ -96,7 +93,7 @@ _thread_init(void) /* * Create a pipe that is written to by the signal handler to prevent - * signals being missed in calls to _thread_sys_select: + * signals being missed in calls to _select: */ if (_thread_sys_pipe(_thread_kern_pipe) != 0) { /* Cannot create pipe, so abort: */ @@ -144,7 +141,6 @@ _thread_init(void) _thread_queue_init(&(_thread_initial->join_queue)); /* Initialise the rest of the fields: */ - _thread_initial->parent_thread = NULL; _thread_initial->specific_data = NULL; _thread_initial->cleanup = NULL; _thread_initial->queue = NULL; @@ -155,49 +151,42 @@ _thread_init(void) _thread_link_list = _thread_initial; _thread_run = _thread_initial; - /* Enter a loop to get the existing signal status: */ - for (i = 1; i < NSIG; i++) { - /* Check for signals which cannot be trapped: */ - if (i == SIGKILL || i == SIGSTOP) { - } - /* Get the signal handler details: */ - else if (_thread_sys_sigaction(i, NULL, &act) != 0) { - /* - * Abort this process if signal - * initialisation fails: - */ - PANIC("Cannot read signal handler info"); - } - /* Set the signal handler for the initial thread: */ - else if (sigaction(i, &act, NULL) != 0) { - /* - * Abort this process if signal - * initialisation fails: - */ - PANIC("Cannot initialise signal handler for initial thread"); - } - } - /* Initialise the global signal action structure: */ sigfillset(&act.sa_mask); act.sa_handler = (void (*) ()) _thread_sig_handler; act.sa_flags = SA_RESTART; - /* Enter a loop to initialise the rest of the signals: */ + /* Enter a loop to get the existing signal status: */ for (i = 1; i < NSIG; i++) { /* Check for signals which cannot be trapped: */ if (i == SIGKILL || i == SIGSTOP) { } - /* Initialise the signal for default handling: */ - else if (_thread_sys_sigaction(i, &act, NULL) != 0) { + + /* Get the signal handler details: */ + else if (_thread_sys_sigaction(i, NULL, + &_thread_sigact[i - 1]) != 0) { /* * Abort this process if signal * initialisation fails: */ - PANIC("Cannot initialise signal handler"); + PANIC("Cannot read signal handler info"); } } + /* + * Install the signal handler for the most important + * signals that the user-thread kernel needs. Actually + * SIGINFO isn't really needed, but it is nice to have. + */ + if (_thread_sys_sigaction(SIGVTALRM, &act, NULL) != 0 || + _thread_sys_sigaction(SIGINFO , &act, NULL) != 0 || + _thread_sys_sigaction(SIGCHLD , &act, NULL) != 0) { + /* + * Abort this process if signal initialisation fails: + */ + PANIC("Cannot initialise signal handler"); + } + /* Get the table size: */ if ((_thread_dtablesize = getdtablesize()) < 0) { /* diff --git a/lib/libkse/thread/thr_join.c b/lib/libkse/thread/thr_join.c index 9e86a01..83b0c2a 100644 --- a/lib/libkse/thread/thr_join.c +++ b/lib/libkse/thread/thr_join.c @@ -38,9 +38,8 @@ int pthread_join(pthread_t pthread, void **thread_return) { - int rval = 0; - int status; - pthread_t pthread1; + int ret = 0; + pthread_t pthread1 = NULL; /* Check if the caller has specified an invalid thread: */ if (pthread == NULL || pthread->magic != PTHREAD_MAGIC) @@ -52,38 +51,23 @@ pthread_join(pthread_t pthread, void **thread_return) /* Avoid a deadlock condition: */ return(EDEADLK); - /* Block signals: */ - _thread_kern_sig_block(&status); + /* + * Find the thread in the list of active threads or in the + * list of dead threads: + */ + if (_find_thread(pthread) == 0 || + _find_dead_thread(pthread) == 0) + pthread1 = pthread; - /* Point to the first thread in the list: */ - pthread1 = _thread_link_list; - - /* Search for the thread to join to: */ - while (pthread1 != NULL && pthread1 != pthread) { - /* Point to the next thread: */ - pthread1 = pthread1->nxt; - } - - if (pthread1 == NULL) { - /* Point to the first thread in the dead thread list: */ - pthread1 = _thread_dead; - - /* Search for the thread to join to: */ - while (pthread1 != NULL && pthread1 != pthread) { - /* Point to the next thread: */ - pthread1 = pthread1->nxt; - } - } - - if (pthread1 == NULL) { + if (pthread1 == NULL) /* Return an error: */ - rval = ESRCH; + ret = ESRCH; /* Check if this thread has been detached: */ - } else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { + else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) /* Return an error: */ - rval = ESRCH; - } + ret = ESRCH; + /* Check if the thread is not dead: */ else if (pthread->state != PS_DEAD) { /* Add the running thread to the join queue: */ @@ -92,32 +76,22 @@ pthread_join(pthread_t pthread, void **thread_return) /* Schedule the next thread: */ _thread_kern_sched_state(PS_JOIN, __FILE__, __LINE__); - /* Block signals again: */ - _thread_kern_sig_block(NULL); - /* Check if the thread is not detached: */ - if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) { + if ((pthread->attr.flags & PTHREAD_DETACHED) == 0) /* Check if the return value is required: */ - if (thread_return) { + if (thread_return) /* Return the thread's return value: */ *thread_return = pthread->ret; - } - } else { + else /* Return an error: */ - rval = ESRCH; - } - } else { - /* Check if the return value is required: */ - if (thread_return != NULL) { - /* Return the thread's return value: */ - *thread_return = pthread->ret; - } - } + ret = ESRCH; - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Check if the return value is required: */ + } else if (thread_return != NULL) + /* Return the thread's return value: */ + *thread_return = pthread->ret; /* Return the completion status: */ - return (rval); + return (ret); } #endif diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c index 3b62422..36efc31 100644 --- a/lib/libkse/thread/thr_kern.c +++ b/lib/libkse/thread/thr_kern.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_kern.c,v 1.8 1998/04/11 07:47:22 jb Exp $ + * $Id: uthread_kern.c,v 1.9 1998/04/17 09:37:41 jb Exp $ * */ #include <errno.h> @@ -49,15 +49,9 @@ #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) @@ -78,8 +72,12 @@ _thread_kern_sched(struct sigcontext * scp) struct timeval tv; struct timeval tv1; - /* Block signals: */ - _thread_kern_sig_block(NULL); + /* + * Flag the pthread kernel as executing scheduler code + * to avoid a scheduler signal from interrupting this + * execution and calling the scheduler again. + */ + _thread_kern_in_sched = 1; /* Check if this function was called from the signal handler: */ if (scp != NULL) { @@ -101,14 +99,20 @@ __asm__("fnsave %0": :"m"(*fdata)); _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); - + else if (setjmp(_thread_run->saved_jmp_buf) != 0) { /* * This point is reached when a longjmp() is called to * restore the state of a thread. + * + * This is the normal way out of the scheduler. */ + _thread_kern_in_sched = 0; + + /* + * There might be pending signals for this thread, so + * dispatch any that aren't blocked: + */ + _dispatch_signals(); return; } else { /* Flag the jump buffer was the last state saved: */ @@ -135,10 +139,9 @@ __asm__("fnsave %0": :"m"(*fdata)); pthread_prv = pthread; } /* - * Check if this thread has detached or if it is a signal - * handler thread: + * Check if this thread has detached: */ - else if (((pthread->attr.flags & PTHREAD_DETACHED) != 0) || pthread->parent_thread != NULL) { + else if ((pthread->attr.flags & PTHREAD_DETACHED) != 0) { /* Check if there is no previous dead thread: */ if (pthread_prv == NULL) { /* @@ -210,41 +213,10 @@ __asm__("fnsave %0": :"m"(*fdata)); _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: + * Enter a loop to look for sleeping threads that are ready: */ - 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; - } - } - + 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 || @@ -369,45 +341,6 @@ __asm__("fnsave %0": :"m"(*fdata)); * priority that is ready to run: */ for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { - /* Check if in single-threaded mode: */ - if (_thread_single != NULL) { - /* - * Check if the current thread is - * the thread for which single-threaded - * mode is enabled: - */ - if (pthread == _thread_single) { - /* - * This thread is allowed - * to run. - */ - } else { - /* - * Walk up the signal handler - * parent thread tree to see - * if the current thread is - * descended from the thread - * for which single-threaded - * mode is enabled. - */ - pthread_nxt = pthread; - while(pthread_nxt != NULL && - pthread_nxt != _thread_single) { - pthread_nxt = pthread->parent_thread; - } - /* - * Check if the current - * thread is not descended - * from the thread for which - * single-threaded mode is - * enabled. - */ - if (pthread_nxt == NULL) - /* Ignore this thread. */ - continue; - } - } - /* Check if the current thread is unable to run: */ if (pthread->state != PS_RUNNING) { } @@ -433,49 +366,11 @@ __asm__("fnsave %0": :"m"(*fdata)); * least recently. */ for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { - /* Check if in single-threaded mode: */ - if (_thread_single != NULL) { - /* - * Check if the current thread is - * the thread for which single-threaded - * mode is enabled: - */ - if (pthread == _thread_single) { - /* - * This thread is allowed - * to run. - */ - } else { - /* - * Walk up the signal handler - * parent thread tree to see - * if the current thread is - * descended from the thread - * for which single-threaded - * mode is enabled. - */ - pthread_nxt = pthread; - while(pthread_nxt != NULL && - pthread_nxt != _thread_single) { - pthread_nxt = pthread->parent_thread; - } - /* - * Check if the current - * thread is not descended - * from the thread for which - * single-threaded mode is - * enabled. - */ - if (pthread_nxt == NULL) - /* Ignore this thread. */ - continue; - } - } - /* 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 @@ -487,6 +382,7 @@ __asm__("fnsave %0": :"m"(*fdata)); * priority. */ } + /* * Check if the current thread reached its time slice * allocation last time it ran (or if it has not run @@ -494,6 +390,7 @@ __asm__("fnsave %0": :"m"(*fdata)); */ 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 @@ -526,45 +423,6 @@ __asm__("fnsave %0": :"m"(*fdata)); * priority. 3. Became inactive least recently. */ for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { - /* Check if in single-threaded mode: */ - if (_thread_single != NULL) { - /* - * Check if the current thread is - * the thread for which single-threaded - * mode is enabled: - */ - if (pthread == _thread_single) { - /* - * This thread is allowed - * to run. - */ - } else { - /* - * Walk up the signal handler - * parent thread tree to see - * if the current thread is - * descended from the thread - * for which single-threaded - * mode is enabled. - */ - pthread_nxt = pthread; - while(pthread_nxt != NULL && - pthread_nxt != _thread_single) { - pthread_nxt = pthread->parent_thread; - } - /* - * Check if the current - * thread is not descended - * from the thread for which - * single-threaded mode is - * enabled. - */ - if (pthread_nxt == NULL) - /* Ignore this thread. */ - continue; - } - } - /* * Check if the current thread is unable to * run: @@ -804,14 +662,13 @@ __asm__("fnsave %0": :"m"(*fdata)); * was interrupted by a signal: */ _thread_sys_sigreturn(&_thread_run->saved_sigcontext); - } else { + } 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); - } + longjmp(_thread_run->saved_jmp_buf, 1); /* This point should not be reached. */ PANIC("Thread has returned from sigreturn or longjmp"); @@ -822,243 +679,6 @@ __asm__("fnsave %0": :"m"(*fdata)); exit(0); } -static void -_thread_signal(pthread_t pthread, int sig) -{ - int done; - long l; - pthread_t new_pthread; - struct sigaction act; - void *arg; - - /* - * Assume that the signal will not be dealt with according - * to the thread state: - */ - done = 0; - - /* 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_FILE_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_NEW_STATE(pthread,PS_RUNNING); - } else { - /* Return the 'interrupted' error: */ - _thread_seterrno(pthread, EINTR); - - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - } - pthread->interrupted = 1; - break; - - /* Waiting on I/O for zero or more file descriptors: */ - case PS_SELECT_WAIT: - pthread->data.select_data->nfds = -1; - - /* Return the 'interrupted' error: */ - _thread_seterrno(pthread, EINTR); - pthread->interrupted = 1; - - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread,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_SLEEP_WAIT: - case PS_SIGWAIT: - /* Return the 'interrupted' error: */ - _thread_seterrno(pthread, EINTR); - pthread->interrupted = 1; - - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - - /* Return the signal number: */ - pthread->signo = sig; - break; - } - - /* - * Check if this signal has been dealt with, or is being - * ignored: - */ - if (done || 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; - - /* - * The following signals should terminate the - * process. Do this by clearing the signal action - * and then re-throwing the signal. - */ - case SIGHUP: - case SIGINT: - case SIGPIPE: - case SIGALRM: - case SIGTERM: - case SIGXCPU: - case SIGXFSZ: - case SIGVTALRM: - case SIGUSR1: - case SIGUSR2: - /* These signals stop the process. Also re-throw them. */ - case SIGTSTP: - case SIGTTIN: - case SIGTTOU: - /* Clear the signal action: */ - sigfillset(&act.sa_mask); - act.sa_handler = SIG_DFL; - act.sa_flags = SA_RESTART; - _thread_sys_sigaction(sig, &act, NULL); - /* Re-throw to ourselves. */ - kill(getpid(), sig); - break; - - case SIGCONT: - /* - * If we get this it means that we were - * probably stopped and then continued. - * Reset the handler for the SIGTSTP, SIGTTIN - * and SIGTTOU signals. - */ - - sigfillset(&act.sa_mask); - act.sa_handler = (void (*) ()) _thread_sig_handler; - act.sa_flags = SA_RESTART; - - /* Initialise the signals for default handling: */ - if (_thread_sys_sigaction(SIGTSTP, &act, NULL) != 0) { - PANIC("Cannot initialise SIGTSTP signal handler"); - } - if (_thread_sys_sigaction(SIGTTIN, &act, NULL) != 0) { - PANIC("Cannot initialise SIGTTIN signal handler"); - } - if (_thread_sys_sigaction(SIGTTOU, &act, NULL) != 0) { - PANIC("Cannot initialise SIGTTOU signal handler"); - } - 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) { @@ -1382,18 +1002,12 @@ _thread_kern_select(int wait_reqd) */ _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; @@ -1426,10 +1040,10 @@ _thread_kern_select(int wait_reqd) * 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 + * used to cause the _select call * to complete if the signal occurred between * the time when signals were unblocked and - * the _thread_sys_select select call being + * the _select select call being * made. */ } @@ -1439,36 +1053,22 @@ _thread_kern_select(int wait_reqd) 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: + * that the call to _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: + * Check if any file descriptors are ready: */ - 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 { + if (count > 0) { /* * Enter a loop to look for threads waiting on file * descriptors that are flagged as available by the - * _thread_sys_select syscall: + * _select syscall: */ for (pthread = _thread_link_list; pthread != NULL; pthread = pthread->nxt) { /* Process according to thread state: */ diff --git a/lib/libkse/thread/thr_kill.c b/lib/libkse/thread/thr_kill.c index eb2c6b7..f6e684b 100644 --- a/lib/libkse/thread/thr_kill.c +++ b/lib/libkse/thread/thr_kill.c @@ -39,40 +39,19 @@ int pthread_kill(pthread_t pthread, int sig) { - int rval = 0; - int status; - pthread_t p_pthread; + int ret; /* Check for invalid signal numbers: */ if (sig < 0 || sig >= NSIG) /* Invalid signal: */ - rval = EINVAL; - else { - /* Assume that the search will succeed: */ - rval = 0; + ret = EINVAL; - /* Block signals: */ - _thread_kern_sig_block(&status); - - /* Search for the thread: */ - p_pthread = _thread_link_list; - while (p_pthread != NULL && p_pthread != pthread) { - p_pthread = p_pthread->nxt; - } - - /* Check if the thread was not found: */ - if (p_pthread == NULL) - /* Can't find the thread: */ - rval = ESRCH; - else - /* Increment the pending signal count: */ - p_pthread->sigpend[sig] += 1; - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - } + /* Find the thread in the list of active threads: */ + else if ((ret = _find_thread(pthread)) == 0) + /* Increment the pending signal count: */ + sigaddset(&pthread->sigpend,sig); /* Return the completion status: */ - return (rval); + return (ret); } #endif diff --git a/lib/libkse/thread/thr_mutex.c b/lib/libkse/thread/thr_mutex.c index 9f92de9..fb737cf 100644 --- a/lib/libkse/thread/thr_mutex.c +++ b/lib/libkse/thread/thr_mutex.c @@ -49,28 +49,26 @@ pthread_mutex_init(pthread_mutex_t * mutex, ret = EINVAL; } else { /* Check if default mutex attributes: */ - if (mutex_attr == NULL || *mutex_attr == NULL) { + if (mutex_attr == NULL || *mutex_attr == NULL) /* Default to a fast mutex: */ type = MUTEX_TYPE_FAST; - } else if ((*mutex_attr)->m_type >= MUTEX_TYPE_MAX) { + + else if ((*mutex_attr)->m_type >= MUTEX_TYPE_MAX) /* Return an invalid argument error: */ ret = EINVAL; - } else { + else /* Use the requested mutex type: */ type = (*mutex_attr)->m_type; - } /* Check no errors so far: */ if (ret == 0) { - if ((pmutex = (pthread_mutex_t) malloc(sizeof(struct pthread_mutex))) == NULL) { + if ((pmutex = (pthread_mutex_t) + malloc(sizeof(struct pthread_mutex))) == NULL) ret = ENOMEM; - } else { + else { /* Reset the mutex flags: */ pmutex->m_flags = 0; - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Process according to mutex type: */ switch (type) { /* Fast mutex: */ @@ -96,14 +94,12 @@ pthread_mutex_init(pthread_mutex_t * mutex, pmutex->m_flags |= MUTEX_FLAGS_INITED; pmutex->m_owner = NULL; pmutex->m_type = type; + pmutex->access_lock = 0; *mutex = pmutex; } else { free(pmutex); *mutex = NULL; } - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); } } } @@ -114,42 +110,25 @@ pthread_mutex_init(pthread_mutex_t * mutex, int pthread_mutex_destroy(pthread_mutex_t * mutex) { - int ret = 0; - int status; + int ret = 0; - if (mutex == NULL || *mutex == NULL) { + if (mutex == NULL || *mutex == NULL) ret = EINVAL; - } else { - /* Block signals: */ - _thread_kern_sig_block(&status); - - /* Process according to mutex type: */ - switch ((*mutex)->m_type) { - /* Fast mutex: */ - case MUTEX_TYPE_FAST: - /* Nothing to do here. */ - break; - - /* Counting mutex: */ - case MUTEX_TYPE_COUNTING_FAST: - /* Reset the mutex count: */ - (*mutex)->m_data.m_count = 0; - break; - - /* Trap undefined mutex types: */ - default: - /* Return an invalid argument error: */ - ret = EINVAL; - break; - } - - /* Clean up the mutex in case that others want to use it: */ - _thread_queue_init(&(*mutex)->m_queue); - (*mutex)->m_owner = NULL; - (*mutex)->m_flags = 0; - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + else { + /* Lock the mutex structure: */ + _spinlock(&(*mutex)->access_lock); + + /* + * Free the memory allocated for the mutex + * structure: + */ + free(*mutex); + + /* + * Leave the caller's pointer NULL now that + * the mutex has been destroyed: + */ + *mutex = NULL; } /* Return the completion status: */ @@ -160,7 +139,6 @@ int pthread_mutex_trylock(pthread_mutex_t * mutex) { int ret = 0; - int status; if (mutex == NULL) ret = EINVAL; @@ -171,8 +149,8 @@ pthread_mutex_trylock(pthread_mutex_t * mutex) */ else if (*mutex != NULL || (ret = pthread_mutex_init(mutex,NULL)) == 0) { - /* Block signals: */ - _thread_kern_sig_block(&status); + /* Lock the mutex structure: */ + _spinlock(&(*mutex)->access_lock); /* Process according to mutex type: */ switch ((*mutex)->m_type) { @@ -216,8 +194,8 @@ pthread_mutex_trylock(pthread_mutex_t * mutex) break; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the mutex structure: */ + _atomic_unlock(&(*mutex)->access_lock); } /* Return the completion status: */ @@ -228,7 +206,6 @@ int pthread_mutex_lock(pthread_mutex_t * mutex) { int ret = 0; - int status; if (mutex == NULL) ret = EINVAL; @@ -239,8 +216,8 @@ pthread_mutex_lock(pthread_mutex_t * mutex) */ else if (*mutex != NULL || (ret = pthread_mutex_init(mutex,NULL)) == 0) { - /* Block signals: */ - _thread_kern_sig_block(&status); + /* Lock the mutex structure: */ + _spinlock(&(*mutex)->access_lock); /* Process according to mutex type: */ switch ((*mutex)->m_type) { @@ -262,11 +239,14 @@ pthread_mutex_lock(pthread_mutex_t * mutex) */ _thread_queue_enq(&(*mutex)->m_queue, _thread_run); + /* Unlock the mutex structure: */ + _atomic_unlock(&(*mutex)->access_lock); + /* Block signals: */ _thread_kern_sched_state(PS_MUTEX_WAIT, __FILE__, __LINE__); - /* Block signals: */ - _thread_kern_sig_block(NULL); + /* Lock the mutex again: */ + _spinlock(&(*mutex)->access_lock); } } break; @@ -292,11 +272,14 @@ pthread_mutex_lock(pthread_mutex_t * mutex) */ _thread_queue_enq(&(*mutex)->m_queue, _thread_run); + /* Unlock the mutex structure: */ + _atomic_unlock(&(*mutex)->access_lock); + /* Block signals: */ _thread_kern_sched_state(PS_MUTEX_WAIT, __FILE__, __LINE__); - /* Block signals: */ - _thread_kern_sig_block(NULL); + /* Lock the mutex again: */ + _spinlock(&(*mutex)->access_lock); } } @@ -311,8 +294,8 @@ pthread_mutex_lock(pthread_mutex_t * mutex) break; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the mutex structure: */ + _atomic_unlock(&(*mutex)->access_lock); } /* Return the completion status: */ @@ -323,13 +306,12 @@ int pthread_mutex_unlock(pthread_mutex_t * mutex) { int ret = 0; - int status; if (mutex == NULL || *mutex == NULL) { ret = EINVAL; } else { - /* Block signals: */ - _thread_kern_sig_block(&status); + /* Lock the mutex structure: */ + _spinlock(&(*mutex)->access_lock); /* Process according to mutex type: */ switch ((*mutex)->m_type) { @@ -379,8 +361,8 @@ pthread_mutex_unlock(pthread_mutex_t * mutex) break; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the mutex structure: */ + _atomic_unlock(&(*mutex)->access_lock); } /* Return the completion status: */ diff --git a/lib/libkse/thread/thr_nanosleep.c b/lib/libkse/thread/thr_nanosleep.c index 39c7dad..4ec7d3f 100644 --- a/lib/libkse/thread/thr_nanosleep.c +++ b/lib/libkse/thread/thr_nanosleep.c @@ -66,6 +66,7 @@ nanosleep(const struct timespec * time_to_sleep, _thread_run->wakeup_time.tv_sec += 1; _thread_run->wakeup_time.tv_nsec -= 1000000000; } + _thread_run->interrupted = 0; /* Reschedule the current thread to sleep: */ _thread_kern_sched_state(PS_SLEEP_WAIT, __FILE__, __LINE__); @@ -106,8 +107,8 @@ nanosleep(const struct timespec * time_to_sleep, time_remaining->tv_nsec = remaining_time.tv_nsec; } - /* Check if the entire sleep was not completed: */ - if (remaining_time.tv_nsec != 0 || remaining_time.tv_sec != 0) { + /* Check if the sleep was interrupted: */ + if (_thread_run->interrupted) { /* Return an EINTR error : */ errno = EINTR; ret = -1; diff --git a/lib/libkse/thread/thr_open.c b/lib/libkse/thread/thr_open.c index ef7a504..a538a75 100644 --- a/lib/libkse/thread/thr_open.c +++ b/lib/libkse/thread/thr_open.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: uthread_open.c,v 1.3 1997/05/03 03:57:21 jb Exp $ * */ #include <stdarg.h> @@ -49,9 +49,6 @@ open(const char *path, int flags,...) int status; va_list ap; - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Check if the file is being created: */ if (flags & O_CREAT) { /* Get the creation mode: */ @@ -71,9 +68,6 @@ open(const char *path, int flags,...) fd = -1; } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - /* Return the file descriptor or -1 on error: */ return (fd); } diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h index 5ceeaba..5f0f3a8 100644 --- a/lib/libkse/thread/thr_private.h +++ b/lib/libkse/thread/thr_private.h @@ -60,6 +60,10 @@ */ #define PANIC(string) _thread_exit(__FILE__,__LINE__,string) +/* Output debug messages like this: */ +#define stdout_debug(_x) _write(1,_x,strlen(_x)); +#define stderr_debug(_x) _write(2,_x,strlen(_x)); + /* * State change macro: */ @@ -97,6 +101,11 @@ struct pthread_mutex { struct pthread *m_owner; union pthread_mutex_data m_data; long m_flags; + + /* + * Lock for accesses to this structure. + */ + long access_lock; }; /* @@ -131,6 +140,11 @@ struct pthread_cond { struct pthread_queue c_queue; void *c_data; long c_flags; + + /* + * Lock for accesses to this structure. + */ + long access_lock; }; struct pthread_cond_attr { @@ -204,6 +218,7 @@ struct pthread_attr { struct pthread_key { pthread_mutex_t mutex; + long access_lock; long count; void (*destructor) (); }; @@ -243,6 +258,13 @@ enum pthread_state { * File descriptor table structure. */ struct fd_table_entry { + /* + * Lock for accesses to this file descriptor table + * entry. This is passed to _spinlock() to provide atomic + * access to this structure. It does *not* represent the + * state of the lock on the file descriptor. + */ + long access_lock; struct pthread_queue r_queue; /* Read queue. */ struct pthread_queue w_queue; /* Write queue. */ struct pthread *r_owner; /* Ptr to thread owning read lock. */ @@ -288,6 +310,11 @@ struct pthread { char *name; /* + * Lock for accesses to this thread structure. + */ + long access_lock; + + /* * Pointer to the next thread in the thread linked list. */ struct pthread *nxt; @@ -301,13 +328,6 @@ struct pthread { void *stack; struct pthread_attr attr; - /* - * Thread-specific signal handler interface: - * - * Array of signal actions for this thread. - */ - struct sigaction act[NSIG]; - #if (defined(__FreeBSD__) || defined(__NetBSD__)) && defined(__i386__) /* * Saved floating point registers on systems where they are not @@ -335,17 +355,10 @@ struct pthread { int sig_saved; /* - * Current signal mask and array of pending signals. + * Current signal mask and pending signals. */ sigset_t sigmask; - int sigpend[NSIG]; - - /* - * Pointer to the parent thread for which the current thread is - * a signal handler thread, otherwise NULL if the current thread - * is not a signal handler thread. - */ - struct pthread *parent_thread; + sigset_t sigpend; /* Thread state: */ enum pthread_state state; @@ -463,7 +476,7 @@ SCLASS struct pthread * volatile _thread_link_list /* * Array of kernel pipe file descriptors that are used to ensure that - * no signals are missed in calls to _thread_sys_select. + * no signals are missed in calls to _select. */ SCLASS int _thread_kern_pipe[2] #ifdef GLOBAL_PTHREAD_PRIVATE @@ -480,6 +493,12 @@ SCLASS int _thread_kern_in_select #else ; #endif +SCLASS int _thread_kern_in_sched +#ifdef GLOBAL_PTHREAD_PRIVATE += 0; +#else +; +#endif /* Last time that an incremental priority update was performed: */ SCLASS struct timeval kern_inc_prio_time @@ -558,6 +577,11 @@ SCLASS int _thread_dtablesize /* Descriptor table size. */ ; #endif +/* + * Array of signal actions for this process. + */ +struct sigaction _thread_sigact[NSIG]; + /* Undefine the storage class specifier: */ #undef SCLASS @@ -568,8 +592,18 @@ __BEGIN_DECLS char *__ttyname_basic(int); char *__ttyname_r_basic(int, char *, size_t); char *ttyname_r(int, char *, size_t); +int _find_dead_thread(pthread_t); +int _find_thread(pthread_t); int _thread_create(pthread_t *,const pthread_attr_t *,void *(*start_routine)(void *),void *,pthread_t); int _thread_fd_lock(int, int, struct timespec *,char *fname,int lineno); +void _dispatch_signals(void); +void _thread_signal(pthread_t, int); +void _lock_dead_thread_list(void); +void _lock_thread(void); +void _lock_thread_list(void); +void _unlock_dead_thread_list(void); +void _unlock_thread(void); +void _unlock_thread_list(void); void _thread_exit(char *, int, char *); void _thread_fd_unlock(int, int); void *_thread_cleanup(pthread_t); @@ -579,8 +613,6 @@ void _thread_init(void); void _thread_kern_sched(struct sigcontext *); void _thread_kern_sched_state(enum pthread_state,char *fname,int lineno); void _thread_kern_set_timeout(struct timespec *); -void _thread_kern_sig_block(int *); -void _thread_kern_sig_unblock(int); void _thread_sig_handler(int, int, struct sigcontext *); void _thread_start(void); void _thread_start_sig_handler(void); @@ -597,11 +629,9 @@ int _thread_sys_sigaction(int, const struct sigaction *, struct sigaction *) int _thread_sys_sigpending(sigset_t *); int _thread_sys_sigprocmask(int, const sigset_t *, sigset_t *); int _thread_sys_sigsuspend(const sigset_t *); -int _thread_sys_sigblock(int); int _thread_sys_siginterrupt(int, int); int _thread_sys_sigpause(int); int _thread_sys_sigreturn(struct sigcontext *); -int _thread_sys_sigsetmask(int); int _thread_sys_sigstack(const struct sigstack *, struct sigstack *); int _thread_sys_sigvec(int, struct sigvec *, struct sigvec *); void _thread_sys_psignal(unsigned int, const char *); @@ -734,17 +764,6 @@ int _thread_sys_flock(int, int); int _thread_sys_open(const char *, int, ...); #endif -/* #include <setjmp.h> */ -#ifdef _SETJMP_H_ -int __thread_sys_setjmp(jmp_buf); -int _thread_sys_setjmp(jmp_buf); -int _thread_sys_sigsetjmp(sigjmp_buf, int); -void __thread_sys_longjmp(jmp_buf, int); -void _thread_sys_longjmp(jmp_buf, int); -void _thread_sys_longjmperror(void); -void _thread_sys_siglongjmp(sigjmp_buf, int); -#endif - /* #include <sys/ioctl.h> */ #ifdef _SYS_IOCTL_H_ int _thread_sys_ioctl(int, unsigned long, ...); diff --git a/lib/libkse/thread/thr_read.c b/lib/libkse/thread/thr_read.c index 242014e..76df2cf 100644 --- a/lib/libkse/thread/thr_read.c +++ b/lib/libkse/thread/thr_read.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: uthread_read.c,v 1.3 1997/04/01 22:44:15 jb Exp $ * */ #include <sys/types.h> @@ -45,7 +45,6 @@ ssize_t read(int fd, void *buf, size_t nbytes) { int ret; - int status; /* Lock the file descriptor for read: */ if ((ret = _thread_fd_lock(fd, FD_READ, NULL, @@ -54,7 +53,6 @@ read(int fd, void *buf, size_t nbytes) while ((ret = _thread_sys_read(fd, buf, nbytes)) < 0) { if ((_thread_fd_table[fd]->flags & O_NONBLOCK) == 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) { - _thread_kern_sig_block(&status); _thread_run->data.fd.fd = fd; _thread_kern_set_timeout(NULL); @@ -69,6 +67,7 @@ read(int fd, void *buf, size_t nbytes) * interrupted by a signal */ if (_thread_run->interrupted) { + errno = EINTR; ret = -1; break; } diff --git a/lib/libkse/thread/thr_readv.c b/lib/libkse/thread/thr_readv.c index 3ea065e..10d1a43 100644 --- a/lib/libkse/thread/thr_readv.c +++ b/lib/libkse/thread/thr_readv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id$ + * $Id: uthread_readv.c,v 1.3 1997/04/01 22:44:16 jb Exp $ * */ #include <sys/types.h> @@ -45,7 +45,6 @@ ssize_t readv(int fd, const struct iovec * iov, int iovcnt) { int ret; - int status; /* Lock the file descriptor for read: */ if ((ret = _thread_fd_lock(fd, FD_READ, NULL, @@ -54,7 +53,6 @@ readv(int fd, const struct iovec * iov, int iovcnt) while ((ret = _thread_sys_readv(fd, iov, iovcnt)) < 0) { if ((_thread_fd_table[fd]->flags & O_NONBLOCK) == 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) { - _thread_kern_sig_block(&status); _thread_run->data.fd.fd = fd; _thread_kern_set_timeout(NULL); @@ -69,6 +67,7 @@ readv(int fd, const struct iovec * iov, int iovcnt) * interrupted by a signal */ if (_thread_run->interrupted) { + errno = EINTR; ret = -1; break; } diff --git a/lib/libkse/thread/thr_resume_np.c b/lib/libkse/thread/thr_resume_np.c index 934df58..7c5f46a 100644 --- a/lib/libkse/thread/thr_resume_np.c +++ b/lib/libkse/thread/thr_resume_np.c @@ -35,36 +35,20 @@ #include <pthread.h> #include "pthread_private.h" +/* Resume a thread: */ int pthread_resume_np(pthread_t thread) { - int ret = -1; - pthread_t pthread; - /* - * Search for the thread in the linked list. - */ - for (pthread = _thread_link_list; pthread != NULL && ret == -1; pthread = pthread->nxt) { - /* Is this the thread? */ - if (pthread == thread) { - /* Found the thread. Is it suspended? */ - if (pthread->state == PS_SUSPENDED) { - /* Allow the thread to run. */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - ret = 0; - } else if (pthread->state == PS_RUNNING) { - /* Thread is already running. */ - ret = 0; - } else { - /* Thread is in some other state. */ - errno = EINVAL; - } + int ret; + + /* Find the thread in the list of active threads: */ + if ((ret = _find_thread(thread)) == 0) { + /* The thread exists. Is it suspended? */ + if (thread->state != PS_SUSPENDED) { + /* Allow the thread to run. */ + PTHREAD_NEW_STATE(thread,PS_RUNNING); } } - /* Check if thread was not found. */ - if (ret == -1) { - /* No such thread */ - errno = ESRCH; - } return(ret); } #endif diff --git a/lib/libkse/thread/thr_select.c b/lib/libkse/thread/thr_select.c index 7db3ed2..96df9c1 100644 --- a/lib/libkse/thread/thr_select.c +++ b/lib/libkse/thread/thr_select.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -125,8 +125,13 @@ select(int numfds, fd_set * readfds, fd_set * writefds, memcpy(&data.exceptfds, exceptfds, sizeof(data.exceptfds)); } _thread_run->data.select_data = &data; + _thread_run->interrupted = 0; _thread_kern_sched_state(PS_SELECT_WAIT, __FILE__, __LINE__); - ret = data.nfds; + if (_thread_run->interrupted) { + errno = EINTR; + ret = -1; + } else + ret = data.nfds; } } /* clean up the locks */ diff --git a/lib/libkse/thread/thr_seterrno.c b/lib/libkse/thread/thr_seterrno.c index c4fe08b6..570807f 100644 --- a/lib/libkse/thread/thr_seterrno.c +++ b/lib/libkse/thread/thr_seterrno.c @@ -47,15 +47,14 @@ void _thread_seterrno(pthread_t thread, int error) { /* Check for the initial thread: */ - if (thread == _thread_initial) { + if (thread == _thread_initial) /* The initial thread always uses the global error variable: */ errno = error; - } else { + else /* * Threads other than the initial thread always use the error * field in the thread structureL */ thread->error = error; - } } #endif diff --git a/lib/libkse/thread/thr_setprio.c b/lib/libkse/thread/thr_setprio.c index 4b49ed0..dd89f15 100644 --- a/lib/libkse/thread/thr_setprio.c +++ b/lib/libkse/thread/thr_setprio.c @@ -38,43 +38,19 @@ int pthread_setprio(pthread_t pthread, int prio) { - int rval = 0; - int status; - pthread_t pthread_p; + int ret; /* Check if the priority is invalid: */ - if (prio < PTHREAD_MIN_PRIORITY || prio > PTHREAD_MAX_PRIORITY) { + if (prio < PTHREAD_MIN_PRIORITY || prio > PTHREAD_MAX_PRIORITY) /* Return an invalid argument error: */ - errno = EINVAL; - rval = -1; - } else { - /* Block signals: */ - _thread_kern_sig_block(&status); + ret = EINVAL; - /* Point to the first thread in the list: */ - pthread_p = _thread_link_list; - - /* Enter a loop to search for the thread: */ - while (pthread_p != NULL && pthread_p != pthread) { - /* Point to the next thread: */ - pthread_p = pthread_p->nxt; - } - - /* Check if the thread pointer is NULL: */ - if (pthread == NULL || pthread_p == NULL) { - /* Return a 'search' error: */ - errno = ESRCH; - rval = -1; - } else { - /* Set the thread priority: */ - pthread->pthread_priority = prio; - } - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - } + /* Find the thread in the list of active threads: */ + else if ((ret = _find_thread(pthread)) == 0) + /* Set the thread priority: */ + pthread->pthread_priority = prio; /* Return the error status: */ - return (rval); + return (ret); } #endif diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c index e73d5a2..1e5ee63 100644 --- a/lib/libkse/thread/thr_sig.c +++ b/lib/libkse/thread/thr_sig.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -38,16 +38,79 @@ #include <pthread.h> #include "pthread_private.h" +/* Static variables: */ +static int volatile yield_on_unlock_dead = 0; +static int volatile yield_on_unlock_thread = 0; +static long volatile thread_dead_lock = 0; +static long volatile thread_link_list_lock = 0; + +/* Lock the thread list: */ +void +_lock_thread_list() +{ + /* Lock the thread list: */ + _spinlock(&thread_link_list_lock); +} + +/* Lock the dead thread list: */ +void +_lock_dead_thread_list() +{ + /* Lock the dead thread list: */ + _spinlock(&thread_dead_lock); +} + +/* Lock the thread list: */ +void +_unlock_thread_list() +{ + /* Unlock the thread list: */ + _atomic_unlock(&thread_link_list_lock); + + /* + * Check if a scheduler interrupt occurred while the thread + * list was locked: + */ + if (yield_on_unlock_thread) { + /* Reset the interrupt flag: */ + yield_on_unlock_thread = 0; + + /* This thread has overstayed it's welcome: */ + sched_yield(); + } +} + +/* Lock the dead thread list: */ +void +_unlock_dead_thread_list() +{ + /* Unlock the dead thread list: */ + _atomic_unlock(&thread_dead_lock); + + /* + * Check if a scheduler interrupt occurred while the dead + * thread list was locked: + */ + if (yield_on_unlock_dead) { + /* Reset the interrupt flag: */ + yield_on_unlock_dead = 0; + + /* This thread has overstayed it's welcome: */ + sched_yield(); + } +} + void _thread_sig_handler(int sig, int code, struct sigcontext * scp) { char c; int i; + int dispatch = 0; pthread_t pthread; /* * Check if the pthread kernel has unblocked signals (or is about to) - * and was on its way into a _thread_sys_select when the current + * and was on its way into a _select when the current * signal interrupted it: */ if (_thread_kern_in_select) { @@ -57,51 +120,65 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) /* * Write the signal number to the kernel pipe so that it will * be ready to read when this signal handler returns. This - * means that the _thread_sys_select call will complete + * means that the _select call will complete * immediately. */ - if (_thread_sys_write(_thread_kern_pipe[1], &c, 1) != 1) { - } + _thread_sys_write(_thread_kern_pipe[1], &c, 1); } + /* Check if the signal requires a dump of thread information: */ - if (sig == SIGINFO) { + if (sig == SIGINFO) /* Dump thread information to file: */ _thread_dump_info(); - } else { - /* Handle depending on signal type: */ - switch (sig) { - /* Interval timer used for timeslicing: */ - case SIGVTALRM: + + /* Check if an interval timer signal: */ + else if (sig == SIGVTALRM) { + /* Check if the scheduler interrupt has come at an + * unfortunate time which one of the threads is + * modifying the thread list: + */ + if (thread_link_list_lock) /* - * Don't add the signal to any thread. Just want to - * call the scheduler: + * Set a flag so that the thread that has + * the lock yields when it unlocks the + * thread list: */ - break; + yield_on_unlock_thread = 1; - /* Child termination: */ - case SIGCHLD: + /* Check if the scheduler interrupt has come at an + * unfortunate time which one of the threads is + * modifying the dead thread list: + */ + if (thread_dead_lock) /* - * Enter a loop to process each thread in the linked - * list: + * Set a flag so that the thread that has + * the lock yields when it unlocks the + * dead thread list: */ - for (pthread = _thread_link_list; pthread != NULL; - pthread = pthread->nxt) { - /* - * Add the signal to the set of pending - * signals: - */ - pthread->sigpend[sig] += 1; - if (pthread->state == PS_WAIT_WAIT) { - /* Reset the error: */ - /* There should be another flag so that this is not required! ### */ - _thread_seterrno(pthread, 0); - - /* Change the state of the thread to run: */ - PTHREAD_NEW_STATE(pthread,PS_RUNNING); - } - } + yield_on_unlock_dead = 1; + + /* + * Check if the kernel has not been interrupted while + * executing scheduler code: + */ + else if (!_thread_kern_in_sched) { + /* + * Schedule the next thread. This function is not + * expected to return because it will do a longjmp + * instead. + */ + _thread_kern_sched(scp); /* + * This point should not be reached, so abort the + * process: + */ + PANIC("Returned to signal function from scheduler"); + } + } else { + /* Check if a child has terminated: */ + if (sig == SIGCHLD) { + /* * Go through the file list and set all files * to non-blocking again in case the child * set some of them to block. Sigh. @@ -109,59 +186,137 @@ _thread_sig_handler(int sig, int code, struct sigcontext * scp) for (i = 0; i < _thread_dtablesize; i++) { /* Check if this file is used: */ if (_thread_fd_table[i] != NULL) { - /* Set the file descriptor to non-blocking: */ - _thread_sys_fcntl(i, F_SETFL, _thread_fd_table[i]->flags | O_NONBLOCK); + /* + * Set the file descriptor to + * non-blocking: + */ + _thread_sys_fcntl(i, F_SETFL, + _thread_fd_table[i]->flags | + O_NONBLOCK); } } - break; + } - /* Signals specific to the running thread: */ - case SIGBUS: - case SIGEMT: - case SIGFPE: - case SIGILL: - case SIGPIPE: - case SIGSEGV: - case SIGSYS: - /* Add the signal to the set of pending signals: */ - _thread_run->sigpend[sig] += 1; - break; + /* + * POSIX says that pending SIGCONT signals are + * discarded when one of there signals occurs. + */ + if (sig == SIGTSTP || sig == SIGTTIN || sig == SIGTTOU) { + /* + * Enter a loop to discard pending SIGCONT + * signals: + */ + for (pthread = _thread_link_list; + pthread != NULL; + pthread = pthread->nxt) + sigdelset(&pthread->sigpend,SIGCONT); + } - /* Signals to send to all threads: */ - default: + /* Check if the signal is not being ignored: */ + if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) /* * Enter a loop to process each thread in the linked * list: */ for (pthread = _thread_link_list; pthread != NULL; - pthread = pthread->nxt) { - /* - * Add the signal to the set of pending - * signals: - */ - pthread->sigpend[sig] += 1; - } + pthread = pthread->nxt) + _thread_signal(pthread,sig); + + /* Dispatch pending signals to the running thread: */ + _dispatch_signals(); + } + + /* Returns nothing. */ + return; +} + +/* Perform thread specific actions in response to a signal: */ +void +_thread_signal(pthread_t pthread, int sig) +{ + pthread_t saved; + struct sigaction act; + + /* + * Flag the signal as pending. It will be dispatched later. + */ + sigaddset(&pthread->sigpend,sig); + + /* Check if system calls are not restarted: */ + if ((_thread_sigact[sig - 1].sa_flags & SA_RESTART) == 0) { + /* + * 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_FILE_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; + + /* + * 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_SLEEP_WAIT: + case PS_SIGWAIT: + case PS_WAIT_WAIT: + case PS_SELECT_WAIT: + /* Flag the operation as interrupted: */ + pthread->interrupted = 1; + + /* Change the state of the thread to run: */ + PTHREAD_NEW_STATE(pthread,PS_RUNNING); + + /* Return the signal number: */ + pthread->signo = sig; break; } + } +} - /* Check if the kernel is not locked: */ - if (_thread_run != &_thread_kern_thread) { - /* - * Schedule the next thread. This function is not - * expected to return because it will do a longjmp - * instead. - */ - _thread_kern_sched(scp); +/* Dispatch pending signals to the running thread: */ +void +_dispatch_signals() +{ + int i; + /* + * Check if there are pending signals for the running + * thread that aren't blocked: + */ + if ((_thread_run->sigpend & ~_thread_run->sigmask) != 0) + /* Look for all possible pending signals: */ + for (i = 1; i < NSIG; i++) /* - * This point should not be reached, so abort the - * process: + * Check that a custom handler is installed + * and if the signal is not blocked: */ - PANIC("Returned to signal function from scheduler"); - } - } + if (_thread_sigact[i - 1].sa_handler != SIG_DFL && + _thread_sigact[i - 1].sa_handler != SIG_IGN && + sigismember(&_thread_run->sigpend,i) && + !sigismember(&_thread_run->sigmask,i)) { + /* Clear the pending signal: */ + sigdelset(&_thread_run->sigpend,i); - /* Returns nothing. */ - return; + /* + * Dispatch the signal via the custom signal + * handler: + */ + (*(_thread_sigact[i - 1].sa_handler))(i); + } } #endif diff --git a/lib/libkse/thread/thr_sigaction.c b/lib/libkse/thread/thr_sigaction.c index dd08405..3538f27 100644 --- a/lib/libkse/thread/thr_sigaction.c +++ b/lib/libkse/thread/thr_sigaction.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,8 +39,8 @@ int sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { - int ret = 0; - int status; + int ret = 0; + struct sigaction gact; /* Check if the signal number is out of range: */ if (sig < 1 || sig > NSIG) { @@ -54,25 +54,47 @@ sigaction(int sig, const struct sigaction * act, struct sigaction * oact) */ if (oact != NULL) { /* Return the existing signal action contents: */ - oact->sa_handler = _thread_run->act[sig - 1].sa_handler; - oact->sa_mask = _thread_run->act[sig - 1].sa_mask; - oact->sa_flags = _thread_run->act[sig - 1].sa_flags; + oact->sa_handler = _thread_sigact[sig - 1].sa_handler; + oact->sa_mask = _thread_sigact[sig - 1].sa_mask; + oact->sa_flags = _thread_sigact[sig - 1].sa_flags; } + /* Check if a signal action was supplied: */ if (act != NULL) { - /* Block signals while the signal handler is changed: */ - _thread_kern_sig_block(&status); - /* Set the new signal handler: */ - _thread_run->act[sig - 1].sa_handler = act->sa_handler; - _thread_run->act[sig - 1].sa_mask = act->sa_mask; - _thread_run->act[sig - 1].sa_flags = act->sa_flags; + _thread_sigact[sig - 1].sa_mask = act->sa_mask; + _thread_sigact[sig - 1].sa_flags = act->sa_flags; + _thread_sigact[sig - 1].sa_handler = act->sa_handler; + } + + /* + * Check if the kernel needs to be advised of a change + * in signal action: + */ + if (act != NULL && sig != SIGVTALRM && sig != SIGCHLD && + sig != SIGINFO) { + /* Initialise the global signal action structure: */ + gact.sa_mask = act->sa_mask; + gact.sa_flags = act->sa_flags | SA_RESTART; /* - * Unblock signals to allow the new signal handler to - * take effect: + * Check if the signal handler is being set to + * the default or ignore handlers: */ - _thread_kern_sig_unblock(status); + if (act->sa_handler == SIG_DFL || + act->sa_handler == SIG_IGN) + /* Specify the built in handler: */ + gact.sa_handler = act->sa_handler; + else + /* + * Specify the thread kernel signal + * handler: + */ + gact.sa_handler = (void (*) ()) _thread_sig_handler; + + /* Change the signal action in the kernel: */ + if (_thread_sys_sigaction(sig,&gact,NULL) != 0) + ret = -1; } } diff --git a/lib/libkse/thread/thr_sigmask.c b/lib/libkse/thread/thr_sigmask.c index 94f64cb..23d6b7f 100644 --- a/lib/libkse/thread/thr_sigmask.c +++ b/lib/libkse/thread/thr_sigmask.c @@ -39,8 +39,7 @@ int pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) { - int ret = 0; - int status; + int ret = 0; /* Check if the existing signal process mask is to be returned: */ if (oset != NULL) { @@ -49,9 +48,6 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) } /* Check if a new signal set was provided by the caller: */ if (set != NULL) { - /* Block signals while the signal mask is changed: */ - _thread_kern_sig_block(&status); - /* Process according to what to do: */ switch (how) { /* Block signals: */ @@ -81,11 +77,12 @@ pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) } /* - * Schedule the next thread in case there are signals that - * now need to be acted on: + * Dispatch signals to the running thread that are pending + * and now unblocked: */ - _thread_kern_sched(NULL); + _dispatch_signals(); } + /* Return the completion status: */ return (ret); } diff --git a/lib/libkse/thread/thr_sigprocmask.c b/lib/libkse/thread/thr_sigprocmask.c index b4a8d84..81b602f 100644 --- a/lib/libkse/thread/thr_sigprocmask.c +++ b/lib/libkse/thread/thr_sigprocmask.c @@ -39,8 +39,7 @@ int sigprocmask(int how, const sigset_t * set, sigset_t * oset) { - int ret = 0; - int status; + int ret = 0; /* Check if the existing signal process mask is to be returned: */ if (oset != NULL) { @@ -49,9 +48,6 @@ sigprocmask(int how, const sigset_t * set, sigset_t * oset) } /* Check if a new signal set was provided by the caller: */ if (set != NULL) { - /* Block signals while the signal mask is changed: */ - _thread_kern_sig_block(&status); - /* Process according to what to do: */ switch (how) { /* Block signals: */ @@ -81,10 +77,10 @@ sigprocmask(int how, const sigset_t * set, sigset_t * oset) } /* - * Schedule the next thread in case there are signals that - * now need to be acted on: + * Dispatch signals to the running thread that are pending + * and now unblocked: */ - _thread_kern_sched(NULL); + _dispatch_signals(); } /* Return the completion status: */ return (ret); diff --git a/lib/libkse/thread/thr_sigsuspend.c b/lib/libkse/thread/thr_sigsuspend.c index 14cf74b..f7a7b12 100644 --- a/lib/libkse/thread/thr_sigsuspend.c +++ b/lib/libkse/thread/thr_sigsuspend.c @@ -53,6 +53,9 @@ sigsuspend(const sigset_t * set) /* Wait for a signal: */ _thread_kern_sched_state(PS_SIGWAIT, __FILE__, __LINE__); + /* Always return an interrupted error: */ + errno = EINTR; + /* Restore the signal mask: */ _thread_run->sigmask = oset; } else { diff --git a/lib/libkse/thread/thr_sigwait.c b/lib/libkse/thread/thr_sigwait.c index 4f95190..63c7093 100644 --- a/lib/libkse/thread/thr_sigwait.c +++ b/lib/libkse/thread/thr_sigwait.c @@ -43,9 +43,6 @@ sigwait(const sigset_t * set, int *sig) int status; sigset_t oset; - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Save the current sigmal mask: */ oset = _thread_run->sigmask; @@ -55,18 +52,12 @@ sigwait(const sigset_t * set, int *sig) /* Wait for a signal: */ _thread_kern_sched_state(PS_SIGWAIT, __FILE__, __LINE__); - /* Block signals again: */ - _thread_kern_sig_block(NULL); - /* Return the signal number to the caller: */ *sig = _thread_run->signo; /* Restore the signal mask: */ _thread_run->sigmask = oset; - /* Unblock signals: */ - _thread_kern_sig_unblock(status); - /* Return the completion status: */ return (ret); } diff --git a/lib/libkse/thread/thr_spec.c b/lib/libkse/thread/thr_spec.c index 7ff9054..dc493dd 100644 --- a/lib/libkse/thread/thr_spec.c +++ b/lib/libkse/thread/thr_spec.c @@ -39,29 +39,38 @@ #include "pthread_private.h" /* Static variables: */ -static struct pthread_key key_table[PTHREAD_KEYS_MAX]; +static struct pthread_key key_table[PTHREAD_KEYS_MAX]; +static long key_table_lock = 0; int pthread_key_create(pthread_key_t * key, void (*destructor) (void *)) { + /* Lock the key table: */ + _spinlock(&key_table_lock); + for ((*key) = 0; (*key) < PTHREAD_KEYS_MAX; (*key)++) { if (key_table[(*key)].count == 0) { key_table[(*key)].count++; key_table[(*key)].destructor = destructor; + + /* Unlock the key table: */ + _atomic_unlock(&key_table_lock); return (0); } } + + /* Unlock the key table: */ + _atomic_unlock(&key_table_lock); return (EAGAIN); } int pthread_key_delete(pthread_key_t key) { - int ret; - int status; + int ret; - /* Block signals: */ - _thread_kern_sig_block(&status); + /* Lock the key table: */ + _spinlock(&key_table_lock); if (key < PTHREAD_KEYS_MAX) { switch (key_table[key].count) { @@ -74,12 +83,11 @@ pthread_key_delete(pthread_key_t key) default: ret = EBUSY; } - } else { + } else ret = EINVAL; - } - /* Unblock signals: */ - _thread_kern_sig_unblock(status); + /* Unlock the key table: */ + _atomic_unlock(&key_table_lock); return (ret); } @@ -89,14 +97,13 @@ _thread_cleanupspecific(void) void *data; int key; int itr; - int status; - - /* Block signals: */ - _thread_kern_sig_block(&status); for (itr = 0; itr < PTHREAD_DESTRUCTOR_ITERATIONS; itr++) { for (key = 0; key < PTHREAD_KEYS_MAX; key++) { if (_thread_run->specific_data_count) { + /* Lock the key table entry: */ + _spinlock(&key_table[key].access_lock); + if (_thread_run->specific_data[key]) { data = (void *) _thread_run->specific_data[key]; _thread_run->specific_data[key] = NULL; @@ -106,19 +113,16 @@ _thread_cleanupspecific(void) } key_table[key].count--; } + + /* Unlock the key table entry: */ + _atomic_unlock(&key_table[key].access_lock); } else { free(_thread_run->specific_data); - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); return; } } } free(_thread_run->specific_data); - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); } static inline const void ** @@ -136,25 +140,16 @@ pthread_setspecific(pthread_key_t key, const void *value) { pthread_t pthread; int ret = 0; - int status; - - /* Block signals: */ - _thread_kern_sig_block(&status); /* Point to the running thread: */ pthread = _thread_run; - /* - * Enter a loop for signal handler threads to find the parent thread - * which has the specific data associated with it: - */ - while (pthread->parent_thread != NULL) { - /* Point to the parent thread: */ - pthread = pthread->parent_thread; - } - - if ((pthread->specific_data) || (pthread->specific_data = pthread_key_allocate_data())) { + if ((pthread->specific_data) || + (pthread->specific_data = pthread_key_allocate_data())) { if ((key < PTHREAD_KEYS_MAX) && (key_table)) { + /* Lock the key table entry: */ + _spinlock(&key_table[key].access_lock); + if (key_table[key].count) { if (pthread->specific_data[key] == NULL) { if (value != NULL) { @@ -169,18 +164,16 @@ pthread_setspecific(pthread_key_t key, const void *value) } pthread->specific_data[key] = value; ret = 0; - } else { + } else ret = EINVAL; - } - } else { + + /* Unlock the key table entry: */ + _atomic_unlock(&key_table[key].access_lock); + + } else ret = EINVAL; - } - } else { + } else ret = ENOMEM; - } - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); return (ret); } @@ -188,31 +181,17 @@ void * pthread_getspecific(pthread_key_t key) { pthread_t pthread; - int status; void *data; - /* Block signals: */ - _thread_kern_sig_block(&status); - /* Point to the running thread: */ pthread = _thread_run; - /* - * Enter a loop for signal handler threads to find the parent thread - * which has the specific data associated with it: - */ - while (pthread->parent_thread != NULL) { - /* Point to the parent thread: */ - pthread = pthread->parent_thread; - } - - /* Check for errors: */ - if (pthread == NULL) { - /* Return an invalid argument error: */ - data = NULL; - } /* Check if there is specific data: */ - else if (pthread->specific_data != NULL && (key < PTHREAD_KEYS_MAX) && (key_table)) { + if (pthread->specific_data != NULL && + (key < PTHREAD_KEYS_MAX) && (key_table)) { + /* Lock the key table entry: */ + _spinlock(&key_table[key].access_lock); + /* Check if this key has been used before: */ if (key_table[key].count) { /* Return the value: */ @@ -224,13 +203,12 @@ pthread_getspecific(pthread_key_t key) */ data = NULL; } - } else { + + /* Unlock the key table entry: */ + _atomic_unlock(&key_table[key].access_lock); + } else /* No specific data has been created, so just return NULL: */ data = NULL; - } - - /* Unblock signals: */ - _thread_kern_sig_unblock(status); return (data); } #endif diff --git a/lib/libkse/thread/thr_spinlock.c b/lib/libkse/thread/thr_spinlock.c new file mode 100644 index 0000000..b2fe269 --- /dev/null +++ b/lib/libkse/thread/thr_spinlock.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1997 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. + * + * $Id$ + * + */ + +#include <stdio.h> +#include <sched.h> +#include <unistd.h> +#include <pthread.h> +#include "spinlock.h" +#include "pthread_private.h" + +/* + * Lock a location for the running thread. Yield to allow other + * threads to run if this thread is blocked because the lock is + * not available. Note that this function does not sleep. It + * assumes that the lock will be available very soon. + */ +void +_spinlock(long *lck) +{ + do { + /* + * Allow other threads to run if the lock is not + * available: + */ + while (*lck != 0) + sched_yield(); + + /* + * Try to grab the lock and loop if another thread grabs + * it before we do. + */ + } while(_atomic_lock(lck,(long) _thread_run)); +} diff --git a/lib/libkse/thread/thr_suspend_np.c b/lib/libkse/thread/thr_suspend_np.c index d065b73..871683a 100644 --- a/lib/libkse/thread/thr_suspend_np.c +++ b/lib/libkse/thread/thr_suspend_np.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au>. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,32 +35,24 @@ #include <pthread.h> #include "pthread_private.h" +/* Suspend a thread: */ int pthread_suspend_np(pthread_t thread) { - int ret = -1; - pthread_t pthread; - /* - * Search for the thread in the linked list. - */ - for (pthread = _thread_link_list; pthread != NULL && ret == -1; pthread = pthread->nxt) { - /* Is this the thread? */ - if (pthread == thread) { - /* Found the thread. Is it running? */ - if (pthread->state != PS_RUNNING && - pthread->state != PS_SUSPENDED) { - /* The thread operation has been interrupted */ - _thread_seterrno(pthread,EINTR); - } - /* Suspend the thread. */ - PTHREAD_NEW_STATE(pthread,PS_SUSPENDED); - ret = 0; + int ret; + + /* Find the thread in the list of active threads: */ + if ((ret = _find_thread(thread)) == 0) { + /* The thread exists. Is it running? */ + if (thread->state != PS_RUNNING && + thread->state != PS_SUSPENDED) { + /* The thread operation has been interrupted */ + _thread_seterrno(thread,EINTR); + thread->interrupted = 1; } - } - /* Check if thread was not found. */ - if (ret == -1) { - /* No such thread */ - errno = ESRCH; + + /* Suspend the thread. */ + PTHREAD_NEW_STATE(thread,PS_SUSPENDED); } return(ret); } diff --git a/lib/libkse/thread/thr_wait4.c b/lib/libkse/thread/thr_wait4.c index 0a721af4f..dda8aee 100644 --- a/lib/libkse/thread/thr_wait4.c +++ b/lib/libkse/thread/thr_wait4.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -39,23 +39,23 @@ pid_t wait4(pid_t pid, int *istat, int options, struct rusage * rusage) { - int status; pid_t ret; - _thread_kern_sig_block(&status); /* Perform a non-blocking wait4 syscall: */ while ((ret = _thread_sys_wait4(pid, istat, options | WNOHANG, rusage)) == 0 && (options & WNOHANG) == 0) { + /* Reset the interrupted operation flag: */ + _thread_run->interrupted = 0; + /* Schedule the next thread while this one waits: */ _thread_kern_sched_state(PS_WAIT_WAIT, __FILE__, __LINE__); /* Check if this call was interrupted by a signal: */ - if (errno == EINTR) { + if (_thread_run->interrupted) { + errno = EINTR; ret = -1; break; } - _thread_kern_sig_block(NULL); } - _thread_kern_sig_unblock(status); return (ret); } #endif diff --git a/lib/libkse/thread/thr_write.c b/lib/libkse/thread/thr_write.c index e86e71b..f8be7da 100644 --- a/lib/libkse/thread/thr_write.c +++ b/lib/libkse/thread/thr_write.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_write.c,v 1.3 1997/04/01 22:44:17 jb Exp $ + * $Id: uthread_write.c,v 1.4 1998/02/13 01:27:33 julian Exp $ * */ #include <sys/types.h> @@ -45,7 +45,6 @@ ssize_t write(int fd, const void *buf, size_t nbytes) { int ret; - int status; /* Lock the file descriptor for write: */ if ((ret = _thread_fd_lock(fd, FD_WRITE, NULL, @@ -54,7 +53,6 @@ write(int fd, const void *buf, size_t nbytes) while ((ret = _thread_sys_write(fd, buf, nbytes)) < 0) { if ((_thread_fd_table[fd]->flags & O_NONBLOCK) == 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) { - _thread_kern_sig_block(&status); _thread_run->data.fd.fd = fd; _thread_kern_set_timeout(NULL); @@ -69,6 +67,7 @@ write(int fd, const void *buf, size_t nbytes) * interrupted by a signal */ if (_thread_run->interrupted) { + errno = EINTR; ret = -1; break; } diff --git a/lib/libkse/thread/thr_writev.c b/lib/libkse/thread/thr_writev.c index 56e1872..f3badec 100644 --- a/lib/libkse/thread/thr_writev.c +++ b/lib/libkse/thread/thr_writev.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1995 John Birrell <jb@cimlogic.com.au>. + * Copyright (c) 1995-1998 John Birrell <jb@cimlogic.com.au> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,7 +29,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: uthread_writev.c,v 1.3 1997/04/01 22:44:18 jb Exp $ + * $Id: uthread_writev.c,v 1.4 1998/02/13 01:27:34 julian Exp $ * */ #include <sys/types.h> @@ -45,7 +45,6 @@ ssize_t writev(int fd, const struct iovec * iov, int iovcnt) { int ret; - int status; /* Lock the file descriptor for write: */ if ((ret = _thread_fd_lock(fd, FD_WRITE, NULL, @@ -54,7 +53,6 @@ writev(int fd, const struct iovec * iov, int iovcnt) while ((ret = _thread_sys_writev(fd, iov, iovcnt)) < 0) { if ((_thread_fd_table[fd]->flags & O_NONBLOCK) == 0 && (errno == EWOULDBLOCK || errno == EAGAIN)) { - _thread_kern_sig_block(&status); _thread_run->data.fd.fd = fd; _thread_kern_set_timeout(NULL); @@ -69,6 +67,7 @@ writev(int fd, const struct iovec * iov, int iovcnt) * interrupted by a signal */ if (_thread_run->interrupted) { + errno = EINTR; ret = -1; break; } |