diff --git a/lib/libthr/thread/thr_cancel.c b/lib/libthr/thread/thr_cancel.c index 943d53c39e92..e408e03dbcd4 100644 --- a/lib/libthr/thread/thr_cancel.c +++ b/lib/libthr/thread/thr_cancel.c @@ -85,14 +85,11 @@ _pthread_setcancelstate(int state, int *oldstate) oldval = curthread->cancel_enable; switch (state) { case PTHREAD_CANCEL_DISABLE: - THR_LOCK(curthread); curthread->cancel_enable = 0; - THR_UNLOCK(curthread); break; case PTHREAD_CANCEL_ENABLE: - THR_LOCK(curthread); curthread->cancel_enable = 1; - THR_UNLOCK(curthread); + testcancel(curthread); break; default: return (EINVAL); @@ -136,23 +133,22 @@ _pthread_testcancel(void) { struct pthread *curthread = _get_curthread(); - _thr_cancel_enter(curthread); - _thr_cancel_leave(curthread); + curthread->cancel_point = 1; + testcancel(curthread); + curthread->cancel_point = 0; } void _thr_cancel_enter(struct pthread *curthread) { - curthread->cancel_point++; - if (curthread->cancel_enable) - testcancel(curthread); + curthread->cancel_point = 1; + testcancel(curthread); } void -_thr_cancel_enter_defer(struct pthread *curthread, int maycancel) +_thr_cancel_enter2(struct pthread *curthread, int maycancel) { - curthread->cancel_defer++; - curthread->cancel_point++; + curthread->cancel_point = 1; if (__predict_false(SHOULD_CANCEL(curthread) && !THR_IN_CRITICAL(curthread))) { if (!maycancel) @@ -163,24 +159,9 @@ _thr_cancel_enter_defer(struct pthread *curthread, int maycancel) } void -_thr_cancel_leave(struct pthread *curthread) +_thr_cancel_leave(struct pthread *curthread, int maycancel) { - curthread->cancel_point--; -} - -void -_thr_cancel_leave2(struct pthread *curthread, int maycancel) -{ - if (curthread->cancel_enable && maycancel) + if (maycancel) testcancel(curthread); - curthread->cancel_point--; -} - -void -_thr_cancel_leave_defer(struct pthread *curthread, int maycancel) -{ - if (curthread->cancel_enable && maycancel) - testcancel(curthread); - curthread->cancel_point--; - curthread->cancel_defer--; + curthread->cancel_point = 0; } diff --git a/lib/libthr/thread/thr_cond.c b/lib/libthr/thread/thr_cond.c index 95970d9d0fcd..07f1b8b952cc 100644 --- a/lib/libthr/thread/thr_cond.c +++ b/lib/libthr/thread/thr_cond.c @@ -210,10 +210,10 @@ cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, if (cancel) { THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); - _thr_cancel_enter_defer(curthread, 0); + _thr_cancel_enter2(curthread, 0); ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 1); info.cond = NULL; - _thr_cancel_leave_defer(curthread, (ret != 0)); + _thr_cancel_leave(curthread, (ret != 0)); THR_CLEANUP_POP(curthread, 0); } else { ret = _thr_ucond_wait(&cv->c_kerncv, &cv->c_lock, tsp, 0); diff --git a/lib/libthr/thread/thr_create.c b/lib/libthr/thread/thr_create.c index f73a6c9231b3..2677571b424e 100644 --- a/lib/libthr/thread/thr_create.c +++ b/lib/libthr/thread/thr_create.c @@ -127,11 +127,6 @@ _pthread_create(pthread_t * thread, const pthread_attr_t * attr, if (new_thread->attr.flags & PTHREAD_CREATE_DETACHED) new_thread->tlflags |= TLFLAGS_DETACHED; - if (curthread->in_sigcancel_handler) - new_thread->unblock_sigcancel = 1; - else - new_thread->unblock_sigcancel = 0; - /* Add the new thread. */ new_thread->refcount = 1; _thr_link(curthread, new_thread); @@ -263,14 +258,6 @@ thread_start(struct pthread *curthread) if (curthread->force_exit) _pthread_exit(PTHREAD_CANCELED); - if (curthread->unblock_sigcancel) { - sigset_t set1; - - SIGEMPTYSET(set1); - SIGADDSET(set1, SIGCANCEL); - __sys_sigprocmask(SIG_UNBLOCK, &set1, NULL); - } - if (curthread->attr.suspend == THR_CREATE_SUSPENDED) { #if 0 /* Done in THR_UNLOCK() */ diff --git a/lib/libthr/thread/thr_exit.c b/lib/libthr/thread/thr_exit.c index e6facd9d85ba..10581ab23958 100644 --- a/lib/libthr/thread/thr_exit.c +++ b/lib/libthr/thread/thr_exit.c @@ -34,6 +34,8 @@ #include #include #include +#include +#include #include "un-namespace.h" #include "libc_private.h" @@ -57,6 +59,12 @@ _thread_exit(const char *fname, int lineno, const char *msg) void _pthread_exit(void *status) +{ + _pthread_exit_mask(status, NULL); +} + +void +_pthread_exit_mask(void *status, sigset_t *mask) { struct pthread *curthread = _get_curthread(); @@ -73,6 +81,17 @@ _pthread_exit(void *status) curthread->cancelling = 1; curthread->cancel_enable = 0; curthread->cancel_async = 0; + curthread->cancel_point = 0; + if (mask != NULL) + __sys_sigprocmask(SIG_SETMASK, mask, NULL); + if (curthread->unblock_sigcancel) { + sigset_t set; + + curthread->unblock_sigcancel = 0; + SIGEMPTYSET(set); + SIGADDSET(set, SIGCANCEL); + __sys_sigprocmask(SIG_UNBLOCK, mask, NULL); + } /* Save the return value: */ curthread->ret = status; diff --git a/lib/libthr/thread/thr_fork.c b/lib/libthr/thread/thr_fork.c index f20942d42be9..8e1ea6a5e72b 100644 --- a/lib/libthr/thread/thr_fork.c +++ b/lib/libthr/thread/thr_fork.c @@ -115,6 +115,7 @@ __pthread_cxa_finalize(struct dl_phdr_info *phdr_info) } THR_UMUTEX_UNLOCK(curthread, &_thr_atfork_lock); _thr_tsd_unload(phdr_info); + _thr_sigact_unload(phdr_info); } __weak_reference(_fork, fork); @@ -161,6 +162,7 @@ _fork(void) * Block all signals until we reach a safe point. */ _thr_signal_block(curthread); + _thr_signal_prefork(); /* Fork a new process: */ if ((ret = __sys_fork()) == 0) { @@ -182,6 +184,8 @@ _fork(void) _thr_umutex_init(&curthread->lock); _thr_umutex_init(&_thr_atfork_lock); + _thr_signal_postfork_child(); + if (was_threaded) _rtld_atfork_post(rtld_locks); _thr_setthreaded(0); @@ -211,6 +215,8 @@ _fork(void) /* Parent process */ errsave = errno; + _thr_signal_postfork(); + /* Ready to continue, unblock signals. */ _thr_signal_unblock(curthread); diff --git a/lib/libthr/thread/thr_init.c b/lib/libthr/thread/thr_init.c index 1bfdd287c203..ea567ee9958a 100644 --- a/lib/libthr/thread/thr_init.c +++ b/lib/libthr/thread/thr_init.c @@ -289,7 +289,6 @@ void _libpthread_init(struct pthread *curthread) { int fd, first = 0; - sigset_t sigset, oldset; /* Check if this function has already been called: */ if ((_thr_initial != NULL) && (curthread == NULL)) @@ -347,13 +346,8 @@ _libpthread_init(struct pthread *curthread) _tcb_set(curthread->tcb); if (first) { - SIGFILLSET(sigset); - SIGDELSET(sigset, SIGTRAP); - __sys_sigprocmask(SIG_SETMASK, &sigset, &oldset); - _thr_signal_init(); _thr_initial = curthread; - SIGDELSET(oldset, SIGCANCEL); - __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); + _thr_signal_init(); if (_thread_event_mask & TD_CREATE) _thr_report_creation(curthread, curthread); } diff --git a/lib/libthr/thread/thr_join.c b/lib/libthr/thread/thr_join.c index 8201abafeae9..d3c8367dc130 100644 --- a/lib/libthr/thread/thr_join.c +++ b/lib/libthr/thread/thr_join.c @@ -107,7 +107,7 @@ join_common(pthread_t pthread, void **thread_return, THREAD_LIST_UNLOCK(curthread); THR_CLEANUP_PUSH(curthread, backout_join, pthread); - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); tid = pthread->tid; while (pthread->tid != TID_TERMINATED) { @@ -127,7 +127,7 @@ join_common(pthread_t pthread, void **thread_return, break; } - _thr_cancel_leave_defer(curthread, 0); + _thr_cancel_leave(curthread, 0); THR_CLEANUP_POP(curthread, 0); if (ret == ETIMEDOUT) { diff --git a/lib/libthr/thread/thr_kern.c b/lib/libthr/thread/thr_kern.c index 649a973aabdc..3ad33ad995a2 100644 --- a/lib/libthr/thread/thr_kern.c +++ b/lib/libthr/thread/thr_kern.c @@ -60,38 +60,6 @@ _thr_setthreaded(int threaded) return (0); } -void -_thr_signal_block(struct pthread *curthread) -{ - sigset_t set; - - if (curthread->sigblock > 0) { - curthread->sigblock++; - return; - } - SIGFILLSET(set); - SIGDELSET(set, SIGBUS); - SIGDELSET(set, SIGILL); - SIGDELSET(set, SIGFPE); - SIGDELSET(set, SIGSEGV); - SIGDELSET(set, SIGTRAP); - __sys_sigprocmask(SIG_BLOCK, &set, &curthread->sigmask); - curthread->sigblock++; -} - -void -_thr_signal_unblock(struct pthread *curthread) -{ - if (--curthread->sigblock == 0) - __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); -} - -int -_thr_send_sig(struct pthread *thread, int sig) -{ - return thr_kill(thread->tid, sig); -} - void _thr_assert_lock_level() { diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index f261810e7e99..492d58d20638 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -376,12 +376,21 @@ struct pthread { /* Thread temporary signal mask. */ sigset_t sigmask; - /* Thread is in SIGCANCEL handler. */ - int in_sigcancel_handler; - - /* New thread should unblock SIGCANCEL. */ + /* Thread should unblock SIGCANCEL. */ int unblock_sigcancel; + /* In sigsuspend state */ + int in_sigsuspend; + + /* deferred signal info */ + siginfo_t deferred_siginfo; + + /* signal mask to restore. */ + sigset_t deferred_sigmask; + + /* the sigaction should be used for deferred signal. */ + struct sigaction deferred_sigact; + /* Force new thread to exit. */ int force_exit; @@ -629,6 +638,7 @@ void _thr_ref_delete_unlocked(struct pthread *, struct pthread *) __hidden; int _thr_find_thread(struct pthread *, struct pthread *, int) __hidden; void _thr_rtld_init(void) __hidden; void _thr_rtld_fini(void) __hidden; +void _thr_rtld_postfork_child(void) __hidden; int _thr_stack_alloc(struct pthread_attr *) __hidden; void _thr_stack_free(struct pthread_attr *) __hidden; void _thr_free(struct pthread *, struct pthread *) __hidden; @@ -637,10 +647,8 @@ void _thread_cleanupspecific(void) __hidden; void _thread_printf(int, const char *, ...) __hidden; void _thr_spinlock_init(void) __hidden; void _thr_cancel_enter(struct pthread *) __hidden; -void _thr_cancel_leave(struct pthread *) __hidden; -void _thr_cancel_leave2(struct pthread *, int) __hidden; -void _thr_cancel_enter_defer(struct pthread *, int) __hidden; -void _thr_cancel_leave_defer(struct pthread *, int) __hidden; +void _thr_cancel_enter2(struct pthread *, int) __hidden; +void _thr_cancel_leave(struct pthread *, int) __hidden; void _thr_testcancel(struct pthread *) __hidden; void _thr_signal_block(struct pthread *) __hidden; void _thr_signal_unblock(struct pthread *) __hidden; @@ -653,7 +661,6 @@ void _thr_hash_remove(struct pthread *) __hidden; struct pthread *_thr_hash_find(struct pthread *) __hidden; void _thr_link(struct pthread *, struct pthread *) __hidden; void _thr_unlink(struct pthread *, struct pthread *) __hidden; -void _thr_suspend_check(struct pthread *) __hidden; void _thr_assert_lock_level(void) __hidden __dead2; void _thr_ast(struct pthread *) __hidden; void _thr_once_init(void) __hidden; @@ -662,6 +669,9 @@ void _thr_report_creation(struct pthread *curthread, void _thr_report_death(struct pthread *curthread) __hidden; int _thr_getscheduler(lwpid_t, int *, struct sched_param *) __hidden; int _thr_setscheduler(lwpid_t, int, const struct sched_param *) __hidden; +void _thr_signal_prefork(void) __hidden; +void _thr_signal_postfork(void) __hidden; +void _thr_signal_postfork_child(void) __hidden; int _rtp_to_schedparam(const struct rtprio *rtp, int *policy, struct sched_param *param) __hidden; int _schedparam_to_rtp(int policy, const struct sched_param *param, @@ -672,6 +682,8 @@ int _sched_yield(void); void _pthread_cleanup_push(void (*)(void *), void *); void _pthread_cleanup_pop(int); +void _pthread_exit_mask(void *status, sigset_t *mask) __dead2 __hidden; + /* #include */ #ifdef _SYS_FCNTL_H_ @@ -687,7 +699,7 @@ int __sys_sigaction(int, const struct sigaction *, struct sigaction *); int __sys_sigpending(sigset_t *); int __sys_sigprocmask(int, const sigset_t *, sigset_t *); int __sys_sigsuspend(const sigset_t *); -int __sys_sigreturn(ucontext_t *); +int __sys_sigreturn(const ucontext_t *); int __sys_sigaltstack(const struct sigaltstack *, struct sigaltstack *); int __sys_sigwait(const sigset_t *, int *); int __sys_sigtimedwait(const sigset_t *, siginfo_t *, @@ -740,6 +752,7 @@ _thr_check_init(void) struct dl_phdr_info; void __pthread_cxa_finalize(struct dl_phdr_info *phdr_info); void _thr_tsd_unload(struct dl_phdr_info *phdr_info) __hidden; +void _thr_sigact_unload(struct dl_phdr_info *phdr_info) __hidden; __END_DECLS diff --git a/lib/libthr/thread/thr_rtld.c b/lib/libthr/thread/thr_rtld.c index d9041adcfb90..e6af7029c76d 100644 --- a/lib/libthr/thread/thr_rtld.c +++ b/lib/libthr/thread/thr_rtld.c @@ -32,6 +32,7 @@ */ #include #include +#include #include "rtld_lock.h" #include "thr_private.h" @@ -132,7 +133,7 @@ _thr_rtld_wlock_acquire(void *lock) SAVE_ERRNO(); l = (struct rtld_lock *)lock; - _thr_signal_block(curthread); + THR_CRITICAL_ENTER(curthread); while (_thr_rwlock_wrlock(&l->lock, NULL) != 0) ; RESTORE_ERRNO(); @@ -152,12 +153,9 @@ _thr_rtld_lock_release(void *lock) state = l->lock.rw_state; if (_thr_rwlock_unlock(&l->lock) == 0) { - if ((state & URWLOCK_WRITE_OWNER) == 0) { + if ((state & URWLOCK_WRITE_OWNER) == 0) curthread->rdlock_count--; - THR_CRITICAL_LEAVE(curthread); - } else { - _thr_signal_unblock(curthread); - } + THR_CRITICAL_LEAVE(curthread); } RESTORE_ERRNO(); } @@ -193,6 +191,9 @@ _thr_rtld_init(void) /* force to resolve errno() PLT */ __error(); + /* force to resolve memcpy PLT */ + memcpy(&dummy, &dummy, sizeof(dummy)); + li.lock_create = _thr_rtld_lock_create; li.lock_destroy = _thr_rtld_lock_destroy; li.rlock_acquire = _thr_rtld_rlock_acquire; diff --git a/lib/libthr/thread/thr_sig.c b/lib/libthr/thread/thr_sig.c index 312c15f4a3bf..382ff4fbbab7 100644 --- a/lib/libthr/thread/thr_sig.c +++ b/lib/libthr/thread/thr_sig.c @@ -32,11 +32,10 @@ #include #include #include -#include -#include #include #include #include "un-namespace.h" +#include "libc_private.h" #include "thr_private.h" @@ -47,7 +46,19 @@ #define DBG_MSG(x...) #endif -extern int __pause(void); +struct usigaction { + struct sigaction sigact; + struct urwlock lock; +}; + +static struct usigaction _thr_sigact[_SIG_MAXSIG]; + +static void thr_sighandler(int, siginfo_t *, void *); +static void handle_signal(struct sigaction *, int, siginfo_t *, ucontext_t *); +static void check_deferred_signal(struct pthread *); +static void check_suspend(struct pthread *); +static void check_cancel(struct pthread *curthread, ucontext_t *ucp); + int ___pause(void); int _raise(int); int __sigtimedwait(const sigset_t *set, siginfo_t *info, @@ -59,10 +70,49 @@ int _sigwaitinfo(const sigset_t *set, siginfo_t *info); int __sigwait(const sigset_t *set, int *sig); int _sigwait(const sigset_t *set, int *sig); int __sigsuspend(const sigset_t *sigmask); +int _sigaction(int, const struct sigaction *, struct sigaction *); int _setcontext(const ucontext_t *); int _swapcontext(ucontext_t *, const ucontext_t *); -static void +static const sigset_t _thr_deferset={{ + 0xffffffff & ~(_SIG_BIT(SIGBUS)|_SIG_BIT(SIGILL)|_SIG_BIT(SIGFPE)| + _SIG_BIT(SIGSEGV)|_SIG_BIT(SIGTRAP)|_SIG_BIT(SIGSYS)), + 0xffffffff, + 0xffffffff, + 0xffffffff}}; + +static const sigset_t _thr_maskset={{ + 0xffffffff, + 0xffffffff, + 0xffffffff, + 0xffffffff}}; + +void +_thr_signal_block(struct pthread *curthread) +{ + + if (curthread->sigblock > 0) { + curthread->sigblock++; + return; + } + __sys_sigprocmask(SIG_BLOCK, &_thr_maskset, &curthread->sigmask); + curthread->sigblock++; +} + +void +_thr_signal_unblock(struct pthread *curthread) +{ + if (--curthread->sigblock == 0) + __sys_sigprocmask(SIG_SETMASK, &curthread->sigmask, NULL); +} + +int +_thr_send_sig(struct pthread *thread, int sig) +{ + return thr_kill(thread->tid, sig); +} + +static inline void remove_thr_signals(sigset_t *set) { if (SIGISMEMBER(*set, SIGCANCEL)) @@ -72,87 +122,244 @@ remove_thr_signals(sigset_t *set) static const sigset_t * thr_remove_thr_signals(const sigset_t *set, sigset_t *newset) { - const sigset_t *pset; - - if (SIGISMEMBER(*set, SIGCANCEL)) { - *newset = *set; - SIGDELSET(*newset, SIGCANCEL); - pset = newset; - } else - pset = set; - return (pset); + *newset = *set; + remove_thr_signals(newset); + return (newset); } static void sigcancel_handler(int sig __unused, - siginfo_t *info __unused, ucontext_t *ucp __unused) + siginfo_t *info __unused, ucontext_t *ucp) { struct pthread *curthread = _get_curthread(); + int err; - curthread->in_sigcancel_handler++; - _thr_ast(curthread); - curthread->in_sigcancel_handler--; + if (THR_IN_CRITICAL(curthread)) + return; + err = errno; + check_suspend(curthread); + check_cancel(curthread, ucp); + errno = err; +} + +typedef void (*ohandler)(int sig, int code, + struct sigcontext *scp, char *addr, __sighandler_t *catcher); + +/* + * The signal handler wrapper is entered with all signal masked. + */ +static void +thr_sighandler(int sig, siginfo_t *info, void *_ucp) +{ + struct pthread *curthread = _get_curthread(); + ucontext_t *ucp = _ucp; + struct sigaction act; + int err; + + err = errno; + _thr_rwl_rdlock(&_thr_sigact[sig-1].lock); + act = _thr_sigact[sig-1].sigact; + _thr_rwl_unlock(&_thr_sigact[sig-1].lock); + errno = err; + + /* + * if a thread is in critical region, for example it holds low level locks, + * try to defer the signal processing, however if the signal is synchronous + * signal, it means a bad thing has happened, this is a programming error, + * resuming fault point can not help anything (normally causes deadloop), + * so here we let user code handle it immediately. + */ + if (THR_IN_CRITICAL(curthread) && SIGISMEMBER(_thr_deferset, sig)) { + memcpy(&curthread->deferred_sigact, &act, sizeof(struct sigaction)); + memcpy(&curthread->deferred_siginfo, info, sizeof(siginfo_t)); + curthread->deferred_sigmask = ucp->uc_sigmask; + /* mask all signals, we will restore it later. */ + ucp->uc_sigmask = _thr_deferset; + return; + } + + handle_signal(&act, sig, info, ucp); +} + +static void +handle_signal(struct sigaction *actp, int sig, siginfo_t *info, ucontext_t *ucp) +{ + struct pthread *curthread = _get_curthread(); + ucontext_t uc2; + __siginfohandler_t *sigfunc; + int cancel_defer; + int cancel_point; + int cancel_async; + int cancel_enable; + int in_sigsuspend; + int err; + + /* add previous level mask */ + SIGSETOR(actp->sa_mask, ucp->uc_sigmask); + + /* add this signal's mask */ + if (!(actp->sa_flags & SA_NODEFER)) + SIGADDSET(actp->sa_mask, sig); + + in_sigsuspend = curthread->in_sigsuspend; + curthread->in_sigsuspend = 0; + + /* + * if thread is in deferred cancellation mode, disable cancellation + * in signal handler. + * if user signal handler calls a cancellation point function, e.g, + * it calls write() to write data to file, because write() is a + * cancellation point, the thread is immediately cancelled if + * cancellation is pending, to avoid this problem while thread is in + * deferring mode, cancellation is temporarily disabled. + */ + cancel_defer = curthread->cancel_defer; + cancel_point = curthread->cancel_point; + cancel_async = curthread->cancel_async; + cancel_enable = curthread->cancel_enable; + curthread->cancel_point = 0; + curthread->cancel_defer = 0; + if (!cancel_async) + curthread->cancel_enable = 0; + + /* restore correct mask before calling user handler */ + __sys_sigprocmask(SIG_SETMASK, &actp->sa_mask, NULL); + + sigfunc = actp->sa_sigaction; + + /* + * We have already reset cancellation point flags, so if user's code + * longjmp()s out of its signal handler, wish its jmpbuf was set + * outside of a cancellation point, in most cases, this would be + * true. however, ther is no way to save cancel_enable in jmpbuf, + * so after setjmps() returns once more, the user code may need to + * re-set cancel_enable flag by calling pthread_setcancelstate(). + */ + if ((actp->sa_flags & SA_SIGINFO) != 0) + (*(sigfunc))(sig, info, ucp); + else { + ((ohandler)(*sigfunc))( + sig, info->si_code, (struct sigcontext *)ucp, + info->si_addr, (__sighandler_t *)sigfunc); + } + err = errno; + + curthread->in_sigsuspend = in_sigsuspend; + curthread->cancel_defer = cancel_defer; + curthread->cancel_point = cancel_point; + curthread->cancel_enable = cancel_enable; + + memcpy(&uc2, ucp, sizeof(uc2)); + SIGDELSET(uc2.uc_sigmask, SIGCANCEL); + + /* reschedule cancellation */ + check_cancel(curthread, &uc2); + errno = err; + __sys_sigreturn(&uc2); } void _thr_ast(struct pthread *curthread) { - if (THR_IN_CRITICAL(curthread)) - return; - - if (curthread->cancel_pending && curthread->cancel_enable - && !curthread->cancelling) { - if (curthread->cancel_async) { - /* - * asynchronous cancellation mode, act upon - * immediately. - */ - _pthread_exit(PTHREAD_CANCELED); - } else { - /* - * Otherwise, we are in defer mode, and we are at - * cancel point, tell kernel to not block the current - * thread on next cancelable system call. - * - * There are two cases we should call thr_wake() to - * turn on TDP_WAKEUP in kernel: - * 1) we are going to call a cancelable system call, - * non-zero cancel_point means we are already in - * cancelable state, next system call is cancelable. - * 2) because _thr_ast() may be called by - * THR_CRITICAL_LEAVE() which is used by rtld rwlock - * and any libthr internal locks, when rtld rwlock - * is used, it is mostly caused my an unresolved PLT. - * those routines may clear the TDP_WAKEUP flag by - * invoking some system calls, in those cases, we - * also should reenable the flag. - */ - if (curthread->cancel_point) { - if (curthread->cancel_defer) - thr_wake(curthread->tid); - else - _pthread_exit(PTHREAD_CANCELED); - } - } + if (!THR_IN_CRITICAL(curthread)) { + check_deferred_signal(curthread); + check_suspend(curthread); + check_cancel(curthread, NULL); } - - if (__predict_false((curthread->flags & - (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) - == THR_FLAGS_NEED_SUSPEND)) - _thr_suspend_check(curthread); } -void -_thr_suspend_check(struct pthread *curthread) +/* reschedule cancellation */ +static void +check_cancel(struct pthread *curthread, ucontext_t *ucp) +{ + + if (__predict_true(!curthread->cancel_pending || !curthread->cancel_enable || + curthread->cancelling)) + return; + + if (curthread->cancel_async) { + /* + * asynchronous cancellation mode, act upon + * immediately. + */ + _pthread_exit_mask(PTHREAD_CANCELED, + ucp? &ucp->uc_sigmask : NULL); + } else { + /* + * Otherwise, we are in defer mode, and we are at + * cancel point, tell kernel to not block the current + * thread on next cancelable system call. + * + * There are three cases we should call thr_wake() to + * turn on TDP_WAKEUP or send SIGCANCEL in kernel: + * 1) we are going to call a cancelable system call, + * non-zero cancel_point means we are already in + * cancelable state, next system call is cancelable. + * 2) because _thr_ast() may be called by + * THR_CRITICAL_LEAVE() which is used by rtld rwlock + * and any libthr internal locks, when rtld rwlock + * is used, it is mostly caused my an unresolved PLT. + * those routines may clear the TDP_WAKEUP flag by + * invoking some system calls, in those cases, we + * also should reenable the flag. + * 3) thread is in sigsuspend(), and the syscall insists + * on getting a signal before it agrees to return. + */ + if (curthread->cancel_point) { + if (curthread->in_sigsuspend && ucp) { + SIGADDSET(ucp->uc_sigmask, SIGCANCEL); + curthread->unblock_sigcancel = 1; + _thr_send_sig(curthread, SIGCANCEL); + } else + thr_wake(curthread->tid); + } + } +} + +static void +check_deferred_signal(struct pthread *curthread) +{ + ucontext_t uc; + struct sigaction act; + siginfo_t info; + volatile int first; + + if (__predict_true(curthread->deferred_siginfo.si_signo == 0)) + return; + first = 1; + getcontext(&uc); + if (first) { + first = 0; + act = curthread->deferred_sigact; + uc.uc_sigmask = curthread->deferred_sigmask; + memcpy(&info, &curthread->deferred_siginfo, sizeof(siginfo_t)); + /* remove signal */ + curthread->deferred_siginfo.si_signo = 0; + if (act.sa_flags & SA_RESETHAND) { + struct sigaction tact; + + tact = act; + tact.sa_handler = SIG_DFL; + _sigaction(info.si_signo, &tact, NULL); + } + handle_signal(&act, info.si_signo, &info, &uc); + } +} + +static void +check_suspend(struct pthread *curthread) { uint32_t cycle; - int err; + + if (__predict_true((curthread->flags & + (THR_FLAGS_NEED_SUSPEND | THR_FLAGS_SUSPENDED)) + != THR_FLAGS_NEED_SUSPEND)) + return; if (curthread->force_exit) return; - err = errno; /* * Blocks SIGCANCEL which other threads must send. */ @@ -188,15 +395,7 @@ _thr_suspend_check(struct pthread *curthread) THR_UMUTEX_UNLOCK(curthread, &(curthread)->lock); curthread->critical_count--; - /* - * Unblocks SIGCANCEL, it is possible a new SIGCANCEL is ready and - * a new signal frame will nest us, this seems a problem because - * stack will grow and overflow, but because kernel will automatically - * mask the SIGCANCEL when delivering the signal, so we at most only - * have one nesting signal frame, this should be fine. - */ _thr_signal_unblock(curthread); - errno = err; } void @@ -204,11 +403,80 @@ _thr_signal_init(void) { struct sigaction act; - /* Install cancel handler. */ - SIGEMPTYSET(act.sa_mask); - act.sa_flags = SA_SIGINFO | SA_RESTART; + /* Install SIGCANCEL handler. */ + SIGFILLSET(act.sa_mask); + act.sa_flags = SA_SIGINFO; act.sa_sigaction = (__siginfohandler_t *)&sigcancel_handler; __sys_sigaction(SIGCANCEL, &act, NULL); + + /* Unblock SIGCANCEL */ + SIGEMPTYSET(act.sa_mask); + SIGADDSET(act.sa_mask, SIGCANCEL); + __sys_sigprocmask(SIG_UNBLOCK, &act.sa_mask, NULL); +} + +/* + * called from rtld with rtld_lock locked, because rtld_lock is + * a critical region, so all signals have already beeen masked. + */ +void +_thr_sigact_unload(struct dl_phdr_info *phdr_info) +{ + struct urwlock *rwlp; + struct sigaction *actp; + struct sigaction kact; + void (*handler)(int); + int sig; + + for (sig = 1; sig < _SIG_MAXSIG; sig++) { + actp = &_thr_sigact[sig].sigact; +retry: + handler = actp->sa_handler; + if (handler != SIG_DFL && handler != SIG_IGN && + __elf_phdr_match_addr(phdr_info, handler)) { + rwlp = &_thr_sigact[sig].lock; + _thr_rwl_wrlock(rwlp); + if (handler != actp->sa_handler) { + _thr_rwl_unlock(rwlp); + goto retry; + } + actp->sa_handler = SIG_DFL; + actp->sa_flags = SA_SIGINFO; + SIGEMPTYSET(actp->sa_mask); + if (__sys_sigaction(sig, NULL, &kact) == 0 && + kact.sa_handler != SIG_DFL && + kact.sa_handler != SIG_IGN) + __sys_sigaction(sig, actp, NULL); + _thr_rwl_unlock(rwlp); + } + } +} + +void +_thr_signal_prefork(void) +{ + int i; + + for (i = 1; i < _SIG_MAXSIG; ++i) + _thr_rwl_rdlock(&_thr_sigact[i-1].lock); +} + +void +_thr_signal_postfork(void) +{ + int i; + + for (i = 1; i < _SIG_MAXSIG; ++i) + _thr_rwl_unlock(&_thr_sigact[i-1].lock); +} + +void +_thr_signal_postfork_child(void) +{ + int i; + + for (i = 1; i < _SIG_MAXSIG; ++i) + bzero(&_thr_sigact[i-1].lock, sizeof(struct urwlock)); } void @@ -221,14 +489,11 @@ __weak_reference(___pause, pause); int ___pause(void) { - struct pthread *curthread = _get_curthread(); - int ret; + sigset_t oset; - _thr_cancel_enter(curthread); - ret = __pause(); - _thr_cancel_leave(curthread); - - return ret; + if (_sigprocmask(SIG_BLOCK, NULL, &oset) == -1) + return (-1); + return (__sigsuspend(&oset)); } __weak_reference(_raise, raise); @@ -236,13 +501,7 @@ __weak_reference(_raise, raise); int _raise(int sig) { - int ret; - - if (!_thr_isthreaded()) - ret = kill(getpid(), sig); - else - ret = _thr_send_sig(_get_curthread(), sig); - return (ret); + return _thr_send_sig(_get_curthread(), sig); } __weak_reference(_sigaction, sigaction); @@ -250,14 +509,65 @@ __weak_reference(_sigaction, sigaction); int _sigaction(int sig, const struct sigaction * act, struct sigaction * oact) { - /* Check if the signal number is out of range: */ + struct sigaction newact, oldact, oldact2; + sigset_t oldset; + int ret = 0, err = 0; + if (!_SIG_VALID(sig) || sig == SIGCANCEL) { - /* Return an invalid argument: */ errno = EINVAL; return (-1); } - return __sys_sigaction(sig, act, oact); + if (act) + newact = *act; + + __sys_sigprocmask(SIG_SETMASK, &_thr_maskset, &oldset); + _thr_rwl_wrlock(&_thr_sigact[sig-1].lock); + + if (act != NULL) { + oldact2 = _thr_sigact[sig-1].sigact; + + /* + * if a new sig handler is SIG_DFL or SIG_IGN, + * don't remove old handler from _thr_sigact[], + * so deferred signals still can use the handlers, + * multiple threads invoking sigaction itself is + * a race condition, so it is not a problem. + */ + if (newact.sa_handler != SIG_DFL && + newact.sa_handler != SIG_IGN) { + _thr_sigact[sig-1].sigact = newact; + remove_thr_signals( + &_thr_sigact[sig-1].sigact.sa_mask); + newact.sa_flags &= ~SA_NODEFER; + newact.sa_flags |= SA_SIGINFO; + newact.sa_sigaction = thr_sighandler; + newact.sa_mask = _thr_maskset; /* mask all signals */ + } + if ((ret = __sys_sigaction(sig, &newact, &oldact))) { + err = errno; + _thr_sigact[sig-1].sigact = oldact2; + } + } else if (oact != NULL) { + ret = __sys_sigaction(sig, NULL, &oldact); + err = errno; + } + + if (oldact.sa_handler != SIG_DFL && + oldact.sa_handler != SIG_IGN) { + oldact = _thr_sigact[sig-1].sigact; + } + + _thr_rwl_unlock(&_thr_sigact[sig-1].lock); + __sys_sigprocmask(SIG_SETMASK, &oldset, NULL); + + if (ret == 0) { + if (oact != NULL) + *oact = oldact; + } else { + errno = err; + } + return (ret); } __weak_reference(_sigprocmask, sigprocmask); @@ -301,13 +611,24 @@ _sigsuspend(const sigset_t * set) int __sigsuspend(const sigset_t * set) { - struct pthread *curthread = _get_curthread(); + struct pthread *curthread; sigset_t newset; - int ret; + int ret, old; + curthread = _get_curthread(); + + old = curthread->in_sigsuspend; + curthread->in_sigsuspend = 1; _thr_cancel_enter(curthread); ret = __sys_sigsuspend(thr_remove_thr_signals(set, &newset)); - _thr_cancel_leave(curthread); + _thr_cancel_leave(curthread, 1); + curthread->in_sigsuspend = old; + if (curthread->unblock_sigcancel) { + curthread->unblock_sigcancel = 0; + SIGEMPTYSET(newset); + SIGADDSET(newset, SIGCANCEL); + __sys_sigprocmask(SIG_UNBLOCK, &newset, NULL); + } return (ret); } @@ -339,10 +660,10 @@ __sigtimedwait(const sigset_t *set, siginfo_t *info, sigset_t newset; int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_sigtimedwait(thr_remove_thr_signals(set, &newset), info, timeout); - _thr_cancel_leave_defer(curthread, (ret == -1)); + _thr_cancel_leave(curthread, (ret == -1)); return (ret); } @@ -366,9 +687,9 @@ __sigwaitinfo(const sigset_t *set, siginfo_t *info) sigset_t newset; int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_sigwaitinfo(thr_remove_thr_signals(set, &newset), info); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -392,9 +713,9 @@ __sigwait(const sigset_t *set, int *sig) sigset_t newset; int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_sigwait(thr_remove_thr_signals(set, &newset), sig); - _thr_cancel_leave_defer(curthread, (ret != 0)); + _thr_cancel_leave(curthread, (ret != 0)); return (ret); } @@ -404,9 +725,8 @@ _setcontext(const ucontext_t *ucp) { ucontext_t uc; - (void) memcpy(&uc, ucp, sizeof (uc)); + (void) memcpy(&uc, ucp, sizeof(uc)); remove_thr_signals(&uc.uc_sigmask); - return __sys_setcontext(&uc); } @@ -416,7 +736,7 @@ _swapcontext(ucontext_t *oucp, const ucontext_t *ucp) { ucontext_t uc; - (void) memcpy(&uc, ucp, sizeof (uc)); + (void) memcpy(&uc, ucp, sizeof(uc)); remove_thr_signals(&uc.uc_sigmask); return __sys_swapcontext(oucp, &uc); } diff --git a/lib/libthr/thread/thr_syscalls.c b/lib/libthr/thread/thr_syscalls.c index 73fa56fcc8f3..2327d7484e6e 100644 --- a/lib/libthr/thread/thr_syscalls.c +++ b/lib/libthr/thread/thr_syscalls.c @@ -169,9 +169,9 @@ __accept(int s, struct sockaddr *addr, socklen_t *addrlen) int ret; curthread = _get_curthread(); - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_accept(s, addr, addrlen); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -187,7 +187,7 @@ __aio_suspend(const struct aiocb * const iocbs[], int niocb, const struct _thr_cancel_enter(curthread); ret = __sys_aio_suspend(iocbs, niocb, timeout); - _thr_cancel_leave(curthread); + _thr_cancel_leave(curthread, 1); return (ret); } @@ -207,9 +207,9 @@ __close(int fd) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 0); + _thr_cancel_enter2(curthread, 0); ret = __sys_close(fd); - _thr_cancel_leave_defer(curthread, 1); + _thr_cancel_leave(curthread, 1); return (ret); } @@ -226,9 +226,9 @@ __connect(int fd, const struct sockaddr *name, socklen_t namelen) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 0); + _thr_cancel_enter(curthread); ret = __sys_connect(fd, name, namelen); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -245,9 +245,9 @@ ___creat(const char *path, mode_t mode) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __creat(path, mode); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -269,30 +269,15 @@ __fcntl(int fd, int cmd,...) va_list ap; va_start(ap, cmd); - switch (cmd) { - case F_DUPFD: - case F_DUP2FD: - ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); - break; - case F_SETFD: - case F_SETFL: - ret = __sys_fcntl(fd, cmd, va_arg(ap, int)); - break; - case F_GETFD: - case F_GETFL: - ret = __sys_fcntl(fd, cmd); - break; - case F_OSETLKW: - case F_SETLKW: - _thr_cancel_enter_defer(curthread, 1); + if (cmd == F_OSETLKW || cmd == F_SETLKW) { + _thr_cancel_enter(curthread); #ifdef SYSCALL_COMPAT ret = __fcntl_compat(fd, cmd, va_arg(ap, void *)); #else ret = __sys_fcntl(fd, cmd, va_arg(ap, void *)); #endif - _thr_cancel_leave_defer(curthread, ret == -1); - break; - default: + _thr_cancel_leave(curthread, ret == -1); + } else { #ifdef SYSCALL_COMPAT ret = __fcntl_compat(fd, cmd, va_arg(ap, void *)); #else @@ -316,9 +301,9 @@ __fsync(int fd) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 0); + _thr_cancel_enter2(curthread, 0); ret = __sys_fsync(fd); - _thr_cancel_leave_defer(curthread, 1); + _thr_cancel_leave(curthread, 1); return (ret); } @@ -335,9 +320,9 @@ __msync(void *addr, size_t len, int flags) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 0); + _thr_cancel_enter2(curthread, 0); ret = __sys_msync(addr, len, flags); - _thr_cancel_leave_defer(curthread, 1); + _thr_cancel_leave(curthread, 1); return ret; } @@ -353,7 +338,7 @@ __nanosleep(const struct timespec *time_to_sleep, _thr_cancel_enter(curthread); ret = __sys_nanosleep(time_to_sleep, time_remaining); - _thr_cancel_leave(curthread); + _thr_cancel_leave(curthread, 1); return (ret); } @@ -380,9 +365,9 @@ __open(const char *path, int flags,...) va_end(ap); } - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_open(path, flags, mode); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -410,9 +395,9 @@ __openat(int fd, const char *path, int flags, ...) va_end(ap); } - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_openat(fd, path, flags, mode); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -430,9 +415,9 @@ __poll(struct pollfd *fds, unsigned int nfds, int timeout) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_poll(fds, nfds, timeout); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -451,9 +436,9 @@ ___pselect(int count, fd_set *rfds, fd_set *wfds, fd_set *efds, struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_pselect(count, rfds, wfds, efds, timo, mask); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -471,9 +456,9 @@ __read(int fd, void *buf, size_t nbytes) struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_read(fd, buf, nbytes); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -491,9 +476,9 @@ __readv(int fd, const struct iovec *iov, int iovcnt) struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_readv(fd, iov, iovcnt); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -511,9 +496,9 @@ __recvfrom(int s, void *b, size_t l, int f, struct sockaddr *from, struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_recvfrom(s, b, l, f, from, fl); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -530,9 +515,9 @@ __recvmsg(int s, struct msghdr *m, int f) struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_recvmsg(s, m, f); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -550,9 +535,9 @@ __select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_select(numfds, readfds, writefds, exceptfds, timeout); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return ret; } @@ -569,9 +554,9 @@ __sendmsg(int s, const struct msghdr *m, int f) struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_sendmsg(s, m, f); - _thr_cancel_leave_defer(curthread, ret <= 0); + _thr_cancel_leave(curthread, ret <= 0); return (ret); } @@ -589,9 +574,9 @@ __sendto(int s, const void *m, size_t l, int f, const struct sockaddr *t, struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_sendto(s, m, l, f, t, tl); - _thr_cancel_leave_defer(curthread, ret <= 0); + _thr_cancel_leave(curthread, ret <= 0); return (ret); } @@ -605,7 +590,7 @@ ___sleep(unsigned int seconds) _thr_cancel_enter(curthread); ret = __sleep(seconds); - _thr_cancel_leave(curthread); + _thr_cancel_leave(curthread, 1); return (ret); } @@ -620,7 +605,7 @@ ___system(const char *string) _thr_cancel_enter(curthread); ret = __system(string); - _thr_cancel_leave(curthread); + _thr_cancel_leave(curthread, 1); return ret; } @@ -638,9 +623,9 @@ ___tcdrain(int fd) struct pthread *curthread = _get_curthread(); int ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __tcdrain(fd); - _thr_cancel_leave_defer(curthread, ret == -1); + _thr_cancel_leave(curthread, ret == -1); return (ret); } @@ -654,7 +639,7 @@ ___usleep(useconds_t useconds) _thr_cancel_enter(curthread); ret = __usleep(useconds); - _thr_cancel_leave(curthread); + _thr_cancel_leave(curthread, 1); return (ret); } @@ -672,9 +657,9 @@ ___wait(int *istat) struct pthread *curthread = _get_curthread(); pid_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __wait(istat); - _thr_cancel_leave_defer(curthread, ret <= 0); + _thr_cancel_leave(curthread, ret <= 0); return ret; } @@ -692,9 +677,9 @@ __wait3(int *status, int options, struct rusage *rusage) struct pthread *curthread = _get_curthread(); pid_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = _wait4(WAIT_ANY, status, options, rusage); - _thr_cancel_leave_defer(curthread, ret <= 0); + _thr_cancel_leave(curthread, ret <= 0); return (ret); } @@ -712,9 +697,9 @@ __wait4(pid_t pid, int *status, int options, struct rusage *rusage) struct pthread *curthread = _get_curthread(); pid_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_wait4(pid, status, options, rusage); - _thr_cancel_leave_defer(curthread, ret <= 0); + _thr_cancel_leave(curthread, ret <= 0); return ret; } @@ -732,9 +717,9 @@ ___waitpid(pid_t wpid, int *status, int options) struct pthread *curthread = _get_curthread(); pid_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __waitpid(wpid, status, options); - _thr_cancel_leave_defer(curthread, ret <= 0); + _thr_cancel_leave(curthread, ret <= 0); return ret; } @@ -752,9 +737,9 @@ __write(int fd, const void *buf, size_t nbytes) struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_write(fd, buf, nbytes); - _thr_cancel_leave_defer(curthread, (ret <= 0)); + _thr_cancel_leave(curthread, (ret <= 0)); return ret; } @@ -771,8 +756,8 @@ __writev(int fd, const struct iovec *iov, int iovcnt) struct pthread *curthread = _get_curthread(); ssize_t ret; - _thr_cancel_enter_defer(curthread, 1); + _thr_cancel_enter(curthread); ret = __sys_writev(fd, iov, iovcnt); - _thr_cancel_leave_defer(curthread, (ret <= 0)); + _thr_cancel_leave(curthread, (ret <= 0)); return ret; } diff --git a/lib/libthr/thread/thr_umtx.c b/lib/libthr/thread/thr_umtx.c index 5af923dce4fb..b712b7af880b 100644 --- a/lib/libthr/thread/thr_umtx.c +++ b/lib/libthr/thread/thr_umtx.c @@ -217,3 +217,42 @@ __thr_rwlock_unlock(struct urwlock *rwlock) { return _umtx_op_err(rwlock, UMTX_OP_RW_UNLOCK, 0, NULL, NULL); } + +void +_thr_rwl_rdlock(struct urwlock *rwlock) +{ + int ret; + + for (;;) { + if (_thr_rwlock_tryrdlock(rwlock, URWLOCK_PREFER_READER) == 0) + return; + ret = __thr_rwlock_rdlock(rwlock, URWLOCK_PREFER_READER, NULL); + if (ret == 0) + return; + if (ret != EINTR) + PANIC("rdlock error"); + } +} + +void +_thr_rwl_wrlock(struct urwlock *rwlock) +{ + int ret; + + for (;;) { + if (_thr_rwlock_trywrlock(rwlock) == 0) + return; + ret = __thr_rwlock_wrlock(rwlock, NULL); + if (ret == 0) + return; + if (ret != EINTR) + PANIC("wrlock error"); + } +} + +void +_thr_rwl_unlock(struct urwlock *rwlock) +{ + if (_thr_rwlock_unlock(rwlock)) + PANIC("unlock error"); +} diff --git a/lib/libthr/thread/thr_umtx.h b/lib/libthr/thread/thr_umtx.h index a6e462e29ca5..20489849f53f 100644 --- a/lib/libthr/thread/thr_umtx.h +++ b/lib/libthr/thread/thr_umtx.h @@ -58,6 +58,11 @@ int __thr_rwlock_rdlock(struct urwlock *rwlock, int flags, struct timespec *tsp) int __thr_rwlock_wrlock(struct urwlock *rwlock, struct timespec *tsp) __hidden; int __thr_rwlock_unlock(struct urwlock *rwlock) __hidden; +/* Internal used only */ +void _thr_rwl_rdlock(struct urwlock *rwlock) __hidden; +void _thr_rwl_wrlock(struct urwlock *rwlock) __hidden; +void _thr_rwl_unlock(struct urwlock *rwlock) __hidden; + static inline int _thr_umutex_trylock(struct umutex *mtx, uint32_t id) {