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/libpthread/thread/thr_kern.c | |
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/libpthread/thread/thr_kern.c')
-rw-r--r-- | lib/libpthread/thread/thr_kern.c | 470 |
1 files changed, 35 insertions, 435 deletions
diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c index 3b62422..36efc31 100644 --- a/lib/libpthread/thread/thr_kern.c +++ b/lib/libpthread/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: */ |