diff options
author | davidxu <davidxu@FreeBSD.org> | 2003-06-28 09:55:02 +0000 |
---|---|---|
committer | davidxu <davidxu@FreeBSD.org> | 2003-06-28 09:55:02 +0000 |
commit | 7b25bda5637f6013ca7067e4482a40b0a4c73bc6 (patch) | |
tree | 081cc14974dcb04e9a4a9f829846792004190d57 | |
parent | edb09b0c7fe5cfb64cf9fbdff972073411292488 (diff) | |
download | FreeBSD-src-7b25bda5637f6013ca7067e4482a40b0a4c73bc6.zip FreeBSD-src-7b25bda5637f6013ca7067e4482a40b0a4c73bc6.tar.gz |
o Use a daemon thread to monitor signal events in kernel, if pending
signals were changed in kernel, it will retrieve the pending set and
try to find a thread to dispatch the signal. The dispatching process
can be rolled back if the signal is no longer in kernel.
o Create two functions _thr_signal_init() and _thr_signal_deinit(),
all signal action settings are retrieved from kernel when threading
mode is turned on, after a fork(), child process will reset them to
user settings by calling _thr_signal_deinit(). when threading mode
is not turned on, all signal operations are direct past to kernel.
o When a thread generated a synchoronous signals and its context returned
from completed list, UTS will retrieve the signal from its mailbox and try
to deliver the signal to thread.
o Context signal mask is now only used when delivering signals, thread's
current signal mask is always the one in pthread structure.
o Remove have_signals field in pthread structure, replace it with
psf_valid in pthread_signal_frame. when psf_valid is true, in context
switch time, thread will backout itself from some mutex/condition
internal queues, then begin to process signals. when a thread is not
at blocked state and running, check_pending indicates there are signals
for the thread, after preempted and then resumed time, UTS will try to
deliver signals to the thread.
o At signal delivering time, not only pending signals in thread will be
scanned, process's pending signals will be scanned too.
o Change sigwait code a bit, remove field sigwait in pthread_wait_data,
replace it with oldsigmask in pthread structure, when a thread calls
sigwait(), its current signal mask is backuped to oldsigmask, and waitset
is copied to its signal mask and when the thread gets a signal in the
waitset range, its current signal mask is restored from oldsigmask,
these are done in atomic fashion.
o Two additional POSIX APIs are implemented, sigwaitinfo() and sigtimedwait().
o Signal code locking is better than previous, there is fewer race conditions.
o Temporary disable most of code in _kse_single_thread as it is not safe
after fork().
27 files changed, 1622 insertions, 1172 deletions
diff --git a/lib/libkse/support/thr_support.c b/lib/libkse/support/thr_support.c index 1b3c4ef..ad40fec 100644 --- a/lib/libkse/support/thr_support.c +++ b/lib/libkse/support/thr_support.c @@ -54,5 +54,8 @@ __strong_reference(memcpy, _thr_memcpy); __strong_reference(strcpy, _thr_strcpy); __strong_reference(strlen, _thr_strlen); __strong_reference(bzero, _thr_bzero); +__strong_reference(bcopy, _thr_bcopy); __strong_reference(__sys_write, _thr__sys_write); +__strong_reference(__sys_sigtimedwait, _thr__sys_sigtimedwait); + diff --git a/lib/libkse/thread/thr_info.c b/lib/libkse/thread/thr_info.c index 3218b5b..4410ace 100644 --- a/lib/libkse/thread/thr_info.c +++ b/lib/libkse/thread/thr_info.c @@ -77,7 +77,7 @@ _thread_dump_info(void) int fd, i; for (i = 0; i < 100000; i++) { - snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i", + snprintf(tmpfile, sizeof(tmpfile), "/tmp/pthread.dump.%u.%i", getpid(), i); /* Open the dump file for append and create it if necessary: */ if ((fd = __sys_open(tmpfile, O_RDWR | O_CREAT | O_EXCL, @@ -166,6 +166,12 @@ dump_thread(int fd, pthread_t pthread, int long_version) strcpy(s, "This is the initial thread\n"); __sys_write(fd, s, strlen(s)); } + /* Check if this is the signal daemon thread: */ + if (pthread == _thr_sig_daemon) { + /* Output a record for the signal thread: */ + strcpy(s, "This is the signal daemon thread\n"); + __sys_write(fd, s, strlen(s)); + } /* Process according to thread state: */ switch (pthread->state) { case PS_SIGWAIT: @@ -173,6 +179,15 @@ dump_thread(int fd, pthread_t pthread, int long_version) __sys_write(fd, s, strlen(s)); for (i = _SIG_WORDS - 1; i >= 0; i--) { snprintf(s, sizeof(s), "%08x\n", + pthread->oldsigmask.__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + snprintf(s, sizeof(s), "waitset (hi)"); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x\n", pthread->sigmask.__bits[i]); __sys_write(fd, s, strlen(s)); } diff --git a/lib/libkse/thread/thr_init.c b/lib/libkse/thread/thr_init.c index 227bdf3..202db62 100644 --- a/lib/libkse/thread/thr_init.c +++ b/lib/libkse/thread/thr_init.c @@ -39,6 +39,7 @@ #include "namespace.h" #include <sys/param.h> #include <sys/types.h> +#include <sys/signalvar.h> #include <machine/reg.h> #include <sys/ioctl.h> @@ -304,7 +305,7 @@ _libpthread_init(struct pthread *curthread) _thr_initial->kse->k_curthread = _thr_initial; _thr_initial->kse->k_flags |= KF_INITIALIZED; _kse_initial->k_curthread = _thr_initial; - + _thr_rtld_init(); } @@ -365,14 +366,13 @@ init_main_thread(struct pthread *thread) thread->name = strdup("initial thread"); /* Initialize the thread for signals: */ - sigemptyset(&thread->sigmask); + SIGEMPTYSET(thread->sigmask); /* * Set up the thread mailbox. The threads saved context * is also in the mailbox. */ thread->tmbx.tm_udata = thread; - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; thread->tmbx.tm_context.uc_stack.ss_size = thread->attr.stacksize_attr; thread->tmbx.tm_context.uc_stack.ss_sp = thread->attr.stackaddr_attr; @@ -407,10 +407,8 @@ static void init_private(void) { struct clockinfo clockinfo; - struct sigaction act; size_t len; int mib[2]; - int i; /* * Avoid reinitializing some things if they don't need to be, @@ -448,36 +446,6 @@ init_private(void) _thr_page_size = getpagesize(); _thr_guard_default = _thr_page_size; - - /* 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 (__sys_sigaction(i, NULL, - &_thread_sigact[i - 1]) != 0) { - /* - * Abort this process if signal - * initialisation fails: - */ - PANIC("Cannot read signal handler info"); - } - } - /* - * Install the signal handler for SIGINFO. It isn't - * really needed, but it is nice to have for debugging - * purposes. - */ - if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { - /* - * Abort this process if signal initialisation fails: - */ - PANIC("Cannot initialize signal handler"); - } - _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; - init_once = 1; /* Don't do this again. */ } else { /* @@ -495,8 +463,6 @@ init_private(void) TAILQ_INIT(&_thread_list); TAILQ_INIT(&_thread_gc_list); - /* Enter a loop to get the existing signal status: */ - /* Initialize the SIG_DFL dummy handler count. */ bzero(_thread_dfl_count, sizeof(_thread_dfl_count)); @@ -520,8 +486,7 @@ init_private(void) _thr_spinlock_init(); /* Clear pending signals and get the process signal mask. */ - sigemptyset(&_thr_proc_sigpending); - __sys_sigprocmask(SIG_SETMASK, NULL, &_thr_proc_sigmask); + SIGEMPTYSET(_thr_proc_sigpending); /* * _thread_list_lock and _kse_count are initialized diff --git a/lib/libkse/thread/thr_kern.c b/lib/libkse/thread/thr_kern.c index d1a2006..8a1ecc3 100644 --- a/lib/libkse/thread/thr_kern.c +++ b/lib/libkse/thread/thr_kern.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/signalvar.h> #include <sys/queue.h> #include <machine/atomic.h> +#include <machine/sigframe.h> #include <assert.h> #include <errno.h> @@ -125,7 +126,6 @@ static void dump_queues(struct kse *curkse); #endif static void kse_check_completed(struct kse *kse); static void kse_check_waitq(struct kse *kse); -static void kse_check_signals(struct kse *kse); static void kse_fini(struct kse *curkse); static void kse_reinit(struct kse *kse); static void kse_sched_multi(struct kse *curkse); @@ -143,27 +143,39 @@ static void kse_wakeup_multi(struct kse *curkse); static void kse_wakeup_one(struct pthread *thread); static void thr_cleanup(struct kse *kse, struct pthread *curthread); static void thr_link(struct pthread *thread); -static void thr_resume_wrapper(int unused_1, siginfo_t *unused_2, - ucontext_t *ucp); +static void thr_resume_wrapper(int sig, siginfo_t *, ucontext_t *); static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf); static int thr_timedout(struct pthread *thread, struct timespec *curtime); static void thr_unlink(struct pthread *thread); + /* * This is called after a fork(). * No locks need to be taken here since we are guaranteed to be * single threaded. + * + * XXX + * POSIX says for threaded process, fork() function is used + * only to run new programs, and the effects of calling functions + * that require certain resources between the call to fork() and + * the call to an exec function are undefined. + * + * Here it is not safe to reinitialize the library after fork(). + * Because memory management may be corrupted, further calling + * malloc()/free() may cause undefined behavior. */ void _kse_single_thread(struct pthread *curthread) { +#ifdef NOTYET struct kse *kse; struct kse_group *kseg; struct pthread *thread; kse_critical_t crit; int i; + /* * Disable upcalls and clear the threaded flag. * XXX - I don't think we need to disable upcalls after a fork(). @@ -172,6 +184,7 @@ _kse_single_thread(struct pthread *curthread) crit = _kse_critical_enter(); __isthreaded = 0; active_threads = 1; + _thr_signal_deinit(); /* * Enter a loop to remove and free all threads other than @@ -201,7 +214,7 @@ _kse_single_thread(struct pthread *curthread) TAILQ_INIT(&curthread->mutexq); /* initialize mutex queue */ curthread->joiner = NULL; /* no joining threads yet */ curthread->refcount = 0; - sigemptyset(&curthread->sigpend); /* clear pending signals */ + SIGEMPTYSET(curthread->sigpend); /* clear pending signals */ if (curthread->specific != NULL) { free(curthread->specific); curthread->specific = NULL; @@ -307,6 +320,12 @@ _kse_single_thread(struct pthread *curthread) curthread->kseg = NULL; _kse_initial = NULL; _libpthread_init(curthread); +#else + _ksd_readandclear_tmbx(); + __isthreaded = 0; + active_threads = 0; + _thr_signal_deinit(); +#endif } /* @@ -363,15 +382,17 @@ _kse_setthreaded(int threaded) * Tell the kernel to create a KSE for the initial thread * and enable upcalls in it. */ + _thr_signal_init(); _kse_initial->k_flags |= KF_STARTED; if (kse_create(&_kse_initial->k_mbx, 0) != 0) { _kse_initial->k_flags &= ~KF_STARTED; __isthreaded = 0; /* may abort() */ - DBG_MSG("kse_create failed\n"); + PANIC("kse_create() failed\n"); return (-1); } KSE_SET_MBOX(_kse_initial, _thr_initial); + _thr_start_sig_daemon(); _thr_setmaxconcurrency(); } return (0); @@ -536,6 +557,7 @@ _thr_sched_switch_unlocked(struct pthread *curthread) int ret; volatile int uts_once; volatile int resume_once = 0; + ucontext_t uc; /* We're in the scheduler, 5 by 5: */ curkse = _get_curkse(); @@ -552,7 +574,7 @@ _thr_sched_switch_unlocked(struct pthread *curthread) * a thread can be interrupted by other signals while * it is running down pending signals. */ - sigemptyset(&psf.psf_sigset); + psf.psf_valid = 0; curthread->curframe = &psf; /* @@ -561,6 +583,8 @@ _thr_sched_switch_unlocked(struct pthread *curthread) * o The current thread is dead; it's stack needs to be * cleaned up and it can't be done while operating on * it. + * o The current thread has signals pending, should + * let scheduler install signal trampoline for us. * o There are no runnable threads. * o The next thread to run won't unlock the scheduler * lock. A side note: the current thread may be run @@ -570,8 +594,10 @@ _thr_sched_switch_unlocked(struct pthread *curthread) if ((curthread->state == PS_DEAD) || (((td = KSE_RUNQ_FIRST(curkse)) == NULL) && (curthread->state != PS_RUNNING)) || - ((td != NULL) && (td->lock_switch == 0))) + ((td != NULL) && (td->lock_switch == 0))) { + curkse->k_switch = 1; _thread_enter_uts(&curthread->tmbx, &curkse->k_mbx); + } else { uts_once = 0; THR_GETCONTEXT(&curthread->tmbx.tm_context); @@ -623,6 +649,15 @@ _thr_sched_switch_unlocked(struct pthread *curthread) } } + if (psf.psf_valid) { + /* + * It is ugly we must increase critical count, because we + * have a frame saved, we must backout state in psf + * before we can process signals. + */ + curthread->critical_count++; + } + if (curthread->lock_switch != 0) { /* * Unlock the scheduling queue and leave the @@ -638,11 +673,15 @@ _thr_sched_switch_unlocked(struct pthread *curthread) /* * This thread is being resumed; check for cancellations. */ - if ((resume_once == 0) && (!THR_IN_CRITICAL(curthread))) { - resume_once = 1; - thr_resume_check(curthread, &curthread->tmbx.tm_context, &psf); + if ((psf.psf_valid || curthread->check_pending)) { + resume_once = 0; + THR_GETCONTEXT(&uc); + if (resume_once == 0) { + resume_once = 1; + curthread->check_pending = 0; + thr_resume_check(curthread, &uc, &psf); + } } - THR_ACTIVATE_LAST_LOCK(curthread); } @@ -747,9 +786,6 @@ kse_sched_single(struct kse *curkse) KSE_WAITQ_REMOVE(curkse, td_wait); } } - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Remove the frame reference. */ @@ -810,12 +846,12 @@ kse_sched_multi(struct kse *curkse) KSE_CLEAR_WAIT(curkse); } - /* Lock the scheduling lock. */ - curthread = curkse->k_curthread; - if ((curthread == NULL) || (curthread->need_switchout == 0)) { - /* This is an upcall; take the scheduler lock. */ + /*If this is an upcall; take the scheduler lock. */ + if (curkse->k_switch == 0) KSE_SCHED_LOCK(curkse, curkse->k_kseg); - } + curkse->k_switch = 0; + + curthread = curkse->k_curthread; if (KSE_IS_IDLE(curkse)) { KSE_CLEAR_IDLE(curkse); @@ -879,11 +915,6 @@ kse_sched_multi(struct kse *curkse) kse_wakeup_multi(curkse); - /* This has to be done without the scheduling lock held. */ - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); - #ifdef DEBUG_THREAD_KERN dump_queues(curkse); #endif @@ -899,9 +930,6 @@ kse_sched_multi(struct kse *curkse) kse_wait(curkse, td_wait); kse_check_completed(curkse); kse_check_waitq(curkse); - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Check for no more threads: */ @@ -966,15 +994,19 @@ kse_sched_multi(struct kse *curkse) * signal frame to the thread's context. */ #ifdef NOT_YET - if ((curframe == NULL) && ((curthread->have_signals != 0) || + if ((((curframe == NULL) && (curthread->check_pending != 0)) || (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && - ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)))) + ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))) && + !THR_IN_CRITICAL(curthread)) signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); #else - if ((curframe == NULL) && (curthread->have_signals != 0)) + if ((curframe == NULL) && (curthread->check_pending != 0) && + !THR_IN_CRITICAL(curthread)) { + curthread->check_pending = 0; signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); + } #endif /* * Continue the thread at its current frame: @@ -999,61 +1031,31 @@ kse_sched_multi(struct kse *curkse) } static void -kse_check_signals(struct kse *curkse) -{ - sigset_t sigset; - int i; - - /* Deliver posted signals. */ - for (i = 0; i < _SIG_WORDS; i++) { - atomic_swap_int(&curkse->k_mbx.km_sigscaught.__bits[i], - 0, &sigset.__bits[i]); - } - if (SIGNOTEMPTY(sigset)) { - /* - * Dispatch each signal. - * - * XXX - There is no siginfo for any of these. - * I think there should be, especially for - * signals from other processes (si_pid, si_uid). - */ - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - DBG_MSG("Dispatching signal %d\n", i); - _thr_sig_dispatch(curkse, i, - NULL /* no siginfo */); - } - } - sigemptyset(&sigset); - __sys_sigprocmask(SIG_SETMASK, &sigset, NULL); - } -} - -static void -thr_resume_wrapper(int unused_1, siginfo_t *unused_2, ucontext_t *ucp) +thr_resume_wrapper(int sig, siginfo_t *siginfo, ucontext_t *ucp) { struct pthread *curthread = _get_curthread(); + struct kse *curkse; + int ret; + DBG_MSG(">>> sig wrapper\n"); + if (curthread->lock_switch) + PANIC("thr_resume_wrapper, lock_switch != 0\n"); thr_resume_check(curthread, ucp, NULL); + _kse_critical_enter(); + curkse = _get_curkse(); + curthread->tmbx.tm_context = *ucp; + ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); + if (ret != 0) + PANIC("thr_resume_wrapper: thread has returned " + "from _thread_switch"); + /* THR_SETCONTEXT(ucp); */ /* not work, why ? */ } static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - /* Check signals before cancellations. */ - while (curthread->have_signals != 0) { - /* Clear the pending flag. */ - curthread->have_signals = 0; - - /* - * It's perfectly valid, though not portable, for - * signal handlers to munge their interrupted context - * and expect to return to it. Ensure we use the - * correct context when running down signals. - */ - _thr_sig_rundown(curthread, ucp, psf); - } + _thr_sig_rundown(curthread, ucp, psf); #ifdef NOT_YET if (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && @@ -1124,7 +1126,7 @@ thr_cleanup(struct kse *curkse, struct pthread *thread) THR_GCLIST_ADD(thread); /* Use thread_list_lock */ active_threads--; - if (active_threads == 0) { + if (active_threads == 1) { KSE_LOCK_RELEASE(curkse, &_thread_list_lock); exit(0); } @@ -1203,8 +1205,15 @@ _thr_gc(struct pthread *curthread) KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } - DBG_MSG("Freeing thread %p\n", td); - _thr_free(curthread, td); + /* + * XXX we don't free initial thread, because there might + * have some code referencing initial thread. + */ + if (td != _thr_initial) { + DBG_MSG("Freeing thread %p\n", td); + _thr_free(curthread, td); + } else + DBG_MSG("Initial thread won't be freed\n"); } /* XXX free kse and ksegrp list should be looked as well */ } @@ -1216,7 +1225,6 @@ _thr_gc(struct pthread *curthread) int _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) { - struct kse *curkse; kse_critical_t crit; int ret; @@ -1250,15 +1258,11 @@ _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) /* * This thread needs a new KSE and KSEG. */ - crit = _kse_critical_enter(); - curkse = _get_curkse(); - _ksd_setprivate(&newthread->kse->k_ksd); - newthread->kse->k_flags |= KF_INITIALIZED|KF_STARTED; + newthread->kse->k_flags &= ~KF_INITIALIZED; + newthread->kse->k_flags |= KF_STARTED; ret = kse_create(&newthread->kse->k_mbx, 1); if (ret != 0) ret = errno; - _ksd_setprivate(&curkse->k_ksd); - _kse_critical_leave(crit); } else { /* @@ -1266,6 +1270,7 @@ _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) * assigned threads. If the new thread is runnable, also * add it to the KSE's run queue. */ + crit = _kse_critical_enter(); KSE_SCHED_LOCK(curthread->kse, newthread->kseg); KSEG_THRQ_ADD(newthread->kseg, newthread); if (newthread->state == PS_RUNNING) @@ -1288,6 +1293,7 @@ _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) kse_wakeup_one(newthread); } KSE_SCHED_UNLOCK(curthread->kse, newthread->kseg); + _kse_critical_leave(crit); ret = 0; } if (ret != 0) @@ -1328,6 +1334,7 @@ kse_check_completed(struct kse *kse) { struct pthread *thread; struct kse_thr_mailbox *completed; + int sig; if ((completed = kse->k_mbx.km_completed) != NULL) { kse->k_mbx.km_completed = NULL; @@ -1348,6 +1355,13 @@ kse_check_completed(struct kse *kse) thread->active = 0; } } + if ((sig = thread->tmbx.tm_syncsig.si_signo) != 0) { + if (SIGISMEMBER(thread->sigmask, sig)) + SIGADDSET(thread->sigpend, sig); + else + _thr_sig_add(thread, sig, &thread->tmbx.tm_syncsig); + thread->tmbx.tm_syncsig.si_signo = 0; + } completed = completed->tm_next; } } @@ -1446,6 +1460,8 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) { int level; int i; + int restart; + siginfo_t siginfo; /* * Place the currently running thread into the @@ -1459,15 +1475,25 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) thread->need_switchout = 0; /* This thread must have blocked in the kernel. */ /* thread->slice_usec = -1;*/ /* restart timeslice */ - /* - * XXX - Check for pending signals for this thread to - * see if we need to interrupt it in the kernel. - */ - /* if (thread->check_pending != 0) */ if ((thread->slice_usec != -1) && (thread->attr.sched_policy != SCHED_FIFO)) thread->slice_usec += (thread->tmbx.tm_uticks + thread->tmbx.tm_sticks) * _clock_res_usec; + /* + * Check for pending signals for this thread to + * see if we need to interrupt it in the kernel. + */ + if (thread->check_pending != 0) { + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(thread->sigpend, i) && + !SIGISMEMBER(thread->sigmask, i)) { + restart = _thread_sigact[1 - 1].sa_flags & SA_RESTART; + kse_thr_interrupt(&thread->tmbx, + restart ? -2 : -1); + break; + } + } + } } else { switch (thread->state) { @@ -1507,10 +1533,12 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) THR_SET_STATE(thread, PS_RUNNING); break; + case PS_SIGWAIT: + KSE_WAITQ_INSERT(kse, thread); + break; case PS_JOIN: case PS_MUTEX_WAIT: case PS_SIGSUSPEND: - case PS_SIGWAIT: case PS_SUSPENDED: case PS_DEADLOCK: default: @@ -1564,11 +1592,18 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) if (thread->check_pending != 0) { /* Install pending signals into the frame. */ thread->check_pending = 0; - for (i = 0; i < _SIG_MAXSIG; i++) { - if (sigismember(&thread->sigpend, i) && - !sigismember(&thread->tmbx.tm_context.uc_sigmask, i)) + KSE_LOCK_ACQUIRE(kse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(thread->sigmask, i)) + continue; + if (SIGISMEMBER(thread->sigpend, i)) _thr_sig_add(thread, i, &thread->siginfo[i]); + else if (SIGISMEMBER(_thr_proc_sigpending, i) && + _thr_getprocsig_unlocked(i, &siginfo)) { + _thr_sig_add(thread, i, &siginfo); + } } + KSE_LOCK_RELEASE(kse, &_thread_signal_lock); } } @@ -2016,6 +2051,10 @@ _kse_alloc(struct pthread *curthread) _lockuser_destroy(&kse->k_lockusers[i]); } free(kse); + if (curthread != NULL) { + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } return (NULL); } kse->k_flags = 0; @@ -2044,7 +2083,7 @@ kse_reinit(struct kse *kse) kse->k_kseg = 0; kse->k_schedq = 0; kse->k_locklevel = 0; - sigemptyset(&kse->k_sigmask); + SIGEMPTYSET(kse->k_sigmask); bzero(&kse->k_sigq, sizeof(kse->k_sigq)); kse->k_check_sigq = 0; kse->k_flags = 0; @@ -2053,6 +2092,7 @@ kse_reinit(struct kse *kse) kse->k_error = 0; kse->k_cpu = 0; kse->k_done = 0; + kse->k_switch = 0; } void @@ -2171,10 +2211,12 @@ thr_link(struct pthread *thread) { kse_critical_t crit; struct kse *curkse; + struct pthread *curthread; crit = _kse_critical_enter(); curkse = _get_curkse(); - + curthread = _get_curthread(); + thread->sigmask = curthread->sigmask; KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); /* * Initialize the unique id (which GDB uses to track diff --git a/lib/libkse/thread/thr_kill.c b/lib/libkse/thread/thr_kill.c index 19f34bb..226cb86 100644 --- a/lib/libkse/thread/thr_kill.c +++ b/lib/libkse/thread/thr_kill.c @@ -45,7 +45,7 @@ _pthread_kill(pthread_t pthread, int sig) int ret; /* Check for invalid signal numbers: */ - if (sig < 0 || sig >= NSIG) + if (sig < 0 || sig > _SIG_MAXSIG) /* Invalid signal: */ ret = EINVAL; /* diff --git a/lib/libkse/thread/thr_private.h b/lib/libkse/thread/thr_private.h index 3672025..fefacd1 100644 --- a/lib/libkse/thread/thr_private.h +++ b/lib/libkse/thread/thr_private.h @@ -171,7 +171,7 @@ typedef struct kse_thr_mailbox *kse_critical_t; struct kse_group; -#define MAX_KSE_LOCKLEVEL 3 +#define MAX_KSE_LOCKLEVEL 5 struct kse { struct kse_mailbox k_mbx; /* kernel kse mailbox */ /* -- location and order specific items for gdb -- */ @@ -190,7 +190,7 @@ struct kse { struct lockuser k_lockusers[MAX_KSE_LOCKLEVEL]; int k_locklevel; sigset_t k_sigmask; - struct sigstatus k_sigq[NSIG]; + struct sigstatus k_sigq[_SIG_MAXSIG]; stack_t k_stack; int k_check_sigq; int k_flags; @@ -201,6 +201,7 @@ struct kse { int k_error; /* syscall errno in critical */ int k_cpu; /* CPU ID when bound */ int k_done; /* this KSE is done */ + int k_switch; /* thread switch in UTS */ }; /* @@ -546,8 +547,8 @@ enum pthread_state { union pthread_wait_data { pthread_mutex_t mutex; pthread_cond_t cond; - const sigset_t *sigwait; /* Waiting on a signal in sigwait */ struct lock *lock; + siginfo_t *sigwaitinfo; /* used to save siginfo for sigwaitinfo() */ }; /* @@ -563,6 +564,7 @@ typedef void (*thread_continuation_t) (void *); * state is restored from here. */ struct pthread_sigframe { + int psf_valid; int psf_flags; int psf_interrupted; int psf_signo; @@ -586,7 +588,7 @@ struct pthread_specific_elem { }; -#define MAX_THR_LOCKLEVEL 3 +#define MAX_THR_LOCKLEVEL 5 /* * Thread structure. */ @@ -640,7 +642,7 @@ struct pthread { * Used for tracking delivery of signal handlers. */ struct pthread_sigframe *curframe; - siginfo_t siginfo[NSIG]; + siginfo_t siginfo[_SIG_MAXSIG]; /* * Cancelability flags - the lower 2 bits are used by cancel @@ -657,11 +659,10 @@ struct pthread { * The thread's base and pending signal masks. The active * signal mask is stored in the thread's context (in mailbox). */ + sigset_t oldsigmask; sigset_t sigmask; sigset_t sigpend; - int sigmask_seqno; int check_pending; - int have_signals; int refcount; /* Thread state: */ @@ -997,14 +998,14 @@ SCLASS struct pthread_cond_attr _pthread_condattr_default SCLASS int _clock_res_usec SCLASS_PRESET(CLOCK_RES_USEC); /* Array of signal actions for this process: */ -SCLASS struct sigaction _thread_sigact[NSIG]; +SCLASS struct sigaction _thread_sigact[_SIG_MAXSIG]; /* * Array of counts of dummy handlers for SIG_DFL signals. This is used to * assure that there is always a dummy signal handler installed while there * is a thread sigwait()ing on the corresponding signal. */ -SCLASS int _thread_dfl_count[NSIG]; +SCLASS int _thread_dfl_count[_SIG_MAXSIG]; /* * Lock for above count of dummy handlers and for the process signal @@ -1014,8 +1015,7 @@ SCLASS struct lock _thread_signal_lock; /* Pending signals and mask for this process: */ SCLASS sigset_t _thr_proc_sigpending; -SCLASS sigset_t _thr_proc_sigmask SCLASS_PRESET({{0, 0, 0, 0}}); -SCLASS siginfo_t _thr_proc_siginfo[NSIG]; +SCLASS siginfo_t _thr_proc_siginfo[_SIG_MAXSIG]; SCLASS pid_t _thr_pid SCLASS_PRESET(0); @@ -1030,7 +1030,7 @@ SCLASS struct lock _keytable_lock; SCLASS struct lock _thread_list_lock; SCLASS int _thr_guard_default; SCLASS int _thr_page_size; - +SCLASS pthread_t _thr_sig_daemon; SCLASS int _thr_debug_flags SCLASS_PRESET(0); /* Undefine the storage class and preset specifiers: */ @@ -1116,7 +1116,6 @@ void _thr_panic_exit(char *, int, char *); void _thread_cleanupspecific(void); void _thread_dump_info(void); void _thread_printf(int, const char *, ...); -void _thr_sched_frame(struct pthread_sigframe *); void _thr_sched_switch(struct pthread *); void _thr_sched_switch_unlocked(struct pthread *); void _thr_set_timeout(const struct timespec *); @@ -1126,13 +1125,17 @@ void _thr_sig_check_pending(struct pthread *); void _thr_sig_rundown(struct pthread *, ucontext_t *, struct pthread_sigframe *); void _thr_sig_send(struct pthread *pthread, int sig); -void _thr_sig_wrapper(void); void _thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); void _thr_spinlock_init(void); void _thr_enter_cancellation_point(struct pthread *); void _thr_leave_cancellation_point(struct pthread *); int _thr_setconcurrency(int new_level); int _thr_setmaxconcurrency(void); +int _thr_start_sig_daemon(void); +int _thr_getprocsig(int sig, siginfo_t *siginfo); +int _thr_getprocsig_unlocked(int sig, siginfo_t *siginfo); +void _thr_signal_init(void); +void _thr_signal_deinit(void); /* * Aliases for _pthread functions. Should be called instead of @@ -1216,6 +1219,8 @@ int __sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); ssize_t __sys_read(int, void *, size_t); ssize_t __sys_write(int, const void *, size_t); void __sys_exit(int); +int __sys_sigwait(const sigset_t *, int *); +int __sys_sigtimedwait(sigset_t *, siginfo_t *, struct timespec *); #endif /* #include <poll.h> */ diff --git a/lib/libkse/thread/thr_sig.c b/lib/libkse/thread/thr_sig.c index 94e5630..d06db9d 100644 --- a/lib/libkse/thread/thr_sig.c +++ b/lib/libkse/thread/thr_sig.c @@ -45,19 +45,15 @@ /* Prototypes: */ static void build_siginfo(siginfo_t *info, int signo); -/* static void thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info); */ static void thr_sig_check_state(struct pthread *pthread, int sig); static struct pthread *thr_sig_find(struct kse *curkse, int sig, siginfo_t *info); static void handle_special_signals(struct kse *curkse, int sig); -static void thr_sigframe_add(struct pthread *thread, int sig, - siginfo_t *info); +static void thr_sigframe_add(struct pthread *thread); static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); static void thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf); -static void thr_sig_invoke_handler(struct pthread *, int sig, - siginfo_t *info, ucontext_t *ucp); /* #define DEBUG_SIGNAL */ #ifdef DEBUG_SIGNAL @@ -137,6 +133,65 @@ static void thr_sig_invoke_handler(struct pthread *, int sig, * signal unmasked. */ +static void * +sig_daemon(void *arg /* Unused */) +{ + int i; + kse_critical_t crit; + struct timespec ts; + sigset_t set; + struct kse *curkse; + struct pthread *curthread = _get_curthread(); + + DBG_MSG("signal daemon started"); + + curthread->name = strdup("signal thread"); + crit = _kse_critical_enter(); + curkse = _get_curkse(); + SIGFILLSET(set); + __sys_sigprocmask(SIG_SETMASK, &set, NULL); + __sys_sigpending(&set); + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (1) { + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + _thr_proc_sigpending = set; + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(set, i) != 0) + _thr_sig_dispatch(curkse, i, + NULL /* no siginfo */); + } + ts.tv_sec = 30; + ts.tv_nsec = 0; + curkse->k_mbx.km_flags = + KMF_NOUPCALL | KMF_NOCOMPLETED | KMF_WAITSIGEVENT; + kse_release(&ts); + curkse->k_mbx.km_flags = 0; + set = curkse->k_mbx.km_sigscaught; + } + return (0); +} + +/* Utility function to create signal daemon thread */ +int +_thr_start_sig_daemon(void) +{ + pthread_attr_t attr; + sigset_t sigset, oldset; + + SIGFILLSET(sigset); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + /* sigmask will be inherited */ + if (pthread_create(&_thr_sig_daemon, &attr, sig_daemon, NULL)) + PANIC("can not create signal daemon thread!\n"); + pthread_attr_destroy(&attr); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return (0); +} + /* * This signal handler only delivers asynchronous signals. * This must be called with upcalls disabled and without @@ -151,7 +206,6 @@ _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) /* Some signals need special handling: */ handle_special_signals(curkse, sig); - DBG_MSG("dispatch sig:%d\n", sig); while ((thread = thr_sig_find(curkse, sig, info)) != NULL) { /* * Setup the target thread to receive the signal: @@ -163,18 +217,17 @@ _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) THR_IS_EXITING(thread) || THR_IS_SUSPENDED(thread)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } else if (sigismember(&thread->tmbx.tm_context.uc_sigmask, - sig)) { + } else if (SIGISMEMBER(thread->sigmask, sig)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } - else { + } else { _thr_sig_add(thread, sig, info); KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); break; } } + DBG_MSG("<<< _thr_sig_dispatch\n"); } void @@ -187,7 +240,6 @@ _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) if ((curkse == NULL) || ((curkse->k_flags & KF_STARTED) == 0)) { /* Upcalls are not yet started; just call the handler. */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN) && (sigfunc != (__siginfohandler_t *)_thr_sig_handler)) { @@ -202,68 +254,119 @@ _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) else { /* Nothing. */ DBG_MSG("Got signal %d\n", sig); - sigaddset(&curkse->k_mbx.km_sigscaught, sig); - ucp->uc_sigmask = _thr_proc_sigmask; + /* XXX Bound thread will fall into this... */ } } +/* Must be called with signal lock and schedule lock held in order */ static void thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, ucontext_t *ucp) { void (*sigfunc)(int, siginfo_t *, void *); - sigset_t saved_mask; - int saved_seqno; + sigset_t sigmask; + int sa_flags; + struct sigaction act; + struct kse *curkse; - /* Invoke the signal handler without going through the scheduler: + /* + * Invoke the signal handler without going through the scheduler: */ DBG_MSG("Got signal %d, calling handler for current thread %p\n", sig, curthread); - /* - * Setup the threads signal mask. - * - * The mask is changed in the thread's active signal mask - * (in the context) and not in the base signal mask because - * a thread is allowed to change its signal mask within a - * signal handler. If it does, the signal mask restored - * after the handler should be the same as that set by the - * thread during the handler, not the original mask from - * before calling the handler. The thread could also - * modify the signal mask in the context and expect this - * mask to be used. - */ - THR_SCHED_LOCK(curthread, curthread); - saved_mask = curthread->tmbx.tm_context.uc_sigmask; - saved_seqno = curthread->sigmask_seqno; - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&curthread->tmbx.tm_context.uc_sigmask, sig); - THR_SCHED_UNLOCK(curthread, curthread); - + if (!_kse_in_critical()) + PANIC("thr_sig_invoke_handler without in critical\n"); + curkse = _get_curkse(); /* * Check that a custom handler is installed and if * the signal is not blocked: */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; + sa_flags = _thread_sigact[sig - 1].sa_flags & SA_SIGINFO; + sigmask = curthread->sigmask; + SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask); + if (!(sa_flags & (SA_NODEFER | SA_RESETHAND))) + SIGADDSET(curthread->sigmask, sig); + if ((sig != SIGILL) && (sa_flags & SA_RESETHAND)) { + if (_thread_dfl_count[sig - 1] == 0) { + act.sa_handler = SIG_DFL; + act.sa_flags = SA_RESTART; + SIGEMPTYSET(act.sa_mask); + __sys_sigaction(sig, &act, NULL); + __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]); + } + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + ucp->uc_sigmask = sigmask; + if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN)) { - if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || - (info == NULL)) + if ((sa_flags & SA_SIGINFO) != 0 || info == NULL) (*(sigfunc))(sig, info, ucp); else (*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code, ucp); + } else { + /* XXX + * TODO: exit process if signal would kill it. + */ +#ifdef NOTYET + if (sigprop(sig) & SA_KILL) + kse_sigexit(sig); +#endif } - + _kse_critical_enter(); + /* Don't trust after critical leave/enter */ + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); /* * Restore the thread's signal mask. */ - if (saved_seqno == curthread->sigmask_seqno) - curthread->tmbx.tm_context.uc_sigmask = saved_mask; - else - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; + curthread->sigmask = ucp->uc_sigmask; + + DBG_MSG("Got signal %d, handler returned %p\n", sig, curthread); +} + +int +_thr_getprocsig(int sig, siginfo_t *siginfo) +{ + kse_critical_t crit; + struct kse *curkse; + int ret; + + DBG_MSG(">>> _thr_getprocsig\n"); + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + ret = _thr_getprocsig_unlocked(sig, siginfo); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + _kse_critical_leave(crit); + + DBG_MSG("<<< _thr_getprocsig\n"); + return (ret); +} + +int +_thr_getprocsig_unlocked(int sig, siginfo_t *siginfo) +{ + sigset_t sigset; + struct timespec ts; + + /* try to retrieve signal from kernel */ + SIGEMPTYSET(sigset); + SIGADDSET(sigset, sig); + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (__sys_sigtimedwait(&sigset, siginfo, &ts) > 0) { + SIGDELSET(_thr_proc_sigpending, sig); + return (sig); + } + return (0); } /* @@ -273,15 +376,12 @@ thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, struct pthread * thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) { - int handler_installed; struct pthread *pthread; struct pthread *suspended_thread, *signaled_thread; + siginfo_t si; DBG_MSG("Looking for thread to handle signal %d\n", sig); - handler_installed = (_thread_sigact[sig - 1].sa_handler != SIG_IGN) && - (_thread_sigact[sig - 1].sa_handler != SIG_DFL); - /* Check if the signal requires a dump of thread information: */ if (sig == SIGINFO) { /* Dump thread information to file: */ @@ -302,19 +402,39 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (pthread == _thr_sig_daemon) + continue; +#ifdef NOTYET + /* Signal delivering to bound thread is done by kernel */ + if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + continue; +#endif + /* Take the scheduling lock. */ KSE_SCHED_LOCK(curkse, pthread->kseg); - if ((pthread->state == PS_SIGWAIT) && - sigismember(pthread->data.sigwait, sig)) { + if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + THR_IS_EXITING(pthread) || + THR_IS_SUSPENDED(pthread) || + SIGISMEMBER(pthread->sigmask, sig)) { + ; /* Skip this thread. */ + } else if (pthread->state == PS_SIGWAIT) { /* - * Return the signal number and make the - * thread runnable. + * retrieve signal from kernel, if it is job control + * signal, and sigaction is SIG_DFL, then we will + * be stopped in kernel, we hold lock here, but that + * does not matter, because that's job control, and + * whole process should be stopped. */ - pthread->signo = sig; - _thr_setrunnable_unlocked(pthread); - + if (_thr_getprocsig(sig, &si)) { + DBG_MSG("Waking thread %p in sigwait" + " with signal %d\n", pthread, sig); + /* where to put siginfo ? */ + *(pthread->data.sigwaitinfo) = si; + pthread->sigmask = pthread->oldsigmask; + _thr_setrunnable_unlocked(pthread); + } KSE_SCHED_UNLOCK(curkse, pthread->kseg); - /* * POSIX doesn't doesn't specify which thread * will get the signal if there are multiple @@ -326,16 +446,8 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) * to the process pending set. */ KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - DBG_MSG("Waking thread %p in sigwait with signal %d\n", - pthread, sig); return (NULL); - } - else if ((pthread->state == PS_DEAD) || - (pthread->state == PS_DEADLOCK) || - THR_IS_EXITING(pthread) || THR_IS_SUSPENDED(pthread)) - ; /* Skip this thread. */ - else if ((handler_installed != 0) && - !sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { + } else { if (pthread->state == PS_SIGSUSPEND) { if (suspended_thread == NULL) { suspended_thread = pthread; @@ -344,49 +456,22 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) } else if (signaled_thread == NULL) { signaled_thread = pthread; signaled_thread->refcount++; - } + } } KSE_SCHED_UNLOCK(curkse, pthread->kseg); } KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - /* - * Only perform wakeups and signal delivery if there is a - * custom handler installed: - */ - if (handler_installed == 0) { - /* - * There is no handler installed; nothing to do here. - */ - } else if (suspended_thread == NULL && - signaled_thread == NULL) { - /* - * Add it to the set of signals pending - * on the process: - */ - KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - if (!sigismember(&_thr_proc_sigpending, sig)) { - sigaddset(&_thr_proc_sigpending, sig); - if (info == NULL) - build_siginfo(&_thr_proc_siginfo[sig], sig); - else - memcpy(&_thr_proc_siginfo[sig], info, - sizeof(*info)); - } - KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); - } else { - /* - * We only deliver the signal to one thread; - * give preference to the suspended thread: - */ - if (suspended_thread != NULL) { - pthread = suspended_thread; + if (suspended_thread != NULL) { + pthread = suspended_thread; + if (signaled_thread) _thr_ref_delete(NULL, signaled_thread); - } else - pthread = signaled_thread; - return (pthread); + } else if (signaled_thread) { + pthread = signaled_thread; + } else { + pthread = NULL; } - return (NULL); + return (pthread); } static void @@ -405,64 +490,80 @@ void _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - struct pthread_sigframe psf_save; - sigset_t sigset; + siginfo_t siginfo; int i; + kse_critical_t crit; + struct kse *curkse; - THR_SCHED_LOCK(curthread, curthread); - memcpy(&sigset, &curthread->sigpend, sizeof(sigset)); - sigemptyset(&curthread->sigpend); - if (psf != NULL) { - memcpy(&psf_save, psf, sizeof(*psf)); - SIGSETOR(sigset, psf_save.psf_sigset); - sigemptyset(&psf->psf_sigset); - } - THR_SCHED_UNLOCK(curthread, curthread); - + DBG_MSG(">>> thr_sig_rundown %p\n", curthread); /* Check the threads previous state: */ - if ((psf != NULL) && (psf_save.psf_state != PS_RUNNING)) { + if ((psf != NULL) && (psf->psf_valid != 0)) { /* * Do a little cleanup handling for those threads in * queues before calling the signal handler. Signals * for these threads are temporarily blocked until * after cleanup handling. */ - switch (psf_save.psf_state) { + switch (psf->psf_state) { case PS_COND_WAIT: _cond_wait_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; case PS_MUTEX_WAIT: _mutex_lock_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; + case PS_RUNNING: + break; + default: + psf->psf_state = PS_RUNNING; break; } + /* XXX see comment in thr_sched_switch_unlocked */ + curthread->critical_count--; } + /* * Lower the priority before calling the handler in case * it never returns (longjmps back): */ + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); curthread->active_priority &= ~THR_SIGNAL_PRIORITY; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], ucp); + while (1) { + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(curthread->sigmask, i)) + continue; + if (SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + siginfo = curthread->siginfo[i]; + break; + } + if (SIGISMEMBER(_thr_proc_sigpending, i)) { + if (_thr_getprocsig_unlocked(i, &siginfo)) + break; + } } + if (i <= _SIG_MAXSIG) + thr_sig_invoke_handler(curthread, i, &siginfo, ucp); + else + break; } - THR_SCHED_LOCK(curthread, curthread); - if (psf != NULL) - thr_sigframe_restore(curthread, &psf_save); - /* Restore the signal mask. */ - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; - THR_SCHED_UNLOCK(curthread, curthread); - _thr_sig_check_pending(curthread); + if (psf != NULL && psf->psf_valid != 0) + thr_sigframe_restore(curthread, psf); + curkse = _get_curkse(); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + + DBG_MSG("<<< thr_sig_rundown %p\n", curthread); } /* @@ -477,87 +578,19 @@ _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, void _thr_sig_check_pending(struct pthread *curthread) { - sigset_t sigset; - sigset_t pending_process; - sigset_t pending_thread; - kse_critical_t crit; - int i; - - curthread->check_pending = 0; - - /* - * Check if there are pending signals for the running - * thread or process that aren't blocked: - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - sigset = _thr_proc_sigpending; - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); + ucontext_t uc; + volatile int once; - THR_SCHED_LOCK(curthread, curthread); - SIGSETOR(sigset, curthread->sigpend); - SIGSETNAND(sigset, curthread->tmbx.tm_context.uc_sigmask); - if (SIGNOTEMPTY(sigset)) { - ucontext_t uc; - volatile int once; + if (THR_IN_CRITICAL(curthread)) + return; + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; curthread->check_pending = 0; - THR_SCHED_UNLOCK(curthread, curthread); - - /* - * Split the pending signals into those that were - * pending on the process and those that were pending - * on the thread. - */ - sigfillset(&pending_process); - sigfillset(&pending_thread); - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - if (sigismember(&curthread->sigpend, i) != 0) { - build_siginfo(&curthread->siginfo[i], i); - sigdelset(&pending_thread, i); - } else { - memcpy(&curthread->siginfo[i], - &_thr_proc_siginfo[i], - sizeof(siginfo_t)); - sigdelset(&pending_process, i); - } - } - } - /* - * Remove any process pending signals that were scheduled - * to be delivered from process' pending set. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETAND(_thr_proc_sigpending, pending_process); - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - - /* - * Remove any thread pending signals that were scheduled - * to be delivered from thread's pending set. - */ - THR_SCHED_LOCK(curthread, curthread); - SIGSETAND(curthread->sigpend, pending_thread); - THR_SCHED_UNLOCK(curthread, curthread); - - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], &uc); - } - } - } + _thr_sig_rundown(curthread, &uc, NULL); } - else - THR_SCHED_UNLOCK(curthread, curthread); } /* @@ -575,10 +608,15 @@ handle_special_signals(struct kse *curkse, int sig) case SIGTTIN: case SIGTTOU: KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - sigdelset(&_thr_proc_sigpending, SIGCONT); + SIGDELSET(_thr_proc_sigpending, SIGCONT); KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); break; - + case SIGCONT: + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + SIGDELSET(_thr_proc_sigpending, SIGTSTP); + SIGDELSET(_thr_proc_sigpending, SIGTTIN); + SIGDELSET(_thr_proc_sigpending, SIGTTOU); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); default: break; } @@ -597,72 +635,92 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) { int restart; int suppress_handler = 0; + int fromproc = 0; + struct pthread *curthread = _get_curthread(); + struct kse *curkse; + siginfo_t siginfo; - if (pthread->curframe == NULL) { - /* - * This thread is active. Just add it to the - * thread's pending set. - */ - sigaddset(&pthread->sigpend, sig); - pthread->check_pending = 1; - if (info == NULL) - build_siginfo(&pthread->siginfo[sig], sig); - else if (info != &pthread->siginfo[sig]) - memcpy(&pthread->siginfo[sig], info, - sizeof(*info)); - if ((pthread->blocked != 0) && !THR_IN_CRITICAL(pthread)) - kse_thr_interrupt(&pthread->tmbx /* XXX - restart?!?! */); - } - else { - restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + DBG_MSG(">>> _thr_sig_add\n"); - /* Make sure this signal isn't still in the pending set: */ - sigdelset(&pthread->sigpend, sig); + curkse = _get_curkse(); + restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + fromproc = (curthread == _thr_sig_daemon); + + if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK || + pthread->state == PS_STATE_MAX) + return; /* return false */ + +#ifdef NOTYET + if ((pthread->attrs.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + if (!fromproc) + kse_thr_interrupt(&pthread->tmbx, 0, sig); + return; + } +#endif + if (pthread->curframe == NULL || + SIGISMEMBER(pthread->sigmask, sig) || + THR_IN_CRITICAL(pthread)) { + /* thread is running or signal was being masked */ + if (!fromproc) { + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); + } else { + if (!_thr_getprocsig(sig, &pthread->siginfo[sig])) + return; + SIGADDSET(pthread->sigpend, sig); + } + if (!SIGISMEMBER(pthread->sigmask, sig)) { + pthread->check_pending = 1; + if (pthread->blocked != 0 && !THR_IN_CRITICAL(pthread)) + kse_thr_interrupt(&pthread->tmbx, + restart ? -2 : -1); + } + } + else { + /* if process signal not exists, just return */ + if (fromproc) { + if (!_thr_getprocsig(sig, &siginfo)) + return; + info = &siginfo; + } /* * Process according to thread state: */ switch (pthread->state) { - /* - * States which do not change when a signal is trapped: - */ case PS_DEAD: case PS_DEADLOCK: + case PS_STATE_MAX: + return; /* XXX return false */ case PS_LOCKWAIT: case PS_SUSPENDED: - case PS_STATE_MAX: /* * You can't call a signal handler for threads in these * states. */ suppress_handler = 1; break; - - /* - * States which do not need any cleanup handling when signals - * occur: - */ case PS_RUNNING: - /* - * Remove the thread from the queue before changing its - * priority: - */ - if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0) + if ((pthread->flags & THR_FLAGS_IN_RUNQ)) { THR_RUNQ_REMOVE(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + THR_RUNQ_INSERT_TAIL(pthread); + } else { + /* Possible not in RUNQ and has curframe ? */ + pthread->active_priority |= THR_SIGNAL_PRIORITY; + } + suppress_handler = 1; break; - /* * States which cannot be interrupted but still require the * signal handler to run: */ case PS_COND_WAIT: case PS_MUTEX_WAIT: - /* - * Remove the thread from the wait queue. It will - * be added back to the wait queue once all signal - * handlers have been invoked. - */ - KSE_WAITQ_REMOVE(pthread->kse, pthread); break; case PS_SLEEP_WAIT: @@ -671,60 +729,64 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) * early regardless of SA_RESTART: */ pthread->interrupted = 1; - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); break; case PS_JOIN: + break; + case PS_SIGSUSPEND: - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); + pthread->interrupted = 1; break; case PS_SIGWAIT: + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); /* * The signal handler is not called for threads in * SIGWAIT. */ suppress_handler = 1; - /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + /* Wake up the thread if the signal is not blocked. */ + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Make the thread runnable: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); - break; + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } + + return; } + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, sizeof(*info)); + if (suppress_handler == 0) { /* * Setup a signal frame and save the current threads * state: */ - thr_sigframe_add(pthread, sig, info); - - if (pthread->state != PS_RUNNING) - THR_SET_STATE(pthread, PS_RUNNING); - - /* - * The thread should be removed from all scheduling - * queues at this point. Raise the priority and - * place the thread in the run queue. It is also - * possible for a signal to be sent to a suspended - * thread, mostly via pthread_kill(). If a thread - * is suspended, don't insert it into the priority - * queue; just set its state to suspended and it - * will run the signal handler when it is resumed. - */ + thr_sigframe_add(pthread); + if (pthread->flags & THR_FLAGS_IN_RUNQ) + THR_RUNQ_REMOVE(pthread); pthread->active_priority |= THR_SIGNAL_PRIORITY; - if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0) - THR_RUNQ_INSERT_TAIL(pthread); + _thr_setrunnable_unlocked(pthread); + } else { + pthread->check_pending = 1; } } + + DBG_MSG("<<< _thr_sig_add\n"); } static void @@ -749,16 +811,19 @@ thr_sig_check_state(struct pthread *pthread, int sig) break; case PS_SIGWAIT: + build_siginfo(&pthread->siginfo[sig], sig); /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Change the state of the thread to run: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } break; case PS_SIGSUSPEND: @@ -783,6 +848,12 @@ _thr_sig_send(struct pthread *pthread, int sig) { struct pthread *curthread = _get_curthread(); +#ifdef NOTYET + if ((pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) { + kse_thr_interrupt(&pthread->tmbx, sig); + return; + } +#endif /* Lock the scheduling queue of the target thread. */ THR_SCHED_LOCK(curthread, pthread); @@ -792,7 +863,7 @@ _thr_sig_send(struct pthread *pthread, int sig) * Check to see if a temporary signal handler is * installed for sigwaiters: */ - if (_thread_dfl_count[sig] == 0) { + if (_thread_dfl_count[sig - 1] == 0) { /* * Deliver the signal to the process if a handler * is not installed: @@ -812,87 +883,47 @@ _thr_sig_send(struct pthread *pthread, int sig) * Check that the signal is not being ignored: */ else if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { - if (pthread->state == PS_SIGWAIT && - sigismember(pthread->data.sigwait, sig)) { - /* Return the signal number: */ - pthread->signo = sig; - - /* Change the state of the thread to run: */ - _thr_setrunnable_unlocked(pthread); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { - /* Add the signal to the pending set: */ - sigaddset(&pthread->sigpend, sig); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (pthread == curthread) { - ucontext_t uc; - siginfo_t info; - volatile int once; - - THR_SCHED_UNLOCK(curthread, pthread); - build_siginfo(&info, sig); - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - /* - * Call the signal handler for the current - * thread: - */ - thr_sig_invoke_handler(curthread, sig, - &info, &uc); - } - } else { - /* - * Perform any state changes due to signal - * arrival: - */ - _thr_sig_add(pthread, sig, NULL); - THR_SCHED_UNLOCK(curthread, pthread); - } + _thr_sig_add(pthread, sig, NULL); + THR_SCHED_UNLOCK(curthread, pthread); + /* XXX + * If thread sent signal to itself, check signals now. + * It is not really needed, _kse_critical_leave should + * have already checked signals. + */ + if (pthread == curthread && curthread->check_pending) + _thr_sig_check_pending(curthread); + } else { + THR_SCHED_UNLOCK(curthread, pthread); } } static void -thr_sigframe_add(struct pthread *thread, int sig, siginfo_t *info) +thr_sigframe_add(struct pthread *thread) { if (thread->curframe == NULL) PANIC("Thread doesn't have signal frame "); - if (thread->have_signals == 0) { + if (thread->curframe->psf_valid == 0) { + thread->curframe->psf_valid = 1; /* * Multiple signals can be added to the same signal * frame. Only save the thread's state the first time. */ thr_sigframe_save(thread, thread->curframe); - thread->have_signals = 1; - thread->flags &= THR_FLAGS_PRIVATE; } - sigaddset(&thread->curframe->psf_sigset, sig); - if (info == NULL) - build_siginfo(&thread->siginfo[sig], sig); - else if (info != &thread->siginfo[sig]) - memcpy(&thread->siginfo[sig], info, sizeof(*info)); - - /* Setup the new signal mask. */ - SIGSETOR(thread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&thread->tmbx.tm_context.uc_sigmask, sig); } -void +static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf) { + if (psf->psf_valid == 0) + PANIC("invalid pthread_sigframe\n"); thread->flags = psf->psf_flags; thread->interrupted = psf->psf_interrupted; thread->signo = psf->psf_signo; thread->state = psf->psf_state; thread->data = psf->psf_wait_data; thread->wakeup_time = psf->psf_wakeup_time; - if (thread->sigmask_seqno == psf->psf_seqno) - thread->tmbx.tm_context.uc_sigmask = psf->psf_sigmask; - else - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; } static void @@ -906,7 +937,74 @@ thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf) psf->psf_state = thread->state; psf->psf_wait_data = thread->data; psf->psf_wakeup_time = thread->wakeup_time; - psf->psf_sigmask = thread->tmbx.tm_context.uc_sigmask; - psf->psf_seqno = thread->sigmask_seqno; - sigemptyset(&psf->psf_sigset); } + +void +_thr_signal_init(void) +{ + sigset_t sigset; + struct sigaction act; + int i; + + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Get the signal handler details: */ + else if (__sys_sigaction(i, NULL, + &_thread_sigact[i - 1]) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot read signal handler info"); + } + } + /* + * Install the signal handler for SIGINFO. It isn't + * really needed, but it is nice to have for debugging + * purposes. + */ + _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; + SIGEMPTYSET(act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = (__siginfohandler_t *)&_thr_sig_handler; + if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { + /* + * Abort this process if signal initialisation fails: + */ + PANIC("Cannot initialize signal handler"); + } +} + +void +_thr_signal_deinit(void) +{ + sigset_t tmpmask, oldmask; + int i; + + SIGFILLSET(tmpmask); + SIG_CANTMASK(tmpmask); + __sys_sigprocmask(SIG_SETMASK, &tmpmask, &oldmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Set the signal handler details: */ + else if (__sys_sigaction(i, &_thread_sigact[i - 1], NULL) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot set signal handler info"); + } + } + __sys_sigprocmask(SIG_SETMASK, &oldmask, NULL); +} + diff --git a/lib/libkse/thread/thr_sigaction.c b/lib/libkse/thread/thr_sigaction.c index 7ede6d2..2bef7f2 100644 --- a/lib/libkse/thread/thr_sigaction.c +++ b/lib/libkse/thread/thr_sigaction.c @@ -43,16 +43,21 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { int ret = 0; struct sigaction gact; + struct pthread *curthread; + kse_critical_t crit; /* Check if the signal number is out of range: */ - if (sig < 1 || sig > NSIG) { + if (sig < 1 || sig > _SIG_MAXSIG) { /* Return an invalid argument: */ errno = EINVAL; ret = -1; } else { - if (_thr_initial == NULL) - _libpthread_init(NULL); + if (!_kse_isthreaded()) + return __sys_sigaction(sig, act, oact); + crit = _kse_critical_enter(); + curthread = _get_curthread(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); /* * Check if the existing signal action structure contents are * to be returned: @@ -99,6 +104,8 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) if (__sys_sigaction(sig, &gact, NULL) != 0) ret = -1; } + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); } /* Return the completion status: */ diff --git a/lib/libkse/thread/thr_sigmask.c b/lib/libkse/thread/thr_sigmask.c index d9cb839..c8fcec8 100644 --- a/lib/libkse/thread/thr_sigmask.c +++ b/lib/libkse/thread/thr_sigmask.c @@ -48,33 +48,35 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) struct pthread *curthread = _get_curthread(); int ret; + if (! _kse_isthreaded()) + _kse_setthreaded(1); + + THR_SCHED_LOCK(curthread, curthread); ret = 0; if (oset != NULL) /* Return the current mask: */ - *oset = curthread->tmbx.tm_context.uc_sigmask; + *oset = curthread->sigmask; /* Check if a new signal set was provided by the caller: */ if (set != NULL) { - THR_SCHED_LOCK(curthread, curthread); - /* Process according to what to do: */ switch (how) { /* Block signals: */ case SIG_BLOCK: /* Add signals to the existing mask: */ - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETOR(curthread->sigmask, *set); break; /* Unblock signals: */ case SIG_UNBLOCK: /* Clear signals from the existing mask: */ - SIGSETNAND(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETNAND(curthread->sigmask, *set); break; /* Set the signal process mask: */ case SIG_SETMASK: /* Set the new mask: */ - curthread->tmbx.tm_context.uc_sigmask = *set; + curthread->sigmask = *set; break; /* Trap invalid actions: */ @@ -84,13 +86,7 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) ret = -1; break; } - - if (ret == 0) { - curthread->sigmask = - curthread->tmbx.tm_context.uc_sigmask; - curthread->sigmask_seqno++; - } - + SIG_CANTMASK(curthread->sigmask); THR_SCHED_UNLOCK(curthread, curthread); /* @@ -98,6 +94,7 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) */ if (ret == 0) _thr_sig_check_pending(curthread); - } + } else + THR_SCHED_UNLOCK(curthread, curthread); return (ret); } diff --git a/lib/libkse/thread/thr_sigpending.c b/lib/libkse/thread/thr_sigpending.c index 7f42ff3..1b9b502 100644 --- a/lib/libkse/thread/thr_sigpending.c +++ b/lib/libkse/thread/thr_sigpending.c @@ -54,8 +54,13 @@ _sigpending(sigset_t *set) ret = EINVAL; } else { - *set = curthread->sigpend; + if (!_kse_isthreaded()) + return __sys_sigpending(set); + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + *set = curthread->sigpend; + KSE_SCHED_UNLOCK(curthread->kse, curthread->kseg); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); SIGSETOR(*set, _thr_proc_sigpending); KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); diff --git a/lib/libkse/thread/thr_sigprocmask.c b/lib/libkse/thread/thr_sigprocmask.c index 9cb493a..262848a 100644 --- a/lib/libkse/thread/thr_sigprocmask.c +++ b/lib/libkse/thread/thr_sigprocmask.c @@ -46,8 +46,9 @@ _sigprocmask(int how, const sigset_t *set, sigset_t *oset) { int ret; - ret = pthread_sigmask(how, set, oset); - if ((ret == 0) && (_kse_isthreaded() == 0)) + if (_kse_isthreaded() == 0) ret = __sys_sigprocmask(how, set, oset); + else + ret = pthread_sigmask(how, set, oset); return (ret); } diff --git a/lib/libkse/thread/thr_sigsuspend.c b/lib/libkse/thread/thr_sigsuspend.c index 2da790d..8d087f5 100644 --- a/lib/libkse/thread/thr_sigsuspend.c +++ b/lib/libkse/thread/thr_sigsuspend.c @@ -44,14 +44,19 @@ _sigsuspend(const sigset_t *set) { struct pthread *curthread = _get_curthread(); int ret = -1; + sigset_t osigmask; + + if (!_kse_isthreaded()) + return __sys_sigsuspend(set); /* Check if a new signal set was provided by the caller: */ if (set != NULL) { THR_LOCK_SWITCH(curthread); + /* Save current sigmask */ + memcpy(&osigmask, &curthread->sigmask, sizeof(osigmask)); /* Change the caller's mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - set, sizeof(sigset_t)); + memcpy(&curthread->sigmask, set, sizeof(sigset_t)); THR_SET_STATE(curthread, PS_SIGSUSPEND); @@ -61,9 +66,15 @@ _sigsuspend(const sigset_t *set) /* Always return an interrupted error: */ errno = EINTR; + THR_SCHED_LOCK(curthread, curthread); /* Restore the signal mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - &curthread->sigmask, sizeof(sigset_t)); + memcpy(&curthread->sigmask, &osigmask, sizeof(sigset_t)); + THR_SCHED_UNLOCK(curthread, curthread); + /* + * signal mask is reloaded, need to check if there is + * pending proc signal I can handle. + */ + _thr_sig_check_pending(curthread); } else { /* Return an invalid argument error: */ errno = EINVAL; diff --git a/lib/libkse/thread/thr_sigwait.c b/lib/libkse/thread/thr_sigwait.c index c8c7762..4b9cb69 100644 --- a/lib/libkse/thread/thr_sigwait.c +++ b/lib/libkse/thread/thr_sigwait.c @@ -39,10 +39,13 @@ #include <pthread.h> #include "thr_private.h" -__weak_reference(_sigwait, sigwait); +__weak_reference(__sigwait, sigwait); +__weak_reference(__sigtimedwait, sigtimedwait); +__weak_reference(__sigwaitinfo, sigwaitinfo); -int -_sigwait(const sigset_t *set, int *sig) +static int +lib_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) { struct pthread *curthread = _get_curthread(); int ret = 0; @@ -50,8 +53,14 @@ _sigwait(const sigset_t *set, int *sig) sigset_t tempset, waitset; struct sigaction act; kse_critical_t crit; + siginfo_t siginfo; - _thr_enter_cancellation_point(curthread); + if (!_kse_isthreaded()) { + if (info == NULL) + info = &siginfo; + return __sys_sigtimedwait((sigset_t *)set, info, + (struct timespec *)timeout); + } /* * Specify the thread kernel signal handler. @@ -59,7 +68,7 @@ _sigwait(const sigset_t *set, int *sig) act.sa_handler = (void (*) ()) _thr_sig_handler; act.sa_flags = SA_RESTART | SA_SIGINFO; /* Ensure the signal handler cannot be interrupted by other signals: */ - sigfillset(&act.sa_mask); + SIGFILLSET(act.sa_mask); /* * Initialize the set of signals that will be waited on: @@ -67,41 +76,14 @@ _sigwait(const sigset_t *set, int *sig) waitset = *set; /* These signals can't be waited on. */ - sigdelset(&waitset, SIGKILL); - sigdelset(&waitset, SIGSTOP); + SIGDELSET(waitset, SIGKILL); + SIGDELSET(waitset, SIGSTOP); - /* - * Check to see if a pending signal is in the wait mask. - * This has to be atomic. - */ - tempset = curthread->sigpend; crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETOR(tempset, _thr_proc_sigpending); - SIGSETAND(tempset, waitset); - if (SIGNOTEMPTY(tempset)) { - /* Enter a loop to find a pending signal: */ - for (i = 1; i < NSIG; i++) { - if (sigismember (&tempset, i)) - break; - } - - /* Clear the pending signal: */ - if (sigismember(&curthread->sigpend, i)) - sigdelset(&curthread->sigpend, i); - else - sigdelset(&_thr_proc_sigpending, i); - - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - _thr_leave_cancellation_point(curthread); - /* Return the signal number to the caller: */ - *sig = i; - return (0); - } /* - * Enter a loop to find the signals that are SIG_DFL. For + * Enter a loop to find the signals that are SIG_DFL. For * these signals we must install a dummy signal handler in * order for the kernel to pass them in to us. POSIX says * that the _application_ must explicitly install a dummy @@ -110,66 +92,158 @@ _sigwait(const sigset_t *set, int *sig) * mask because a subsequent sigaction could enable an * ignored signal. */ - sigemptyset(&tempset); - for (i = 1; i < NSIG; i++) { - if (sigismember(&waitset, i) && + SIGEMPTYSET(tempset); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(waitset, i) && (_thread_sigact[i - 1].sa_handler == SIG_DFL)) { - _thread_dfl_count[i]++; - sigaddset(&tempset, i); - if (_thread_dfl_count[i] == 1) { + _thread_dfl_count[i - 1]++; + SIGADDSET(tempset, i); + if (_thread_dfl_count[i - 1] == 1) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */; } } } - /* Done accessing _thread_dfl_count for now. */ - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - if (ret == 0) { - /* - * Save the wait signal mask. The wait signal - * mask is independent of the threads signal mask - * and requires separate storage. - */ - curthread->data.sigwait = &waitset; + if (ret == 0) { + /* Done accessing _thread_dfl_count for now. */ + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(waitset, i) && + SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + *info = curthread->siginfo[i]; + KSE_SCHED_UNLOCK(curthread->kse, + curthread->kseg); + _kse_critical_leave(crit); + return (i); + } + } + curthread->timeout = 0; + _thr_set_timeout(timeout); /* Wait for a signal: */ - THR_LOCK_SWITCH(curthread); + curthread->oldsigmask = curthread->sigmask; + siginfo.si_signo = 0; + curthread->data.sigwaitinfo = &siginfo; + SIGFILLSET(curthread->sigmask); + SIGSETNAND(curthread->sigmask, waitset); THR_SET_STATE(curthread, PS_SIGWAIT); _thr_sched_switch_unlocked(curthread); - /* Return the signal number to the caller: */ - *sig = curthread->signo; + /* + * Return the signal number to the caller: + * XXX Here is race, how about a signal come in before + * we reach here? so we might got an incorrect timeout + * status. + */ + if (siginfo.si_signo > 0) { + if (info) + *info = siginfo; + ret = siginfo.si_signo; + } else { + if (curthread->timeout) + errno = EAGAIN; + ret = -1; + } /* * Probably unnecessary, but since it's in a union struct * we don't know how it could be used in the future. */ - curthread->data.sigwait = NULL; + crit = _kse_critical_enter(); + curthread->data.sigwaitinfo = NULL; + /* + * Relock the array of SIG_DFL wait counts. + */ + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); } - /* - * Relock the array of SIG_DFL wait counts. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - /* Restore the sigactions: */ act.sa_handler = SIG_DFL; - for (i = 1; i < NSIG; i++) { - if (sigismember(&tempset, i)) { - _thread_dfl_count[i]--; + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(tempset, i)) { + _thread_dfl_count[i - 1]--; if ((_thread_sigact[i - 1].sa_handler == SIG_DFL) && - (_thread_dfl_count[i] == 0)) { + (_thread_dfl_count[i - 1] == 0)) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */ ; } } } /* Done accessing _thread_dfl_count. */ KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); _kse_critical_leave(crit); + + return (ret); +} + +int +__sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, timeout); + _thr_leave_cancellation_point(curthread); + return (ret); +} + +int _sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + return lib_sigtimedwait(set, info, timeout); +} + +int +__sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, NULL); + _thr_leave_cancellation_point(curthread); + return (ret); +} + +int +_sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + return lib_sigtimedwait(set, info, NULL); +} + +int +__sigwait(const sigset_t *set, int *sig) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } + else + ret = -1; _thr_leave_cancellation_point(curthread); + return (ret); +} - /* Return the completion status: */ +int +_sigwait(const sigset_t *set, int *sig) +{ + int ret; + + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } else { + ret = -1; + } return (ret); } + diff --git a/lib/libpthread/pthread.map b/lib/libpthread/pthread.map index 1a4d540..c7bb4e4 100644 --- a/lib/libpthread/pthread.map +++ b/lib/libpthread/pthread.map @@ -138,6 +138,8 @@ global: _sigprocmask; _sigsuspend; _sigwait; + _sigtimedwait; + _sigwaitinfo; _sleep; _spinlock; _spinlock_debug; @@ -271,6 +273,8 @@ global: sigprocmask; sigsuspend; sigwait; + sigwaitinfo; + sigtimedwait; sleep; system; tcdrain; diff --git a/lib/libpthread/support/thr_support.c b/lib/libpthread/support/thr_support.c index 1b3c4ef..ad40fec 100644 --- a/lib/libpthread/support/thr_support.c +++ b/lib/libpthread/support/thr_support.c @@ -54,5 +54,8 @@ __strong_reference(memcpy, _thr_memcpy); __strong_reference(strcpy, _thr_strcpy); __strong_reference(strlen, _thr_strlen); __strong_reference(bzero, _thr_bzero); +__strong_reference(bcopy, _thr_bcopy); __strong_reference(__sys_write, _thr__sys_write); +__strong_reference(__sys_sigtimedwait, _thr__sys_sigtimedwait); + diff --git a/lib/libpthread/thread/thr_info.c b/lib/libpthread/thread/thr_info.c index 3218b5b..4410ace 100644 --- a/lib/libpthread/thread/thr_info.c +++ b/lib/libpthread/thread/thr_info.c @@ -77,7 +77,7 @@ _thread_dump_info(void) int fd, i; for (i = 0; i < 100000; i++) { - snprintf(tmpfile, sizeof(tmpfile), "/tmp/uthread.dump.%u.%i", + snprintf(tmpfile, sizeof(tmpfile), "/tmp/pthread.dump.%u.%i", getpid(), i); /* Open the dump file for append and create it if necessary: */ if ((fd = __sys_open(tmpfile, O_RDWR | O_CREAT | O_EXCL, @@ -166,6 +166,12 @@ dump_thread(int fd, pthread_t pthread, int long_version) strcpy(s, "This is the initial thread\n"); __sys_write(fd, s, strlen(s)); } + /* Check if this is the signal daemon thread: */ + if (pthread == _thr_sig_daemon) { + /* Output a record for the signal thread: */ + strcpy(s, "This is the signal daemon thread\n"); + __sys_write(fd, s, strlen(s)); + } /* Process according to thread state: */ switch (pthread->state) { case PS_SIGWAIT: @@ -173,6 +179,15 @@ dump_thread(int fd, pthread_t pthread, int long_version) __sys_write(fd, s, strlen(s)); for (i = _SIG_WORDS - 1; i >= 0; i--) { snprintf(s, sizeof(s), "%08x\n", + pthread->oldsigmask.__bits[i]); + __sys_write(fd, s, strlen(s)); + } + snprintf(s, sizeof(s), "(lo)\n"); + __sys_write(fd, s, strlen(s)); + snprintf(s, sizeof(s), "waitset (hi)"); + __sys_write(fd, s, strlen(s)); + for (i = _SIG_WORDS - 1; i >= 0; i--) { + snprintf(s, sizeof(s), "%08x\n", pthread->sigmask.__bits[i]); __sys_write(fd, s, strlen(s)); } diff --git a/lib/libpthread/thread/thr_init.c b/lib/libpthread/thread/thr_init.c index 227bdf3..202db62 100644 --- a/lib/libpthread/thread/thr_init.c +++ b/lib/libpthread/thread/thr_init.c @@ -39,6 +39,7 @@ #include "namespace.h" #include <sys/param.h> #include <sys/types.h> +#include <sys/signalvar.h> #include <machine/reg.h> #include <sys/ioctl.h> @@ -304,7 +305,7 @@ _libpthread_init(struct pthread *curthread) _thr_initial->kse->k_curthread = _thr_initial; _thr_initial->kse->k_flags |= KF_INITIALIZED; _kse_initial->k_curthread = _thr_initial; - + _thr_rtld_init(); } @@ -365,14 +366,13 @@ init_main_thread(struct pthread *thread) thread->name = strdup("initial thread"); /* Initialize the thread for signals: */ - sigemptyset(&thread->sigmask); + SIGEMPTYSET(thread->sigmask); /* * Set up the thread mailbox. The threads saved context * is also in the mailbox. */ thread->tmbx.tm_udata = thread; - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; thread->tmbx.tm_context.uc_stack.ss_size = thread->attr.stacksize_attr; thread->tmbx.tm_context.uc_stack.ss_sp = thread->attr.stackaddr_attr; @@ -407,10 +407,8 @@ static void init_private(void) { struct clockinfo clockinfo; - struct sigaction act; size_t len; int mib[2]; - int i; /* * Avoid reinitializing some things if they don't need to be, @@ -448,36 +446,6 @@ init_private(void) _thr_page_size = getpagesize(); _thr_guard_default = _thr_page_size; - - /* 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 (__sys_sigaction(i, NULL, - &_thread_sigact[i - 1]) != 0) { - /* - * Abort this process if signal - * initialisation fails: - */ - PANIC("Cannot read signal handler info"); - } - } - /* - * Install the signal handler for SIGINFO. It isn't - * really needed, but it is nice to have for debugging - * purposes. - */ - if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { - /* - * Abort this process if signal initialisation fails: - */ - PANIC("Cannot initialize signal handler"); - } - _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; - init_once = 1; /* Don't do this again. */ } else { /* @@ -495,8 +463,6 @@ init_private(void) TAILQ_INIT(&_thread_list); TAILQ_INIT(&_thread_gc_list); - /* Enter a loop to get the existing signal status: */ - /* Initialize the SIG_DFL dummy handler count. */ bzero(_thread_dfl_count, sizeof(_thread_dfl_count)); @@ -520,8 +486,7 @@ init_private(void) _thr_spinlock_init(); /* Clear pending signals and get the process signal mask. */ - sigemptyset(&_thr_proc_sigpending); - __sys_sigprocmask(SIG_SETMASK, NULL, &_thr_proc_sigmask); + SIGEMPTYSET(_thr_proc_sigpending); /* * _thread_list_lock and _kse_count are initialized diff --git a/lib/libpthread/thread/thr_kern.c b/lib/libpthread/thread/thr_kern.c index d1a2006..8a1ecc3 100644 --- a/lib/libpthread/thread/thr_kern.c +++ b/lib/libpthread/thread/thr_kern.c @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include <sys/signalvar.h> #include <sys/queue.h> #include <machine/atomic.h> +#include <machine/sigframe.h> #include <assert.h> #include <errno.h> @@ -125,7 +126,6 @@ static void dump_queues(struct kse *curkse); #endif static void kse_check_completed(struct kse *kse); static void kse_check_waitq(struct kse *kse); -static void kse_check_signals(struct kse *kse); static void kse_fini(struct kse *curkse); static void kse_reinit(struct kse *kse); static void kse_sched_multi(struct kse *curkse); @@ -143,27 +143,39 @@ static void kse_wakeup_multi(struct kse *curkse); static void kse_wakeup_one(struct pthread *thread); static void thr_cleanup(struct kse *kse, struct pthread *curthread); static void thr_link(struct pthread *thread); -static void thr_resume_wrapper(int unused_1, siginfo_t *unused_2, - ucontext_t *ucp); +static void thr_resume_wrapper(int sig, siginfo_t *, ucontext_t *); static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf); static int thr_timedout(struct pthread *thread, struct timespec *curtime); static void thr_unlink(struct pthread *thread); + /* * This is called after a fork(). * No locks need to be taken here since we are guaranteed to be * single threaded. + * + * XXX + * POSIX says for threaded process, fork() function is used + * only to run new programs, and the effects of calling functions + * that require certain resources between the call to fork() and + * the call to an exec function are undefined. + * + * Here it is not safe to reinitialize the library after fork(). + * Because memory management may be corrupted, further calling + * malloc()/free() may cause undefined behavior. */ void _kse_single_thread(struct pthread *curthread) { +#ifdef NOTYET struct kse *kse; struct kse_group *kseg; struct pthread *thread; kse_critical_t crit; int i; + /* * Disable upcalls and clear the threaded flag. * XXX - I don't think we need to disable upcalls after a fork(). @@ -172,6 +184,7 @@ _kse_single_thread(struct pthread *curthread) crit = _kse_critical_enter(); __isthreaded = 0; active_threads = 1; + _thr_signal_deinit(); /* * Enter a loop to remove and free all threads other than @@ -201,7 +214,7 @@ _kse_single_thread(struct pthread *curthread) TAILQ_INIT(&curthread->mutexq); /* initialize mutex queue */ curthread->joiner = NULL; /* no joining threads yet */ curthread->refcount = 0; - sigemptyset(&curthread->sigpend); /* clear pending signals */ + SIGEMPTYSET(curthread->sigpend); /* clear pending signals */ if (curthread->specific != NULL) { free(curthread->specific); curthread->specific = NULL; @@ -307,6 +320,12 @@ _kse_single_thread(struct pthread *curthread) curthread->kseg = NULL; _kse_initial = NULL; _libpthread_init(curthread); +#else + _ksd_readandclear_tmbx(); + __isthreaded = 0; + active_threads = 0; + _thr_signal_deinit(); +#endif } /* @@ -363,15 +382,17 @@ _kse_setthreaded(int threaded) * Tell the kernel to create a KSE for the initial thread * and enable upcalls in it. */ + _thr_signal_init(); _kse_initial->k_flags |= KF_STARTED; if (kse_create(&_kse_initial->k_mbx, 0) != 0) { _kse_initial->k_flags &= ~KF_STARTED; __isthreaded = 0; /* may abort() */ - DBG_MSG("kse_create failed\n"); + PANIC("kse_create() failed\n"); return (-1); } KSE_SET_MBOX(_kse_initial, _thr_initial); + _thr_start_sig_daemon(); _thr_setmaxconcurrency(); } return (0); @@ -536,6 +557,7 @@ _thr_sched_switch_unlocked(struct pthread *curthread) int ret; volatile int uts_once; volatile int resume_once = 0; + ucontext_t uc; /* We're in the scheduler, 5 by 5: */ curkse = _get_curkse(); @@ -552,7 +574,7 @@ _thr_sched_switch_unlocked(struct pthread *curthread) * a thread can be interrupted by other signals while * it is running down pending signals. */ - sigemptyset(&psf.psf_sigset); + psf.psf_valid = 0; curthread->curframe = &psf; /* @@ -561,6 +583,8 @@ _thr_sched_switch_unlocked(struct pthread *curthread) * o The current thread is dead; it's stack needs to be * cleaned up and it can't be done while operating on * it. + * o The current thread has signals pending, should + * let scheduler install signal trampoline for us. * o There are no runnable threads. * o The next thread to run won't unlock the scheduler * lock. A side note: the current thread may be run @@ -570,8 +594,10 @@ _thr_sched_switch_unlocked(struct pthread *curthread) if ((curthread->state == PS_DEAD) || (((td = KSE_RUNQ_FIRST(curkse)) == NULL) && (curthread->state != PS_RUNNING)) || - ((td != NULL) && (td->lock_switch == 0))) + ((td != NULL) && (td->lock_switch == 0))) { + curkse->k_switch = 1; _thread_enter_uts(&curthread->tmbx, &curkse->k_mbx); + } else { uts_once = 0; THR_GETCONTEXT(&curthread->tmbx.tm_context); @@ -623,6 +649,15 @@ _thr_sched_switch_unlocked(struct pthread *curthread) } } + if (psf.psf_valid) { + /* + * It is ugly we must increase critical count, because we + * have a frame saved, we must backout state in psf + * before we can process signals. + */ + curthread->critical_count++; + } + if (curthread->lock_switch != 0) { /* * Unlock the scheduling queue and leave the @@ -638,11 +673,15 @@ _thr_sched_switch_unlocked(struct pthread *curthread) /* * This thread is being resumed; check for cancellations. */ - if ((resume_once == 0) && (!THR_IN_CRITICAL(curthread))) { - resume_once = 1; - thr_resume_check(curthread, &curthread->tmbx.tm_context, &psf); + if ((psf.psf_valid || curthread->check_pending)) { + resume_once = 0; + THR_GETCONTEXT(&uc); + if (resume_once == 0) { + resume_once = 1; + curthread->check_pending = 0; + thr_resume_check(curthread, &uc, &psf); + } } - THR_ACTIVATE_LAST_LOCK(curthread); } @@ -747,9 +786,6 @@ kse_sched_single(struct kse *curkse) KSE_WAITQ_REMOVE(curkse, td_wait); } } - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Remove the frame reference. */ @@ -810,12 +846,12 @@ kse_sched_multi(struct kse *curkse) KSE_CLEAR_WAIT(curkse); } - /* Lock the scheduling lock. */ - curthread = curkse->k_curthread; - if ((curthread == NULL) || (curthread->need_switchout == 0)) { - /* This is an upcall; take the scheduler lock. */ + /*If this is an upcall; take the scheduler lock. */ + if (curkse->k_switch == 0) KSE_SCHED_LOCK(curkse, curkse->k_kseg); - } + curkse->k_switch = 0; + + curthread = curkse->k_curthread; if (KSE_IS_IDLE(curkse)) { KSE_CLEAR_IDLE(curkse); @@ -879,11 +915,6 @@ kse_sched_multi(struct kse *curkse) kse_wakeup_multi(curkse); - /* This has to be done without the scheduling lock held. */ - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); - #ifdef DEBUG_THREAD_KERN dump_queues(curkse); #endif @@ -899,9 +930,6 @@ kse_sched_multi(struct kse *curkse) kse_wait(curkse, td_wait); kse_check_completed(curkse); kse_check_waitq(curkse); - KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); - kse_check_signals(curkse); - KSE_SCHED_LOCK(curkse, curkse->k_kseg); } /* Check for no more threads: */ @@ -966,15 +994,19 @@ kse_sched_multi(struct kse *curkse) * signal frame to the thread's context. */ #ifdef NOT_YET - if ((curframe == NULL) && ((curthread->have_signals != 0) || + if ((((curframe == NULL) && (curthread->check_pending != 0)) || (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && - ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)))) + ((curthread->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0))) && + !THR_IN_CRITICAL(curthread)) signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); #else - if ((curframe == NULL) && (curthread->have_signals != 0)) + if ((curframe == NULL) && (curthread->check_pending != 0) && + !THR_IN_CRITICAL(curthread)) { + curthread->check_pending = 0; signalcontext(&curthread->tmbx.tm_context, 0, (__sighandler_t *)thr_resume_wrapper); + } #endif /* * Continue the thread at its current frame: @@ -999,61 +1031,31 @@ kse_sched_multi(struct kse *curkse) } static void -kse_check_signals(struct kse *curkse) -{ - sigset_t sigset; - int i; - - /* Deliver posted signals. */ - for (i = 0; i < _SIG_WORDS; i++) { - atomic_swap_int(&curkse->k_mbx.km_sigscaught.__bits[i], - 0, &sigset.__bits[i]); - } - if (SIGNOTEMPTY(sigset)) { - /* - * Dispatch each signal. - * - * XXX - There is no siginfo for any of these. - * I think there should be, especially for - * signals from other processes (si_pid, si_uid). - */ - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - DBG_MSG("Dispatching signal %d\n", i); - _thr_sig_dispatch(curkse, i, - NULL /* no siginfo */); - } - } - sigemptyset(&sigset); - __sys_sigprocmask(SIG_SETMASK, &sigset, NULL); - } -} - -static void -thr_resume_wrapper(int unused_1, siginfo_t *unused_2, ucontext_t *ucp) +thr_resume_wrapper(int sig, siginfo_t *siginfo, ucontext_t *ucp) { struct pthread *curthread = _get_curthread(); + struct kse *curkse; + int ret; + DBG_MSG(">>> sig wrapper\n"); + if (curthread->lock_switch) + PANIC("thr_resume_wrapper, lock_switch != 0\n"); thr_resume_check(curthread, ucp, NULL); + _kse_critical_enter(); + curkse = _get_curkse(); + curthread->tmbx.tm_context = *ucp; + ret = _thread_switch(&curthread->tmbx, &curkse->k_mbx.km_curthread); + if (ret != 0) + PANIC("thr_resume_wrapper: thread has returned " + "from _thread_switch"); + /* THR_SETCONTEXT(ucp); */ /* not work, why ? */ } static void thr_resume_check(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - /* Check signals before cancellations. */ - while (curthread->have_signals != 0) { - /* Clear the pending flag. */ - curthread->have_signals = 0; - - /* - * It's perfectly valid, though not portable, for - * signal handlers to munge their interrupted context - * and expect to return to it. Ensure we use the - * correct context when running down signals. - */ - _thr_sig_rundown(curthread, ucp, psf); - } + _thr_sig_rundown(curthread, ucp, psf); #ifdef NOT_YET if (((curthread->cancelflags & THR_AT_CANCEL_POINT) == 0) && @@ -1124,7 +1126,7 @@ thr_cleanup(struct kse *curkse, struct pthread *thread) THR_GCLIST_ADD(thread); /* Use thread_list_lock */ active_threads--; - if (active_threads == 0) { + if (active_threads == 1) { KSE_LOCK_RELEASE(curkse, &_thread_list_lock); exit(0); } @@ -1203,8 +1205,15 @@ _thr_gc(struct pthread *curthread) KSE_LOCK_RELEASE(curthread->kse, &kse_lock); _kse_critical_leave(crit); } - DBG_MSG("Freeing thread %p\n", td); - _thr_free(curthread, td); + /* + * XXX we don't free initial thread, because there might + * have some code referencing initial thread. + */ + if (td != _thr_initial) { + DBG_MSG("Freeing thread %p\n", td); + _thr_free(curthread, td); + } else + DBG_MSG("Initial thread won't be freed\n"); } /* XXX free kse and ksegrp list should be looked as well */ } @@ -1216,7 +1225,6 @@ _thr_gc(struct pthread *curthread) int _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) { - struct kse *curkse; kse_critical_t crit; int ret; @@ -1250,15 +1258,11 @@ _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) /* * This thread needs a new KSE and KSEG. */ - crit = _kse_critical_enter(); - curkse = _get_curkse(); - _ksd_setprivate(&newthread->kse->k_ksd); - newthread->kse->k_flags |= KF_INITIALIZED|KF_STARTED; + newthread->kse->k_flags &= ~KF_INITIALIZED; + newthread->kse->k_flags |= KF_STARTED; ret = kse_create(&newthread->kse->k_mbx, 1); if (ret != 0) ret = errno; - _ksd_setprivate(&curkse->k_ksd); - _kse_critical_leave(crit); } else { /* @@ -1266,6 +1270,7 @@ _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) * assigned threads. If the new thread is runnable, also * add it to the KSE's run queue. */ + crit = _kse_critical_enter(); KSE_SCHED_LOCK(curthread->kse, newthread->kseg); KSEG_THRQ_ADD(newthread->kseg, newthread); if (newthread->state == PS_RUNNING) @@ -1288,6 +1293,7 @@ _thr_schedule_add(struct pthread *curthread, struct pthread *newthread) kse_wakeup_one(newthread); } KSE_SCHED_UNLOCK(curthread->kse, newthread->kseg); + _kse_critical_leave(crit); ret = 0; } if (ret != 0) @@ -1328,6 +1334,7 @@ kse_check_completed(struct kse *kse) { struct pthread *thread; struct kse_thr_mailbox *completed; + int sig; if ((completed = kse->k_mbx.km_completed) != NULL) { kse->k_mbx.km_completed = NULL; @@ -1348,6 +1355,13 @@ kse_check_completed(struct kse *kse) thread->active = 0; } } + if ((sig = thread->tmbx.tm_syncsig.si_signo) != 0) { + if (SIGISMEMBER(thread->sigmask, sig)) + SIGADDSET(thread->sigpend, sig); + else + _thr_sig_add(thread, sig, &thread->tmbx.tm_syncsig); + thread->tmbx.tm_syncsig.si_signo = 0; + } completed = completed->tm_next; } } @@ -1446,6 +1460,8 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) { int level; int i; + int restart; + siginfo_t siginfo; /* * Place the currently running thread into the @@ -1459,15 +1475,25 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) thread->need_switchout = 0; /* This thread must have blocked in the kernel. */ /* thread->slice_usec = -1;*/ /* restart timeslice */ - /* - * XXX - Check for pending signals for this thread to - * see if we need to interrupt it in the kernel. - */ - /* if (thread->check_pending != 0) */ if ((thread->slice_usec != -1) && (thread->attr.sched_policy != SCHED_FIFO)) thread->slice_usec += (thread->tmbx.tm_uticks + thread->tmbx.tm_sticks) * _clock_res_usec; + /* + * Check for pending signals for this thread to + * see if we need to interrupt it in the kernel. + */ + if (thread->check_pending != 0) { + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(thread->sigpend, i) && + !SIGISMEMBER(thread->sigmask, i)) { + restart = _thread_sigact[1 - 1].sa_flags & SA_RESTART; + kse_thr_interrupt(&thread->tmbx, + restart ? -2 : -1); + break; + } + } + } } else { switch (thread->state) { @@ -1507,10 +1533,12 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) THR_SET_STATE(thread, PS_RUNNING); break; + case PS_SIGWAIT: + KSE_WAITQ_INSERT(kse, thread); + break; case PS_JOIN: case PS_MUTEX_WAIT: case PS_SIGSUSPEND: - case PS_SIGWAIT: case PS_SUSPENDED: case PS_DEADLOCK: default: @@ -1564,11 +1592,18 @@ kse_switchout_thread(struct kse *kse, struct pthread *thread) if (thread->check_pending != 0) { /* Install pending signals into the frame. */ thread->check_pending = 0; - for (i = 0; i < _SIG_MAXSIG; i++) { - if (sigismember(&thread->sigpend, i) && - !sigismember(&thread->tmbx.tm_context.uc_sigmask, i)) + KSE_LOCK_ACQUIRE(kse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(thread->sigmask, i)) + continue; + if (SIGISMEMBER(thread->sigpend, i)) _thr_sig_add(thread, i, &thread->siginfo[i]); + else if (SIGISMEMBER(_thr_proc_sigpending, i) && + _thr_getprocsig_unlocked(i, &siginfo)) { + _thr_sig_add(thread, i, &siginfo); + } } + KSE_LOCK_RELEASE(kse, &_thread_signal_lock); } } @@ -2016,6 +2051,10 @@ _kse_alloc(struct pthread *curthread) _lockuser_destroy(&kse->k_lockusers[i]); } free(kse); + if (curthread != NULL) { + KSE_LOCK_RELEASE(curthread->kse, &kse_lock); + _kse_critical_leave(crit); + } return (NULL); } kse->k_flags = 0; @@ -2044,7 +2083,7 @@ kse_reinit(struct kse *kse) kse->k_kseg = 0; kse->k_schedq = 0; kse->k_locklevel = 0; - sigemptyset(&kse->k_sigmask); + SIGEMPTYSET(kse->k_sigmask); bzero(&kse->k_sigq, sizeof(kse->k_sigq)); kse->k_check_sigq = 0; kse->k_flags = 0; @@ -2053,6 +2092,7 @@ kse_reinit(struct kse *kse) kse->k_error = 0; kse->k_cpu = 0; kse->k_done = 0; + kse->k_switch = 0; } void @@ -2171,10 +2211,12 @@ thr_link(struct pthread *thread) { kse_critical_t crit; struct kse *curkse; + struct pthread *curthread; crit = _kse_critical_enter(); curkse = _get_curkse(); - + curthread = _get_curthread(); + thread->sigmask = curthread->sigmask; KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); /* * Initialize the unique id (which GDB uses to track diff --git a/lib/libpthread/thread/thr_kill.c b/lib/libpthread/thread/thr_kill.c index 19f34bb..226cb86 100644 --- a/lib/libpthread/thread/thr_kill.c +++ b/lib/libpthread/thread/thr_kill.c @@ -45,7 +45,7 @@ _pthread_kill(pthread_t pthread, int sig) int ret; /* Check for invalid signal numbers: */ - if (sig < 0 || sig >= NSIG) + if (sig < 0 || sig > _SIG_MAXSIG) /* Invalid signal: */ ret = EINVAL; /* diff --git a/lib/libpthread/thread/thr_private.h b/lib/libpthread/thread/thr_private.h index 3672025..fefacd1 100644 --- a/lib/libpthread/thread/thr_private.h +++ b/lib/libpthread/thread/thr_private.h @@ -171,7 +171,7 @@ typedef struct kse_thr_mailbox *kse_critical_t; struct kse_group; -#define MAX_KSE_LOCKLEVEL 3 +#define MAX_KSE_LOCKLEVEL 5 struct kse { struct kse_mailbox k_mbx; /* kernel kse mailbox */ /* -- location and order specific items for gdb -- */ @@ -190,7 +190,7 @@ struct kse { struct lockuser k_lockusers[MAX_KSE_LOCKLEVEL]; int k_locklevel; sigset_t k_sigmask; - struct sigstatus k_sigq[NSIG]; + struct sigstatus k_sigq[_SIG_MAXSIG]; stack_t k_stack; int k_check_sigq; int k_flags; @@ -201,6 +201,7 @@ struct kse { int k_error; /* syscall errno in critical */ int k_cpu; /* CPU ID when bound */ int k_done; /* this KSE is done */ + int k_switch; /* thread switch in UTS */ }; /* @@ -546,8 +547,8 @@ enum pthread_state { union pthread_wait_data { pthread_mutex_t mutex; pthread_cond_t cond; - const sigset_t *sigwait; /* Waiting on a signal in sigwait */ struct lock *lock; + siginfo_t *sigwaitinfo; /* used to save siginfo for sigwaitinfo() */ }; /* @@ -563,6 +564,7 @@ typedef void (*thread_continuation_t) (void *); * state is restored from here. */ struct pthread_sigframe { + int psf_valid; int psf_flags; int psf_interrupted; int psf_signo; @@ -586,7 +588,7 @@ struct pthread_specific_elem { }; -#define MAX_THR_LOCKLEVEL 3 +#define MAX_THR_LOCKLEVEL 5 /* * Thread structure. */ @@ -640,7 +642,7 @@ struct pthread { * Used for tracking delivery of signal handlers. */ struct pthread_sigframe *curframe; - siginfo_t siginfo[NSIG]; + siginfo_t siginfo[_SIG_MAXSIG]; /* * Cancelability flags - the lower 2 bits are used by cancel @@ -657,11 +659,10 @@ struct pthread { * The thread's base and pending signal masks. The active * signal mask is stored in the thread's context (in mailbox). */ + sigset_t oldsigmask; sigset_t sigmask; sigset_t sigpend; - int sigmask_seqno; int check_pending; - int have_signals; int refcount; /* Thread state: */ @@ -997,14 +998,14 @@ SCLASS struct pthread_cond_attr _pthread_condattr_default SCLASS int _clock_res_usec SCLASS_PRESET(CLOCK_RES_USEC); /* Array of signal actions for this process: */ -SCLASS struct sigaction _thread_sigact[NSIG]; +SCLASS struct sigaction _thread_sigact[_SIG_MAXSIG]; /* * Array of counts of dummy handlers for SIG_DFL signals. This is used to * assure that there is always a dummy signal handler installed while there * is a thread sigwait()ing on the corresponding signal. */ -SCLASS int _thread_dfl_count[NSIG]; +SCLASS int _thread_dfl_count[_SIG_MAXSIG]; /* * Lock for above count of dummy handlers and for the process signal @@ -1014,8 +1015,7 @@ SCLASS struct lock _thread_signal_lock; /* Pending signals and mask for this process: */ SCLASS sigset_t _thr_proc_sigpending; -SCLASS sigset_t _thr_proc_sigmask SCLASS_PRESET({{0, 0, 0, 0}}); -SCLASS siginfo_t _thr_proc_siginfo[NSIG]; +SCLASS siginfo_t _thr_proc_siginfo[_SIG_MAXSIG]; SCLASS pid_t _thr_pid SCLASS_PRESET(0); @@ -1030,7 +1030,7 @@ SCLASS struct lock _keytable_lock; SCLASS struct lock _thread_list_lock; SCLASS int _thr_guard_default; SCLASS int _thr_page_size; - +SCLASS pthread_t _thr_sig_daemon; SCLASS int _thr_debug_flags SCLASS_PRESET(0); /* Undefine the storage class and preset specifiers: */ @@ -1116,7 +1116,6 @@ void _thr_panic_exit(char *, int, char *); void _thread_cleanupspecific(void); void _thread_dump_info(void); void _thread_printf(int, const char *, ...); -void _thr_sched_frame(struct pthread_sigframe *); void _thr_sched_switch(struct pthread *); void _thr_sched_switch_unlocked(struct pthread *); void _thr_set_timeout(const struct timespec *); @@ -1126,13 +1125,17 @@ void _thr_sig_check_pending(struct pthread *); void _thr_sig_rundown(struct pthread *, ucontext_t *, struct pthread_sigframe *); void _thr_sig_send(struct pthread *pthread, int sig); -void _thr_sig_wrapper(void); void _thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); void _thr_spinlock_init(void); void _thr_enter_cancellation_point(struct pthread *); void _thr_leave_cancellation_point(struct pthread *); int _thr_setconcurrency(int new_level); int _thr_setmaxconcurrency(void); +int _thr_start_sig_daemon(void); +int _thr_getprocsig(int sig, siginfo_t *siginfo); +int _thr_getprocsig_unlocked(int sig, siginfo_t *siginfo); +void _thr_signal_init(void); +void _thr_signal_deinit(void); /* * Aliases for _pthread functions. Should be called instead of @@ -1216,6 +1219,8 @@ int __sys_select(int, fd_set *, fd_set *, fd_set *, struct timeval *); ssize_t __sys_read(int, void *, size_t); ssize_t __sys_write(int, const void *, size_t); void __sys_exit(int); +int __sys_sigwait(const sigset_t *, int *); +int __sys_sigtimedwait(sigset_t *, siginfo_t *, struct timespec *); #endif /* #include <poll.h> */ diff --git a/lib/libpthread/thread/thr_sig.c b/lib/libpthread/thread/thr_sig.c index 94e5630..d06db9d 100644 --- a/lib/libpthread/thread/thr_sig.c +++ b/lib/libpthread/thread/thr_sig.c @@ -45,19 +45,15 @@ /* Prototypes: */ static void build_siginfo(siginfo_t *info, int signo); -/* static void thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info); */ static void thr_sig_check_state(struct pthread *pthread, int sig); static struct pthread *thr_sig_find(struct kse *curkse, int sig, siginfo_t *info); static void handle_special_signals(struct kse *curkse, int sig); -static void thr_sigframe_add(struct pthread *thread, int sig, - siginfo_t *info); +static void thr_sigframe_add(struct pthread *thread); static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf); static void thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf); -static void thr_sig_invoke_handler(struct pthread *, int sig, - siginfo_t *info, ucontext_t *ucp); /* #define DEBUG_SIGNAL */ #ifdef DEBUG_SIGNAL @@ -137,6 +133,65 @@ static void thr_sig_invoke_handler(struct pthread *, int sig, * signal unmasked. */ +static void * +sig_daemon(void *arg /* Unused */) +{ + int i; + kse_critical_t crit; + struct timespec ts; + sigset_t set; + struct kse *curkse; + struct pthread *curthread = _get_curthread(); + + DBG_MSG("signal daemon started"); + + curthread->name = strdup("signal thread"); + crit = _kse_critical_enter(); + curkse = _get_curkse(); + SIGFILLSET(set); + __sys_sigprocmask(SIG_SETMASK, &set, NULL); + __sys_sigpending(&set); + ts.tv_sec = 0; + ts.tv_nsec = 0; + while (1) { + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + _thr_proc_sigpending = set; + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(set, i) != 0) + _thr_sig_dispatch(curkse, i, + NULL /* no siginfo */); + } + ts.tv_sec = 30; + ts.tv_nsec = 0; + curkse->k_mbx.km_flags = + KMF_NOUPCALL | KMF_NOCOMPLETED | KMF_WAITSIGEVENT; + kse_release(&ts); + curkse->k_mbx.km_flags = 0; + set = curkse->k_mbx.km_sigscaught; + } + return (0); +} + +/* Utility function to create signal daemon thread */ +int +_thr_start_sig_daemon(void) +{ + pthread_attr_t attr; + sigset_t sigset, oldset; + + SIGFILLSET(sigset); + pthread_sigmask(SIG_SETMASK, &sigset, &oldset); + pthread_attr_init(&attr); + pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); + /* sigmask will be inherited */ + if (pthread_create(&_thr_sig_daemon, &attr, sig_daemon, NULL)) + PANIC("can not create signal daemon thread!\n"); + pthread_attr_destroy(&attr); + pthread_sigmask(SIG_SETMASK, &oldset, NULL); + return (0); +} + /* * This signal handler only delivers asynchronous signals. * This must be called with upcalls disabled and without @@ -151,7 +206,6 @@ _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) /* Some signals need special handling: */ handle_special_signals(curkse, sig); - DBG_MSG("dispatch sig:%d\n", sig); while ((thread = thr_sig_find(curkse, sig, info)) != NULL) { /* * Setup the target thread to receive the signal: @@ -163,18 +217,17 @@ _thr_sig_dispatch(struct kse *curkse, int sig, siginfo_t *info) THR_IS_EXITING(thread) || THR_IS_SUSPENDED(thread)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } else if (sigismember(&thread->tmbx.tm_context.uc_sigmask, - sig)) { + } else if (SIGISMEMBER(thread->sigmask, sig)) { KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); - } - else { + } else { _thr_sig_add(thread, sig, info); KSE_SCHED_UNLOCK(curkse, thread->kseg); _thr_ref_delete(NULL, thread); break; } } + DBG_MSG("<<< _thr_sig_dispatch\n"); } void @@ -187,7 +240,6 @@ _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) if ((curkse == NULL) || ((curkse->k_flags & KF_STARTED) == 0)) { /* Upcalls are not yet started; just call the handler. */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN) && (sigfunc != (__siginfohandler_t *)_thr_sig_handler)) { @@ -202,68 +254,119 @@ _thr_sig_handler(int sig, siginfo_t *info, ucontext_t *ucp) else { /* Nothing. */ DBG_MSG("Got signal %d\n", sig); - sigaddset(&curkse->k_mbx.km_sigscaught, sig); - ucp->uc_sigmask = _thr_proc_sigmask; + /* XXX Bound thread will fall into this... */ } } +/* Must be called with signal lock and schedule lock held in order */ static void thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, ucontext_t *ucp) { void (*sigfunc)(int, siginfo_t *, void *); - sigset_t saved_mask; - int saved_seqno; + sigset_t sigmask; + int sa_flags; + struct sigaction act; + struct kse *curkse; - /* Invoke the signal handler without going through the scheduler: + /* + * Invoke the signal handler without going through the scheduler: */ DBG_MSG("Got signal %d, calling handler for current thread %p\n", sig, curthread); - /* - * Setup the threads signal mask. - * - * The mask is changed in the thread's active signal mask - * (in the context) and not in the base signal mask because - * a thread is allowed to change its signal mask within a - * signal handler. If it does, the signal mask restored - * after the handler should be the same as that set by the - * thread during the handler, not the original mask from - * before calling the handler. The thread could also - * modify the signal mask in the context and expect this - * mask to be used. - */ - THR_SCHED_LOCK(curthread, curthread); - saved_mask = curthread->tmbx.tm_context.uc_sigmask; - saved_seqno = curthread->sigmask_seqno; - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&curthread->tmbx.tm_context.uc_sigmask, sig); - THR_SCHED_UNLOCK(curthread, curthread); - + if (!_kse_in_critical()) + PANIC("thr_sig_invoke_handler without in critical\n"); + curkse = _get_curkse(); /* * Check that a custom handler is installed and if * the signal is not blocked: */ sigfunc = _thread_sigact[sig - 1].sa_sigaction; - ucp->uc_sigmask = _thr_proc_sigmask; + sa_flags = _thread_sigact[sig - 1].sa_flags & SA_SIGINFO; + sigmask = curthread->sigmask; + SIGSETOR(curthread->sigmask, _thread_sigact[sig - 1].sa_mask); + if (!(sa_flags & (SA_NODEFER | SA_RESETHAND))) + SIGADDSET(curthread->sigmask, sig); + if ((sig != SIGILL) && (sa_flags & SA_RESETHAND)) { + if (_thread_dfl_count[sig - 1] == 0) { + act.sa_handler = SIG_DFL; + act.sa_flags = SA_RESTART; + SIGEMPTYSET(act.sa_mask); + __sys_sigaction(sig, &act, NULL); + __sys_sigaction(sig, NULL, &_thread_sigact[sig - 1]); + } + } + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + ucp->uc_sigmask = sigmask; + if (((__sighandler_t *)sigfunc != SIG_DFL) && ((__sighandler_t *)sigfunc != SIG_IGN)) { - if (((_thread_sigact[sig - 1].sa_flags & SA_SIGINFO) != 0) || - (info == NULL)) + if ((sa_flags & SA_SIGINFO) != 0 || info == NULL) (*(sigfunc))(sig, info, ucp); else (*(sigfunc))(sig, (siginfo_t*)(intptr_t)info->si_code, ucp); + } else { + /* XXX + * TODO: exit process if signal would kill it. + */ +#ifdef NOTYET + if (sigprop(sig) & SA_KILL) + kse_sigexit(sig); +#endif } - + _kse_critical_enter(); + /* Don't trust after critical leave/enter */ + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); /* * Restore the thread's signal mask. */ - if (saved_seqno == curthread->sigmask_seqno) - curthread->tmbx.tm_context.uc_sigmask = saved_mask; - else - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; + curthread->sigmask = ucp->uc_sigmask; + + DBG_MSG("Got signal %d, handler returned %p\n", sig, curthread); +} + +int +_thr_getprocsig(int sig, siginfo_t *siginfo) +{ + kse_critical_t crit; + struct kse *curkse; + int ret; + + DBG_MSG(">>> _thr_getprocsig\n"); + + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + ret = _thr_getprocsig_unlocked(sig, siginfo); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + _kse_critical_leave(crit); + + DBG_MSG("<<< _thr_getprocsig\n"); + return (ret); +} + +int +_thr_getprocsig_unlocked(int sig, siginfo_t *siginfo) +{ + sigset_t sigset; + struct timespec ts; + + /* try to retrieve signal from kernel */ + SIGEMPTYSET(sigset); + SIGADDSET(sigset, sig); + ts.tv_sec = 0; + ts.tv_nsec = 0; + if (__sys_sigtimedwait(&sigset, siginfo, &ts) > 0) { + SIGDELSET(_thr_proc_sigpending, sig); + return (sig); + } + return (0); } /* @@ -273,15 +376,12 @@ thr_sig_invoke_handler(struct pthread *curthread, int sig, siginfo_t *info, struct pthread * thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) { - int handler_installed; struct pthread *pthread; struct pthread *suspended_thread, *signaled_thread; + siginfo_t si; DBG_MSG("Looking for thread to handle signal %d\n", sig); - handler_installed = (_thread_sigact[sig - 1].sa_handler != SIG_IGN) && - (_thread_sigact[sig - 1].sa_handler != SIG_DFL); - /* Check if the signal requires a dump of thread information: */ if (sig == SIGINFO) { /* Dump thread information to file: */ @@ -302,19 +402,39 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) KSE_LOCK_ACQUIRE(curkse, &_thread_list_lock); TAILQ_FOREACH(pthread, &_thread_list, tle) { + if (pthread == _thr_sig_daemon) + continue; +#ifdef NOTYET + /* Signal delivering to bound thread is done by kernel */ + if (pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) + continue; +#endif + /* Take the scheduling lock. */ KSE_SCHED_LOCK(curkse, pthread->kseg); - if ((pthread->state == PS_SIGWAIT) && - sigismember(pthread->data.sigwait, sig)) { + if ((pthread->state == PS_DEAD) || + (pthread->state == PS_DEADLOCK) || + THR_IS_EXITING(pthread) || + THR_IS_SUSPENDED(pthread) || + SIGISMEMBER(pthread->sigmask, sig)) { + ; /* Skip this thread. */ + } else if (pthread->state == PS_SIGWAIT) { /* - * Return the signal number and make the - * thread runnable. + * retrieve signal from kernel, if it is job control + * signal, and sigaction is SIG_DFL, then we will + * be stopped in kernel, we hold lock here, but that + * does not matter, because that's job control, and + * whole process should be stopped. */ - pthread->signo = sig; - _thr_setrunnable_unlocked(pthread); - + if (_thr_getprocsig(sig, &si)) { + DBG_MSG("Waking thread %p in sigwait" + " with signal %d\n", pthread, sig); + /* where to put siginfo ? */ + *(pthread->data.sigwaitinfo) = si; + pthread->sigmask = pthread->oldsigmask; + _thr_setrunnable_unlocked(pthread); + } KSE_SCHED_UNLOCK(curkse, pthread->kseg); - /* * POSIX doesn't doesn't specify which thread * will get the signal if there are multiple @@ -326,16 +446,8 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) * to the process pending set. */ KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - DBG_MSG("Waking thread %p in sigwait with signal %d\n", - pthread, sig); return (NULL); - } - else if ((pthread->state == PS_DEAD) || - (pthread->state == PS_DEADLOCK) || - THR_IS_EXITING(pthread) || THR_IS_SUSPENDED(pthread)) - ; /* Skip this thread. */ - else if ((handler_installed != 0) && - !sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { + } else { if (pthread->state == PS_SIGSUSPEND) { if (suspended_thread == NULL) { suspended_thread = pthread; @@ -344,49 +456,22 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info) } else if (signaled_thread == NULL) { signaled_thread = pthread; signaled_thread->refcount++; - } + } } KSE_SCHED_UNLOCK(curkse, pthread->kseg); } KSE_LOCK_RELEASE(curkse, &_thread_list_lock); - /* - * Only perform wakeups and signal delivery if there is a - * custom handler installed: - */ - if (handler_installed == 0) { - /* - * There is no handler installed; nothing to do here. - */ - } else if (suspended_thread == NULL && - signaled_thread == NULL) { - /* - * Add it to the set of signals pending - * on the process: - */ - KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - if (!sigismember(&_thr_proc_sigpending, sig)) { - sigaddset(&_thr_proc_sigpending, sig); - if (info == NULL) - build_siginfo(&_thr_proc_siginfo[sig], sig); - else - memcpy(&_thr_proc_siginfo[sig], info, - sizeof(*info)); - } - KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); - } else { - /* - * We only deliver the signal to one thread; - * give preference to the suspended thread: - */ - if (suspended_thread != NULL) { - pthread = suspended_thread; + if (suspended_thread != NULL) { + pthread = suspended_thread; + if (signaled_thread) _thr_ref_delete(NULL, signaled_thread); - } else - pthread = signaled_thread; - return (pthread); + } else if (signaled_thread) { + pthread = signaled_thread; + } else { + pthread = NULL; } - return (NULL); + return (pthread); } static void @@ -405,64 +490,80 @@ void _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, struct pthread_sigframe *psf) { - struct pthread_sigframe psf_save; - sigset_t sigset; + siginfo_t siginfo; int i; + kse_critical_t crit; + struct kse *curkse; - THR_SCHED_LOCK(curthread, curthread); - memcpy(&sigset, &curthread->sigpend, sizeof(sigset)); - sigemptyset(&curthread->sigpend); - if (psf != NULL) { - memcpy(&psf_save, psf, sizeof(*psf)); - SIGSETOR(sigset, psf_save.psf_sigset); - sigemptyset(&psf->psf_sigset); - } - THR_SCHED_UNLOCK(curthread, curthread); - + DBG_MSG(">>> thr_sig_rundown %p\n", curthread); /* Check the threads previous state: */ - if ((psf != NULL) && (psf_save.psf_state != PS_RUNNING)) { + if ((psf != NULL) && (psf->psf_valid != 0)) { /* * Do a little cleanup handling for those threads in * queues before calling the signal handler. Signals * for these threads are temporarily blocked until * after cleanup handling. */ - switch (psf_save.psf_state) { + switch (psf->psf_state) { case PS_COND_WAIT: _cond_wait_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; case PS_MUTEX_WAIT: _mutex_lock_backout(curthread); - psf_save.psf_state = PS_RUNNING; + psf->psf_state = PS_RUNNING; break; + case PS_RUNNING: + break; + default: + psf->psf_state = PS_RUNNING; break; } + /* XXX see comment in thr_sched_switch_unlocked */ + curthread->critical_count--; } + /* * Lower the priority before calling the handler in case * it never returns (longjmps back): */ + crit = _kse_critical_enter(); + curkse = _get_curkse(); + KSE_SCHED_LOCK(curkse, curkse->k_kseg); + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); curthread->active_priority &= ~THR_SIGNAL_PRIORITY; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], ucp); + while (1) { + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(curthread->sigmask, i)) + continue; + if (SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + siginfo = curthread->siginfo[i]; + break; + } + if (SIGISMEMBER(_thr_proc_sigpending, i)) { + if (_thr_getprocsig_unlocked(i, &siginfo)) + break; + } } + if (i <= _SIG_MAXSIG) + thr_sig_invoke_handler(curthread, i, &siginfo, ucp); + else + break; } - THR_SCHED_LOCK(curthread, curthread); - if (psf != NULL) - thr_sigframe_restore(curthread, &psf_save); - /* Restore the signal mask. */ - curthread->tmbx.tm_context.uc_sigmask = curthread->sigmask; - THR_SCHED_UNLOCK(curthread, curthread); - _thr_sig_check_pending(curthread); + if (psf != NULL && psf->psf_valid != 0) + thr_sigframe_restore(curthread, psf); + curkse = _get_curkse(); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); + KSE_SCHED_UNLOCK(curkse, curkse->k_kseg); + _kse_critical_leave(&curthread->tmbx); + + DBG_MSG("<<< thr_sig_rundown %p\n", curthread); } /* @@ -477,87 +578,19 @@ _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp, void _thr_sig_check_pending(struct pthread *curthread) { - sigset_t sigset; - sigset_t pending_process; - sigset_t pending_thread; - kse_critical_t crit; - int i; - - curthread->check_pending = 0; - - /* - * Check if there are pending signals for the running - * thread or process that aren't blocked: - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - sigset = _thr_proc_sigpending; - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); + ucontext_t uc; + volatile int once; - THR_SCHED_LOCK(curthread, curthread); - SIGSETOR(sigset, curthread->sigpend); - SIGSETNAND(sigset, curthread->tmbx.tm_context.uc_sigmask); - if (SIGNOTEMPTY(sigset)) { - ucontext_t uc; - volatile int once; + if (THR_IN_CRITICAL(curthread)) + return; + once = 0; + THR_GETCONTEXT(&uc); + if (once == 0) { + once = 1; curthread->check_pending = 0; - THR_SCHED_UNLOCK(curthread, curthread); - - /* - * Split the pending signals into those that were - * pending on the process and those that were pending - * on the thread. - */ - sigfillset(&pending_process); - sigfillset(&pending_thread); - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - if (sigismember(&curthread->sigpend, i) != 0) { - build_siginfo(&curthread->siginfo[i], i); - sigdelset(&pending_thread, i); - } else { - memcpy(&curthread->siginfo[i], - &_thr_proc_siginfo[i], - sizeof(siginfo_t)); - sigdelset(&pending_process, i); - } - } - } - /* - * Remove any process pending signals that were scheduled - * to be delivered from process' pending set. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETAND(_thr_proc_sigpending, pending_process); - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - - /* - * Remove any thread pending signals that were scheduled - * to be delivered from thread's pending set. - */ - THR_SCHED_LOCK(curthread, curthread); - SIGSETAND(curthread->sigpend, pending_thread); - THR_SCHED_UNLOCK(curthread, curthread); - - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - for (i = 1; i < NSIG; i++) { - if (sigismember(&sigset, i) != 0) { - /* Call the handler: */ - thr_sig_invoke_handler(curthread, i, - &curthread->siginfo[i], &uc); - } - } - } + _thr_sig_rundown(curthread, &uc, NULL); } - else - THR_SCHED_UNLOCK(curthread, curthread); } /* @@ -575,10 +608,15 @@ handle_special_signals(struct kse *curkse, int sig) case SIGTTIN: case SIGTTOU: KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); - sigdelset(&_thr_proc_sigpending, SIGCONT); + SIGDELSET(_thr_proc_sigpending, SIGCONT); KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); break; - + case SIGCONT: + KSE_LOCK_ACQUIRE(curkse, &_thread_signal_lock); + SIGDELSET(_thr_proc_sigpending, SIGTSTP); + SIGDELSET(_thr_proc_sigpending, SIGTTIN); + SIGDELSET(_thr_proc_sigpending, SIGTTOU); + KSE_LOCK_RELEASE(curkse, &_thread_signal_lock); default: break; } @@ -597,72 +635,92 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) { int restart; int suppress_handler = 0; + int fromproc = 0; + struct pthread *curthread = _get_curthread(); + struct kse *curkse; + siginfo_t siginfo; - if (pthread->curframe == NULL) { - /* - * This thread is active. Just add it to the - * thread's pending set. - */ - sigaddset(&pthread->sigpend, sig); - pthread->check_pending = 1; - if (info == NULL) - build_siginfo(&pthread->siginfo[sig], sig); - else if (info != &pthread->siginfo[sig]) - memcpy(&pthread->siginfo[sig], info, - sizeof(*info)); - if ((pthread->blocked != 0) && !THR_IN_CRITICAL(pthread)) - kse_thr_interrupt(&pthread->tmbx /* XXX - restart?!?! */); - } - else { - restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + DBG_MSG(">>> _thr_sig_add\n"); - /* Make sure this signal isn't still in the pending set: */ - sigdelset(&pthread->sigpend, sig); + curkse = _get_curkse(); + restart = _thread_sigact[sig - 1].sa_flags & SA_RESTART; + fromproc = (curthread == _thr_sig_daemon); + + if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK || + pthread->state == PS_STATE_MAX) + return; /* return false */ + +#ifdef NOTYET + if ((pthread->attrs.flags & PTHREAD_SCOPE_SYSTEM) != 0) { + if (!fromproc) + kse_thr_interrupt(&pthread->tmbx, 0, sig); + return; + } +#endif + if (pthread->curframe == NULL || + SIGISMEMBER(pthread->sigmask, sig) || + THR_IN_CRITICAL(pthread)) { + /* thread is running or signal was being masked */ + if (!fromproc) { + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); + } else { + if (!_thr_getprocsig(sig, &pthread->siginfo[sig])) + return; + SIGADDSET(pthread->sigpend, sig); + } + if (!SIGISMEMBER(pthread->sigmask, sig)) { + pthread->check_pending = 1; + if (pthread->blocked != 0 && !THR_IN_CRITICAL(pthread)) + kse_thr_interrupt(&pthread->tmbx, + restart ? -2 : -1); + } + } + else { + /* if process signal not exists, just return */ + if (fromproc) { + if (!_thr_getprocsig(sig, &siginfo)) + return; + info = &siginfo; + } /* * Process according to thread state: */ switch (pthread->state) { - /* - * States which do not change when a signal is trapped: - */ case PS_DEAD: case PS_DEADLOCK: + case PS_STATE_MAX: + return; /* XXX return false */ case PS_LOCKWAIT: case PS_SUSPENDED: - case PS_STATE_MAX: /* * You can't call a signal handler for threads in these * states. */ suppress_handler = 1; break; - - /* - * States which do not need any cleanup handling when signals - * occur: - */ case PS_RUNNING: - /* - * Remove the thread from the queue before changing its - * priority: - */ - if ((pthread->flags & THR_FLAGS_IN_RUNQ) != 0) + if ((pthread->flags & THR_FLAGS_IN_RUNQ)) { THR_RUNQ_REMOVE(pthread); + pthread->active_priority |= THR_SIGNAL_PRIORITY; + THR_RUNQ_INSERT_TAIL(pthread); + } else { + /* Possible not in RUNQ and has curframe ? */ + pthread->active_priority |= THR_SIGNAL_PRIORITY; + } + suppress_handler = 1; break; - /* * States which cannot be interrupted but still require the * signal handler to run: */ case PS_COND_WAIT: case PS_MUTEX_WAIT: - /* - * Remove the thread from the wait queue. It will - * be added back to the wait queue once all signal - * handlers have been invoked. - */ - KSE_WAITQ_REMOVE(pthread->kse, pthread); break; case PS_SLEEP_WAIT: @@ -671,60 +729,64 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info) * early regardless of SA_RESTART: */ pthread->interrupted = 1; - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); break; case PS_JOIN: + break; + case PS_SIGSUSPEND: - KSE_WAITQ_REMOVE(pthread->kse, pthread); - THR_SET_STATE(pthread, PS_RUNNING); + pthread->interrupted = 1; break; case PS_SIGWAIT: + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, + sizeof(*info)); /* * The signal handler is not called for threads in * SIGWAIT. */ suppress_handler = 1; - /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + /* Wake up the thread if the signal is not blocked. */ + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Make the thread runnable: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); - break; + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } + + return; } + SIGADDSET(pthread->sigpend, sig); + if (info == NULL) + build_siginfo(&pthread->siginfo[sig], sig); + else if (info != &pthread->siginfo[sig]) + memcpy(&pthread->siginfo[sig], info, sizeof(*info)); + if (suppress_handler == 0) { /* * Setup a signal frame and save the current threads * state: */ - thr_sigframe_add(pthread, sig, info); - - if (pthread->state != PS_RUNNING) - THR_SET_STATE(pthread, PS_RUNNING); - - /* - * The thread should be removed from all scheduling - * queues at this point. Raise the priority and - * place the thread in the run queue. It is also - * possible for a signal to be sent to a suspended - * thread, mostly via pthread_kill(). If a thread - * is suspended, don't insert it into the priority - * queue; just set its state to suspended and it - * will run the signal handler when it is resumed. - */ + thr_sigframe_add(pthread); + if (pthread->flags & THR_FLAGS_IN_RUNQ) + THR_RUNQ_REMOVE(pthread); pthread->active_priority |= THR_SIGNAL_PRIORITY; - if ((pthread->flags & THR_FLAGS_IN_RUNQ) == 0) - THR_RUNQ_INSERT_TAIL(pthread); + _thr_setrunnable_unlocked(pthread); + } else { + pthread->check_pending = 1; } } + + DBG_MSG("<<< _thr_sig_add\n"); } static void @@ -749,16 +811,19 @@ thr_sig_check_state(struct pthread *pthread, int sig) break; case PS_SIGWAIT: + build_siginfo(&pthread->siginfo[sig], sig); /* Wake up the thread if the signal is blocked. */ - if (sigismember(pthread->data.sigwait, sig)) { + if (!SIGISMEMBER(pthread->sigmask, sig)) { /* Return the signal number: */ - pthread->signo = sig; - + *(pthread->data.sigwaitinfo) = pthread->siginfo[sig]; + pthread->sigmask = pthread->oldsigmask; /* Change the state of the thread to run: */ _thr_setrunnable_unlocked(pthread); - } else + } else { /* Increment the pending signal count. */ - sigaddset(&pthread->sigpend, sig); + SIGADDSET(pthread->sigpend, sig); + pthread->check_pending = 1; + } break; case PS_SIGSUSPEND: @@ -783,6 +848,12 @@ _thr_sig_send(struct pthread *pthread, int sig) { struct pthread *curthread = _get_curthread(); +#ifdef NOTYET + if ((pthread->attr.flags & PTHREAD_SCOPE_SYSTEM) == 0) { + kse_thr_interrupt(&pthread->tmbx, sig); + return; + } +#endif /* Lock the scheduling queue of the target thread. */ THR_SCHED_LOCK(curthread, pthread); @@ -792,7 +863,7 @@ _thr_sig_send(struct pthread *pthread, int sig) * Check to see if a temporary signal handler is * installed for sigwaiters: */ - if (_thread_dfl_count[sig] == 0) { + if (_thread_dfl_count[sig - 1] == 0) { /* * Deliver the signal to the process if a handler * is not installed: @@ -812,87 +883,47 @@ _thr_sig_send(struct pthread *pthread, int sig) * Check that the signal is not being ignored: */ else if (_thread_sigact[sig - 1].sa_handler != SIG_IGN) { - if (pthread->state == PS_SIGWAIT && - sigismember(pthread->data.sigwait, sig)) { - /* Return the signal number: */ - pthread->signo = sig; - - /* Change the state of the thread to run: */ - _thr_setrunnable_unlocked(pthread); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (sigismember(&pthread->tmbx.tm_context.uc_sigmask, sig)) { - /* Add the signal to the pending set: */ - sigaddset(&pthread->sigpend, sig); - THR_SCHED_UNLOCK(curthread, pthread); - } else if (pthread == curthread) { - ucontext_t uc; - siginfo_t info; - volatile int once; - - THR_SCHED_UNLOCK(curthread, pthread); - build_siginfo(&info, sig); - once = 0; - THR_GETCONTEXT(&uc); - if (once == 0) { - once = 1; - /* - * Call the signal handler for the current - * thread: - */ - thr_sig_invoke_handler(curthread, sig, - &info, &uc); - } - } else { - /* - * Perform any state changes due to signal - * arrival: - */ - _thr_sig_add(pthread, sig, NULL); - THR_SCHED_UNLOCK(curthread, pthread); - } + _thr_sig_add(pthread, sig, NULL); + THR_SCHED_UNLOCK(curthread, pthread); + /* XXX + * If thread sent signal to itself, check signals now. + * It is not really needed, _kse_critical_leave should + * have already checked signals. + */ + if (pthread == curthread && curthread->check_pending) + _thr_sig_check_pending(curthread); + } else { + THR_SCHED_UNLOCK(curthread, pthread); } } static void -thr_sigframe_add(struct pthread *thread, int sig, siginfo_t *info) +thr_sigframe_add(struct pthread *thread) { if (thread->curframe == NULL) PANIC("Thread doesn't have signal frame "); - if (thread->have_signals == 0) { + if (thread->curframe->psf_valid == 0) { + thread->curframe->psf_valid = 1; /* * Multiple signals can be added to the same signal * frame. Only save the thread's state the first time. */ thr_sigframe_save(thread, thread->curframe); - thread->have_signals = 1; - thread->flags &= THR_FLAGS_PRIVATE; } - sigaddset(&thread->curframe->psf_sigset, sig); - if (info == NULL) - build_siginfo(&thread->siginfo[sig], sig); - else if (info != &thread->siginfo[sig]) - memcpy(&thread->siginfo[sig], info, sizeof(*info)); - - /* Setup the new signal mask. */ - SIGSETOR(thread->tmbx.tm_context.uc_sigmask, - _thread_sigact[sig - 1].sa_mask); - sigaddset(&thread->tmbx.tm_context.uc_sigmask, sig); } -void +static void thr_sigframe_restore(struct pthread *thread, struct pthread_sigframe *psf) { + if (psf->psf_valid == 0) + PANIC("invalid pthread_sigframe\n"); thread->flags = psf->psf_flags; thread->interrupted = psf->psf_interrupted; thread->signo = psf->psf_signo; thread->state = psf->psf_state; thread->data = psf->psf_wait_data; thread->wakeup_time = psf->psf_wakeup_time; - if (thread->sigmask_seqno == psf->psf_seqno) - thread->tmbx.tm_context.uc_sigmask = psf->psf_sigmask; - else - thread->tmbx.tm_context.uc_sigmask = thread->sigmask; } static void @@ -906,7 +937,74 @@ thr_sigframe_save(struct pthread *thread, struct pthread_sigframe *psf) psf->psf_state = thread->state; psf->psf_wait_data = thread->data; psf->psf_wakeup_time = thread->wakeup_time; - psf->psf_sigmask = thread->tmbx.tm_context.uc_sigmask; - psf->psf_seqno = thread->sigmask_seqno; - sigemptyset(&psf->psf_sigset); } + +void +_thr_signal_init(void) +{ + sigset_t sigset; + struct sigaction act; + int i; + + SIGFILLSET(sigset); + __sys_sigprocmask(SIG_SETMASK, &sigset, &_thr_initial->sigmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Get the signal handler details: */ + else if (__sys_sigaction(i, NULL, + &_thread_sigact[i - 1]) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot read signal handler info"); + } + } + /* + * Install the signal handler for SIGINFO. It isn't + * really needed, but it is nice to have for debugging + * purposes. + */ + _thread_sigact[SIGINFO - 1].sa_flags = SA_SIGINFO | SA_RESTART; + SIGEMPTYSET(act.sa_mask); + act.sa_flags = SA_SIGINFO | SA_RESTART; + act.sa_sigaction = (__siginfohandler_t *)&_thr_sig_handler; + if (__sys_sigaction(SIGINFO, &act, NULL) != 0) { + /* + * Abort this process if signal initialisation fails: + */ + PANIC("Cannot initialize signal handler"); + } +} + +void +_thr_signal_deinit(void) +{ + sigset_t tmpmask, oldmask; + int i; + + SIGFILLSET(tmpmask); + SIG_CANTMASK(tmpmask); + __sys_sigprocmask(SIG_SETMASK, &tmpmask, &oldmask); + /* Enter a loop to get the existing signal status: */ + for (i = 1; i <= _SIG_MAXSIG; i++) { + /* Check for signals which cannot be trapped: */ + if (i == SIGKILL || i == SIGSTOP) { + } + + /* Set the signal handler details: */ + else if (__sys_sigaction(i, &_thread_sigact[i - 1], NULL) != 0) { + /* + * Abort this process if signal + * initialisation fails: + */ + PANIC("Cannot set signal handler info"); + } + } + __sys_sigprocmask(SIG_SETMASK, &oldmask, NULL); +} + diff --git a/lib/libpthread/thread/thr_sigaction.c b/lib/libpthread/thread/thr_sigaction.c index 7ede6d2..2bef7f2 100644 --- a/lib/libpthread/thread/thr_sigaction.c +++ b/lib/libpthread/thread/thr_sigaction.c @@ -43,16 +43,21 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { int ret = 0; struct sigaction gact; + struct pthread *curthread; + kse_critical_t crit; /* Check if the signal number is out of range: */ - if (sig < 1 || sig > NSIG) { + if (sig < 1 || sig > _SIG_MAXSIG) { /* Return an invalid argument: */ errno = EINVAL; ret = -1; } else { - if (_thr_initial == NULL) - _libpthread_init(NULL); + if (!_kse_isthreaded()) + return __sys_sigaction(sig, act, oact); + crit = _kse_critical_enter(); + curthread = _get_curthread(); + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); /* * Check if the existing signal action structure contents are * to be returned: @@ -99,6 +104,8 @@ _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) if (__sys_sigaction(sig, &gact, NULL) != 0) ret = -1; } + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + _kse_critical_leave(crit); } /* Return the completion status: */ diff --git a/lib/libpthread/thread/thr_sigmask.c b/lib/libpthread/thread/thr_sigmask.c index d9cb839..c8fcec8 100644 --- a/lib/libpthread/thread/thr_sigmask.c +++ b/lib/libpthread/thread/thr_sigmask.c @@ -48,33 +48,35 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) struct pthread *curthread = _get_curthread(); int ret; + if (! _kse_isthreaded()) + _kse_setthreaded(1); + + THR_SCHED_LOCK(curthread, curthread); ret = 0; if (oset != NULL) /* Return the current mask: */ - *oset = curthread->tmbx.tm_context.uc_sigmask; + *oset = curthread->sigmask; /* Check if a new signal set was provided by the caller: */ if (set != NULL) { - THR_SCHED_LOCK(curthread, curthread); - /* Process according to what to do: */ switch (how) { /* Block signals: */ case SIG_BLOCK: /* Add signals to the existing mask: */ - SIGSETOR(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETOR(curthread->sigmask, *set); break; /* Unblock signals: */ case SIG_UNBLOCK: /* Clear signals from the existing mask: */ - SIGSETNAND(curthread->tmbx.tm_context.uc_sigmask, *set); + SIGSETNAND(curthread->sigmask, *set); break; /* Set the signal process mask: */ case SIG_SETMASK: /* Set the new mask: */ - curthread->tmbx.tm_context.uc_sigmask = *set; + curthread->sigmask = *set; break; /* Trap invalid actions: */ @@ -84,13 +86,7 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) ret = -1; break; } - - if (ret == 0) { - curthread->sigmask = - curthread->tmbx.tm_context.uc_sigmask; - curthread->sigmask_seqno++; - } - + SIG_CANTMASK(curthread->sigmask); THR_SCHED_UNLOCK(curthread, curthread); /* @@ -98,6 +94,7 @@ _pthread_sigmask(int how, const sigset_t *set, sigset_t *oset) */ if (ret == 0) _thr_sig_check_pending(curthread); - } + } else + THR_SCHED_UNLOCK(curthread, curthread); return (ret); } diff --git a/lib/libpthread/thread/thr_sigpending.c b/lib/libpthread/thread/thr_sigpending.c index 7f42ff3..1b9b502 100644 --- a/lib/libpthread/thread/thr_sigpending.c +++ b/lib/libpthread/thread/thr_sigpending.c @@ -54,8 +54,13 @@ _sigpending(sigset_t *set) ret = EINVAL; } else { - *set = curthread->sigpend; + if (!_kse_isthreaded()) + return __sys_sigpending(set); + crit = _kse_critical_enter(); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + *set = curthread->sigpend; + KSE_SCHED_UNLOCK(curthread->kse, curthread->kseg); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); SIGSETOR(*set, _thr_proc_sigpending); KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); diff --git a/lib/libpthread/thread/thr_sigprocmask.c b/lib/libpthread/thread/thr_sigprocmask.c index 9cb493a..262848a 100644 --- a/lib/libpthread/thread/thr_sigprocmask.c +++ b/lib/libpthread/thread/thr_sigprocmask.c @@ -46,8 +46,9 @@ _sigprocmask(int how, const sigset_t *set, sigset_t *oset) { int ret; - ret = pthread_sigmask(how, set, oset); - if ((ret == 0) && (_kse_isthreaded() == 0)) + if (_kse_isthreaded() == 0) ret = __sys_sigprocmask(how, set, oset); + else + ret = pthread_sigmask(how, set, oset); return (ret); } diff --git a/lib/libpthread/thread/thr_sigsuspend.c b/lib/libpthread/thread/thr_sigsuspend.c index 2da790d..8d087f5 100644 --- a/lib/libpthread/thread/thr_sigsuspend.c +++ b/lib/libpthread/thread/thr_sigsuspend.c @@ -44,14 +44,19 @@ _sigsuspend(const sigset_t *set) { struct pthread *curthread = _get_curthread(); int ret = -1; + sigset_t osigmask; + + if (!_kse_isthreaded()) + return __sys_sigsuspend(set); /* Check if a new signal set was provided by the caller: */ if (set != NULL) { THR_LOCK_SWITCH(curthread); + /* Save current sigmask */ + memcpy(&osigmask, &curthread->sigmask, sizeof(osigmask)); /* Change the caller's mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - set, sizeof(sigset_t)); + memcpy(&curthread->sigmask, set, sizeof(sigset_t)); THR_SET_STATE(curthread, PS_SIGSUSPEND); @@ -61,9 +66,15 @@ _sigsuspend(const sigset_t *set) /* Always return an interrupted error: */ errno = EINTR; + THR_SCHED_LOCK(curthread, curthread); /* Restore the signal mask: */ - memcpy(&curthread->tmbx.tm_context.uc_sigmask, - &curthread->sigmask, sizeof(sigset_t)); + memcpy(&curthread->sigmask, &osigmask, sizeof(sigset_t)); + THR_SCHED_UNLOCK(curthread, curthread); + /* + * signal mask is reloaded, need to check if there is + * pending proc signal I can handle. + */ + _thr_sig_check_pending(curthread); } else { /* Return an invalid argument error: */ errno = EINVAL; diff --git a/lib/libpthread/thread/thr_sigwait.c b/lib/libpthread/thread/thr_sigwait.c index c8c7762..4b9cb69 100644 --- a/lib/libpthread/thread/thr_sigwait.c +++ b/lib/libpthread/thread/thr_sigwait.c @@ -39,10 +39,13 @@ #include <pthread.h> #include "thr_private.h" -__weak_reference(_sigwait, sigwait); +__weak_reference(__sigwait, sigwait); +__weak_reference(__sigtimedwait, sigtimedwait); +__weak_reference(__sigwaitinfo, sigwaitinfo); -int -_sigwait(const sigset_t *set, int *sig) +static int +lib_sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) { struct pthread *curthread = _get_curthread(); int ret = 0; @@ -50,8 +53,14 @@ _sigwait(const sigset_t *set, int *sig) sigset_t tempset, waitset; struct sigaction act; kse_critical_t crit; + siginfo_t siginfo; - _thr_enter_cancellation_point(curthread); + if (!_kse_isthreaded()) { + if (info == NULL) + info = &siginfo; + return __sys_sigtimedwait((sigset_t *)set, info, + (struct timespec *)timeout); + } /* * Specify the thread kernel signal handler. @@ -59,7 +68,7 @@ _sigwait(const sigset_t *set, int *sig) act.sa_handler = (void (*) ()) _thr_sig_handler; act.sa_flags = SA_RESTART | SA_SIGINFO; /* Ensure the signal handler cannot be interrupted by other signals: */ - sigfillset(&act.sa_mask); + SIGFILLSET(act.sa_mask); /* * Initialize the set of signals that will be waited on: @@ -67,41 +76,14 @@ _sigwait(const sigset_t *set, int *sig) waitset = *set; /* These signals can't be waited on. */ - sigdelset(&waitset, SIGKILL); - sigdelset(&waitset, SIGSTOP); + SIGDELSET(waitset, SIGKILL); + SIGDELSET(waitset, SIGSTOP); - /* - * Check to see if a pending signal is in the wait mask. - * This has to be atomic. - */ - tempset = curthread->sigpend; crit = _kse_critical_enter(); KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - SIGSETOR(tempset, _thr_proc_sigpending); - SIGSETAND(tempset, waitset); - if (SIGNOTEMPTY(tempset)) { - /* Enter a loop to find a pending signal: */ - for (i = 1; i < NSIG; i++) { - if (sigismember (&tempset, i)) - break; - } - - /* Clear the pending signal: */ - if (sigismember(&curthread->sigpend, i)) - sigdelset(&curthread->sigpend, i); - else - sigdelset(&_thr_proc_sigpending, i); - - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - _thr_leave_cancellation_point(curthread); - /* Return the signal number to the caller: */ - *sig = i; - return (0); - } /* - * Enter a loop to find the signals that are SIG_DFL. For + * Enter a loop to find the signals that are SIG_DFL. For * these signals we must install a dummy signal handler in * order for the kernel to pass them in to us. POSIX says * that the _application_ must explicitly install a dummy @@ -110,66 +92,158 @@ _sigwait(const sigset_t *set, int *sig) * mask because a subsequent sigaction could enable an * ignored signal. */ - sigemptyset(&tempset); - for (i = 1; i < NSIG; i++) { - if (sigismember(&waitset, i) && + SIGEMPTYSET(tempset); + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(waitset, i) && (_thread_sigact[i - 1].sa_handler == SIG_DFL)) { - _thread_dfl_count[i]++; - sigaddset(&tempset, i); - if (_thread_dfl_count[i] == 1) { + _thread_dfl_count[i - 1]++; + SIGADDSET(tempset, i); + if (_thread_dfl_count[i - 1] == 1) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */; } } } - /* Done accessing _thread_dfl_count for now. */ - KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); - _kse_critical_leave(crit); - if (ret == 0) { - /* - * Save the wait signal mask. The wait signal - * mask is independent of the threads signal mask - * and requires separate storage. - */ - curthread->data.sigwait = &waitset; + if (ret == 0) { + /* Done accessing _thread_dfl_count for now. */ + KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); + KSE_SCHED_LOCK(curthread->kse, curthread->kseg); + for (i = 1; i <= _SIG_MAXSIG; ++i) { + if (SIGISMEMBER(waitset, i) && + SIGISMEMBER(curthread->sigpend, i)) { + SIGDELSET(curthread->sigpend, i); + *info = curthread->siginfo[i]; + KSE_SCHED_UNLOCK(curthread->kse, + curthread->kseg); + _kse_critical_leave(crit); + return (i); + } + } + curthread->timeout = 0; + _thr_set_timeout(timeout); /* Wait for a signal: */ - THR_LOCK_SWITCH(curthread); + curthread->oldsigmask = curthread->sigmask; + siginfo.si_signo = 0; + curthread->data.sigwaitinfo = &siginfo; + SIGFILLSET(curthread->sigmask); + SIGSETNAND(curthread->sigmask, waitset); THR_SET_STATE(curthread, PS_SIGWAIT); _thr_sched_switch_unlocked(curthread); - /* Return the signal number to the caller: */ - *sig = curthread->signo; + /* + * Return the signal number to the caller: + * XXX Here is race, how about a signal come in before + * we reach here? so we might got an incorrect timeout + * status. + */ + if (siginfo.si_signo > 0) { + if (info) + *info = siginfo; + ret = siginfo.si_signo; + } else { + if (curthread->timeout) + errno = EAGAIN; + ret = -1; + } /* * Probably unnecessary, but since it's in a union struct * we don't know how it could be used in the future. */ - curthread->data.sigwait = NULL; + crit = _kse_critical_enter(); + curthread->data.sigwaitinfo = NULL; + /* + * Relock the array of SIG_DFL wait counts. + */ + KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); } - /* - * Relock the array of SIG_DFL wait counts. - */ - crit = _kse_critical_enter(); - KSE_LOCK_ACQUIRE(curthread->kse, &_thread_signal_lock); - /* Restore the sigactions: */ act.sa_handler = SIG_DFL; - for (i = 1; i < NSIG; i++) { - if (sigismember(&tempset, i)) { - _thread_dfl_count[i]--; + for (i = 1; i <= _SIG_MAXSIG; i++) { + if (SIGISMEMBER(tempset, i)) { + _thread_dfl_count[i - 1]--; if ((_thread_sigact[i - 1].sa_handler == SIG_DFL) && - (_thread_dfl_count[i] == 0)) { + (_thread_dfl_count[i - 1] == 0)) { if (__sys_sigaction(i, &act, NULL) != 0) - ret = -1; + /* ret = -1 */ ; } } } /* Done accessing _thread_dfl_count. */ KSE_LOCK_RELEASE(curthread->kse, &_thread_signal_lock); _kse_critical_leave(crit); + + return (ret); +} + +int +__sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, timeout); + _thr_leave_cancellation_point(curthread); + return (ret); +} + +int _sigtimedwait(const sigset_t *set, siginfo_t *info, + const struct timespec * timeout) +{ + return lib_sigtimedwait(set, info, timeout); +} + +int +__sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, info, NULL); + _thr_leave_cancellation_point(curthread); + return (ret); +} + +int +_sigwaitinfo(const sigset_t *set, siginfo_t *info) +{ + return lib_sigtimedwait(set, info, NULL); +} + +int +__sigwait(const sigset_t *set, int *sig) +{ + struct pthread *curthread = _get_curthread(); + int ret; + + _thr_enter_cancellation_point(curthread); + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } + else + ret = -1; _thr_leave_cancellation_point(curthread); + return (ret); +} - /* Return the completion status: */ +int +_sigwait(const sigset_t *set, int *sig) +{ + int ret; + + ret = lib_sigtimedwait(set, NULL, NULL); + if (ret > 0) { + *sig = ret; + ret = 0; + } else { + ret = -1; + } return (ret); } + |