mirror of
https://github.com/opnsense/src.git
synced 2026-05-28 04:12:45 -04:00
POSIX says if a thread is in sigwait state, although a signal may not in
its waitset, but if the signal is not masked by the thread, the signal can interrupt the thread and signal action can be invoked by the thread, sigwait should return with errno set to EINTR. Also save and restore thread internal state(timeout and interrupted) around signal handler invoking.
This commit is contained in:
parent
e25d3184d0
commit
d80384bc8d
4 changed files with 58 additions and 22 deletions
|
|
@ -217,7 +217,10 @@ _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->sigmask, sig)) {
|
||||
} else if ((thread->state == PS_SIGWAIT &&
|
||||
SIGISMEMBER(thread->oldsigmask, sig)) ||
|
||||
(thread->state != PS_SIGWAIT &&
|
||||
SIGISMEMBER(thread->sigmask, sig))) {
|
||||
KSE_SCHED_UNLOCK(curkse, thread->kseg);
|
||||
_thr_ref_delete(NULL, thread);
|
||||
} else {
|
||||
|
|
@ -415,10 +418,10 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
|
|||
if ((pthread->state == PS_DEAD) ||
|
||||
(pthread->state == PS_DEADLOCK) ||
|
||||
THR_IS_EXITING(pthread) ||
|
||||
THR_IS_SUSPENDED(pthread) ||
|
||||
SIGISMEMBER(pthread->sigmask, sig)) {
|
||||
THR_IS_SUSPENDED(pthread)) {
|
||||
; /* Skip this thread. */
|
||||
} else if (pthread->state == PS_SIGWAIT) {
|
||||
} else if (pthread->state == PS_SIGWAIT &&
|
||||
!SIGISMEMBER(pthread->sigmask, sig)) {
|
||||
/*
|
||||
* retrieve signal from kernel, if it is job control
|
||||
* signal, and sigaction is SIG_DFL, then we will
|
||||
|
|
@ -447,7 +450,9 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
|
|||
*/
|
||||
KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
|
||||
return (NULL);
|
||||
} else {
|
||||
} else if (!SIGISMEMBER(pthread->sigmask, sig) ||
|
||||
(!SIGISMEMBER(pthread->oldsigmask, sig) &&
|
||||
pthread->state == PS_SIGWAIT)) {
|
||||
if (pthread->state == PS_SIGSUSPEND) {
|
||||
if (suspended_thread == NULL) {
|
||||
suspended_thread = pthread;
|
||||
|
|
@ -490,6 +495,8 @@ void
|
|||
_thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp,
|
||||
struct pthread_sigframe *psf)
|
||||
{
|
||||
int interrupted = curthread->interrupted;
|
||||
int timeout = curthread->timeout;
|
||||
siginfo_t siginfo;
|
||||
int i;
|
||||
kse_critical_t crit;
|
||||
|
|
@ -563,6 +570,9 @@ _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp,
|
|||
KSE_SCHED_UNLOCK(curkse, curkse->k_kseg);
|
||||
_kse_critical_leave(&curthread->tmbx);
|
||||
|
||||
curthread->interrupted = interrupted;
|
||||
curthread->timeout = timeout;
|
||||
|
||||
DBG_MSG("<<< thr_sig_rundown %p\n", curthread);
|
||||
}
|
||||
|
||||
|
|
@ -659,7 +669,8 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info)
|
|||
#endif
|
||||
|
||||
if (pthread->curframe == NULL ||
|
||||
SIGISMEMBER(pthread->sigmask, sig) ||
|
||||
(pthread->state != PS_SIGWAIT &&
|
||||
SIGISMEMBER(pthread->sigmask, sig)) ||
|
||||
THR_IN_CRITICAL(pthread)) {
|
||||
/* thread is running or signal was being masked */
|
||||
if (!fromproc) {
|
||||
|
|
@ -760,6 +771,9 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info)
|
|||
/* Increment the pending signal count. */
|
||||
SIGADDSET(pthread->sigpend, sig);
|
||||
pthread->check_pending = 1;
|
||||
pthread->interrupted = 1;
|
||||
pthread->sigmask = pthread->oldsigmask;
|
||||
_thr_setrunnable_unlocked(pthread);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -823,6 +837,9 @@ thr_sig_check_state(struct pthread *pthread, int sig)
|
|||
/* Increment the pending signal count. */
|
||||
SIGADDSET(pthread->sigpend, sig);
|
||||
pthread->check_pending = 1;
|
||||
pthread->interrupted = 1;
|
||||
pthread->sigmask = pthread->oldsigmask;
|
||||
_thr_setrunnable_unlocked(pthread);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ lib_sigtimedwait(const sigset_t *set, siginfo_t *info,
|
|||
}
|
||||
}
|
||||
curthread->timeout = 0;
|
||||
curthread->interrupted = 0;
|
||||
_thr_set_timeout(timeout);
|
||||
/* Wait for a signal: */
|
||||
curthread->oldsigmask = curthread->sigmask;
|
||||
|
|
@ -134,18 +135,18 @@ lib_sigtimedwait(const sigset_t *set, siginfo_t *info,
|
|||
_thr_sched_switch_unlocked(curthread);
|
||||
/*
|
||||
* 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) {
|
||||
ret = siginfo.si_signo;
|
||||
} else {
|
||||
if (curthread->timeout)
|
||||
if (curthread->interrupted)
|
||||
errno = EINTR;
|
||||
else if (curthread->timeout)
|
||||
errno = EAGAIN;
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
curthread->timeout = 0;
|
||||
curthread->interrupted = 0;
|
||||
/*
|
||||
* Probably unnecessary, but since it's in a union struct
|
||||
* we don't know how it could be used in the future.
|
||||
|
|
|
|||
|
|
@ -217,7 +217,10 @@ _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->sigmask, sig)) {
|
||||
} else if ((thread->state == PS_SIGWAIT &&
|
||||
SIGISMEMBER(thread->oldsigmask, sig)) ||
|
||||
(thread->state != PS_SIGWAIT &&
|
||||
SIGISMEMBER(thread->sigmask, sig))) {
|
||||
KSE_SCHED_UNLOCK(curkse, thread->kseg);
|
||||
_thr_ref_delete(NULL, thread);
|
||||
} else {
|
||||
|
|
@ -415,10 +418,10 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
|
|||
if ((pthread->state == PS_DEAD) ||
|
||||
(pthread->state == PS_DEADLOCK) ||
|
||||
THR_IS_EXITING(pthread) ||
|
||||
THR_IS_SUSPENDED(pthread) ||
|
||||
SIGISMEMBER(pthread->sigmask, sig)) {
|
||||
THR_IS_SUSPENDED(pthread)) {
|
||||
; /* Skip this thread. */
|
||||
} else if (pthread->state == PS_SIGWAIT) {
|
||||
} else if (pthread->state == PS_SIGWAIT &&
|
||||
!SIGISMEMBER(pthread->sigmask, sig)) {
|
||||
/*
|
||||
* retrieve signal from kernel, if it is job control
|
||||
* signal, and sigaction is SIG_DFL, then we will
|
||||
|
|
@ -447,7 +450,9 @@ thr_sig_find(struct kse *curkse, int sig, siginfo_t *info)
|
|||
*/
|
||||
KSE_LOCK_RELEASE(curkse, &_thread_list_lock);
|
||||
return (NULL);
|
||||
} else {
|
||||
} else if (!SIGISMEMBER(pthread->sigmask, sig) ||
|
||||
(!SIGISMEMBER(pthread->oldsigmask, sig) &&
|
||||
pthread->state == PS_SIGWAIT)) {
|
||||
if (pthread->state == PS_SIGSUSPEND) {
|
||||
if (suspended_thread == NULL) {
|
||||
suspended_thread = pthread;
|
||||
|
|
@ -490,6 +495,8 @@ void
|
|||
_thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp,
|
||||
struct pthread_sigframe *psf)
|
||||
{
|
||||
int interrupted = curthread->interrupted;
|
||||
int timeout = curthread->timeout;
|
||||
siginfo_t siginfo;
|
||||
int i;
|
||||
kse_critical_t crit;
|
||||
|
|
@ -563,6 +570,9 @@ _thr_sig_rundown(struct pthread *curthread, ucontext_t *ucp,
|
|||
KSE_SCHED_UNLOCK(curkse, curkse->k_kseg);
|
||||
_kse_critical_leave(&curthread->tmbx);
|
||||
|
||||
curthread->interrupted = interrupted;
|
||||
curthread->timeout = timeout;
|
||||
|
||||
DBG_MSG("<<< thr_sig_rundown %p\n", curthread);
|
||||
}
|
||||
|
||||
|
|
@ -659,7 +669,8 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info)
|
|||
#endif
|
||||
|
||||
if (pthread->curframe == NULL ||
|
||||
SIGISMEMBER(pthread->sigmask, sig) ||
|
||||
(pthread->state != PS_SIGWAIT &&
|
||||
SIGISMEMBER(pthread->sigmask, sig)) ||
|
||||
THR_IN_CRITICAL(pthread)) {
|
||||
/* thread is running or signal was being masked */
|
||||
if (!fromproc) {
|
||||
|
|
@ -760,6 +771,9 @@ _thr_sig_add(struct pthread *pthread, int sig, siginfo_t *info)
|
|||
/* Increment the pending signal count. */
|
||||
SIGADDSET(pthread->sigpend, sig);
|
||||
pthread->check_pending = 1;
|
||||
pthread->interrupted = 1;
|
||||
pthread->sigmask = pthread->oldsigmask;
|
||||
_thr_setrunnable_unlocked(pthread);
|
||||
}
|
||||
|
||||
return;
|
||||
|
|
@ -823,6 +837,9 @@ thr_sig_check_state(struct pthread *pthread, int sig)
|
|||
/* Increment the pending signal count. */
|
||||
SIGADDSET(pthread->sigpend, sig);
|
||||
pthread->check_pending = 1;
|
||||
pthread->interrupted = 1;
|
||||
pthread->sigmask = pthread->oldsigmask;
|
||||
_thr_setrunnable_unlocked(pthread);
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ lib_sigtimedwait(const sigset_t *set, siginfo_t *info,
|
|||
}
|
||||
}
|
||||
curthread->timeout = 0;
|
||||
curthread->interrupted = 0;
|
||||
_thr_set_timeout(timeout);
|
||||
/* Wait for a signal: */
|
||||
curthread->oldsigmask = curthread->sigmask;
|
||||
|
|
@ -134,18 +135,18 @@ lib_sigtimedwait(const sigset_t *set, siginfo_t *info,
|
|||
_thr_sched_switch_unlocked(curthread);
|
||||
/*
|
||||
* 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) {
|
||||
ret = siginfo.si_signo;
|
||||
} else {
|
||||
if (curthread->timeout)
|
||||
if (curthread->interrupted)
|
||||
errno = EINTR;
|
||||
else if (curthread->timeout)
|
||||
errno = EAGAIN;
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
curthread->timeout = 0;
|
||||
curthread->interrupted = 0;
|
||||
/*
|
||||
* Probably unnecessary, but since it's in a union struct
|
||||
* we don't know how it could be used in the future.
|
||||
|
|
|
|||
Loading…
Reference in a new issue