mirror of
https://git.FreeBSD.org/src.git
synced 2024-12-11 09:50:12 +00:00
Fix pthread_suspend_np/pthread_resume_np. For the record, suspending a
thread waiting on an event (I/O, condvar, etc) will, when resumed using pthread_resume_np, return with EINTR. For example, suspending and resuming a thread blocked on read() will not requeue the thread for the read, but will return -1 with errno = EINTR. If the suspended thread is in a critical region, the thread is suspended as soon as it leaves the critical region. Fix a bogon in pthread_kill() where a signal was being delivered twice to threads waiting in sigwait(). Reported by (suspend/resume bug): jdp Reviewed by: jasone
This commit is contained in:
parent
8d548e1f88
commit
1d013a86ed
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=58094
@ -105,7 +105,7 @@
|
||||
else \
|
||||
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
|
||||
} \
|
||||
(thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \
|
||||
(thrd)->flags |= PTHREAD_FLAGS_IN_WAITQ; \
|
||||
} while (0)
|
||||
#define PTHREAD_WAITQ_CLEARACTIVE()
|
||||
#define PTHREAD_WAITQ_SETACTIVE()
|
||||
@ -576,6 +576,8 @@ struct pthread {
|
||||
#define PTHREAD_CANCEL_NEEDED 0x0010
|
||||
int cancelflags;
|
||||
|
||||
int suspended;
|
||||
|
||||
thread_continuation_t continuation;
|
||||
|
||||
/*
|
||||
|
@ -37,6 +37,15 @@ pthread_cancel(pthread_t pthread)
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
break;
|
||||
|
||||
case PS_SUSPENDED:
|
||||
/*
|
||||
* This thread isn't in any scheduling
|
||||
* queues; just change it's state:
|
||||
*/
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
PTHREAD_SET_STATE(pthread, PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
@ -52,7 +61,6 @@ pthread_cancel(pthread_t pthread)
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
/* Interrupt and resume: */
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
|
@ -282,8 +282,11 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
break;
|
||||
}
|
||||
|
||||
if (interrupted != 0 && _thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
if (interrupted != 0) {
|
||||
if (_thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
rval = EINTR;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
@ -449,8 +452,11 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
break;
|
||||
}
|
||||
|
||||
if (interrupted != 0 && _thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
if (interrupted != 0) {
|
||||
if (_thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
rval = EINTR;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
@ -299,10 +299,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
/* Add the thread to the linked list of all threads: */
|
||||
TAILQ_INSERT_HEAD(&_thread_list, new_thread, tle);
|
||||
|
||||
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
|
||||
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED)
|
||||
new_thread->state = PS_SUSPENDED;
|
||||
PTHREAD_WAITQ_INSERT(new_thread);
|
||||
} else {
|
||||
else {
|
||||
new_thread->state = PS_RUNNING;
|
||||
PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
|
||||
}
|
||||
|
@ -184,8 +184,10 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
switch (_thread_run->state) {
|
||||
case PS_DEAD:
|
||||
case PS_STATE_MAX: /* to silence -Wall */
|
||||
case PS_SUSPENDED:
|
||||
/*
|
||||
* Dead threads are not placed in any queue:
|
||||
* Dead and suspended threads are not placed
|
||||
* in any queue:
|
||||
*/
|
||||
break;
|
||||
|
||||
@ -227,7 +229,6 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
case PS_WAIT_WAIT:
|
||||
/* No timeouts for these states: */
|
||||
_thread_run->wakeup_time.tv_sec = -1;
|
||||
|
@ -610,8 +610,10 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
* Check to see if this thread was interrupted and
|
||||
* is still in the mutex queue of waiting threads:
|
||||
*/
|
||||
if (_thread_run->interrupted != 0)
|
||||
if (_thread_run->interrupted != 0) {
|
||||
mutex_queue_remove(*mutex, _thread_run);
|
||||
ret = EINTR;
|
||||
}
|
||||
|
||||
/* Unlock the mutex structure: */
|
||||
_SPINUNLOCK(&(*mutex)->lock);
|
||||
|
@ -44,8 +44,11 @@ pthread_resume_np(pthread_t thread)
|
||||
|
||||
/* Find the thread in the list of active threads: */
|
||||
if ((ret = _find_thread(thread)) == 0) {
|
||||
/* The thread exists. Is it suspended? */
|
||||
if (thread->state != PS_SUSPENDED) {
|
||||
/* Cancel any pending suspensions: */
|
||||
thread->suspended = 0;
|
||||
|
||||
/* Is it currently suspended? */
|
||||
if (thread->state == PS_SUSPENDED) {
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues
|
||||
* from access by the signal handler:
|
||||
@ -53,7 +56,8 @@ pthread_resume_np(pthread_t thread)
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Allow the thread to run. */
|
||||
PTHREAD_NEW_STATE(thread,PS_RUNNING);
|
||||
PTHREAD_SET_STATE(thread,PS_RUNNING);
|
||||
PTHREAD_PRIOQ_INSERT_TAIL(thread);
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
|
@ -149,7 +149,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
|
||||
signal_lock.access_lock = 0;
|
||||
else {
|
||||
sigaddset(&pthread->sigmask, sig);
|
||||
|
||||
|
||||
/*
|
||||
* Make sure not to deliver the same signal to
|
||||
* the thread twice. sigpend is potentially
|
||||
@ -160,7 +160,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
|
||||
*/
|
||||
if (sigismember(&pthread->sigpend, sig))
|
||||
sigdelset(&pthread->sigpend, sig);
|
||||
|
||||
|
||||
signal_lock.access_lock = 0;
|
||||
_thread_sig_deliver(pthread, sig);
|
||||
sigdelset(&pthread->sigmask, sig);
|
||||
@ -461,6 +461,7 @@ handle_state_change(pthread_t pthread)
|
||||
case PS_RUNNING:
|
||||
case PS_SIGTHREAD:
|
||||
case PS_STATE_MAX:
|
||||
case PS_SUSPENDED:
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -492,7 +493,6 @@ handle_state_change(pthread_t pthread)
|
||||
case PS_SIGWAIT:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_SPINBLOCK:
|
||||
case PS_SUSPENDED:
|
||||
case PS_WAIT_WAIT:
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
|
||||
PTHREAD_WAITQ_REMOVE(pthread);
|
||||
@ -628,10 +628,12 @@ _thread_sig_send(pthread_t pthread, int sig)
|
||||
!sigismember(&pthread->sigmask, sig)) {
|
||||
/* Perform any state changes due to signal arrival: */
|
||||
thread_sig_check_state(pthread, sig);
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
} else {
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
}
|
||||
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
static void finish_suspension(void *arg);
|
||||
|
||||
/* Suspend a thread: */
|
||||
int
|
||||
pthread_suspend_np(pthread_t thread)
|
||||
@ -44,22 +46,81 @@ pthread_suspend_np(pthread_t thread)
|
||||
|
||||
/* Find the thread in the list of active threads: */
|
||||
if ((ret = _find_thread(thread)) == 0) {
|
||||
/* The thread exists. Is it running? */
|
||||
if (thread->state != PS_RUNNING &&
|
||||
thread->state != PS_SUSPENDED) {
|
||||
/* The thread operation has been interrupted */
|
||||
_thread_seterrno(thread,EINTR);
|
||||
thread->interrupted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues from
|
||||
* access by the signal handler:
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Suspend the thread. */
|
||||
PTHREAD_NEW_STATE(thread,PS_SUSPENDED);
|
||||
switch (thread->state) {
|
||||
case PS_RUNNING:
|
||||
/*
|
||||
* Remove the thread from the priority queue and
|
||||
* set the state to suspended:
|
||||
*/
|
||||
PTHREAD_PRIOQ_REMOVE(thread);
|
||||
PTHREAD_SET_STATE(thread, PS_SUSPENDED);
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
case PS_POLL_WAIT:
|
||||
case PS_SELECT_WAIT:
|
||||
/*
|
||||
* Remove these threads from the work queue
|
||||
* and mark the operation as interrupted:
|
||||
*/
|
||||
if ((thread->flags & PTHREAD_FLAGS_IN_WORKQ) != 0)
|
||||
PTHREAD_WORKQ_REMOVE(thread);
|
||||
_thread_seterrno(thread,EINTR);
|
||||
thread->interrupted = 1;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
/*
|
||||
* Remove these threads from the waiting queue and
|
||||
* set their state to suspended:
|
||||
*/
|
||||
PTHREAD_WAITQ_REMOVE(thread);
|
||||
PTHREAD_SET_STATE(thread, PS_SUSPENDED);
|
||||
break;
|
||||
|
||||
case PS_MUTEX_WAIT:
|
||||
case PS_COND_WAIT:
|
||||
case PS_FDLR_WAIT:
|
||||
case PS_FDLW_WAIT:
|
||||
case PS_FILE_WAIT:
|
||||
case PS_JOIN:
|
||||
/* Mark the thread as suspended: */
|
||||
thread->suspended = 1;
|
||||
|
||||
/*
|
||||
* Threads in these states may be in queues.
|
||||
* In order to preserve queue integrity, the
|
||||
* cancelled thread must remove itself from the
|
||||
* queue. Mark the thread as interrupted and
|
||||
* set the state to running. When the thread
|
||||
* resumes, it will remove itself from the queue
|
||||
* and call the suspension completion routine.
|
||||
*/
|
||||
thread->interrupted = 1;
|
||||
_thread_seterrno(thread, EINTR);
|
||||
PTHREAD_NEW_STATE(thread, PS_RUNNING);
|
||||
thread->continuation = finish_suspension;
|
||||
break;
|
||||
|
||||
case PS_DEAD:
|
||||
case PS_DEADLOCK:
|
||||
case PS_STATE_MAX:
|
||||
case PS_SUSPENDED:
|
||||
/* Nothing needs to be done: */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
@ -69,4 +130,13 @@ pthread_suspend_np(pthread_t thread)
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
finish_suspension(void *arg)
|
||||
{
|
||||
if (_thread_run->suspended != 0)
|
||||
_thread_kern_sched_state(PS_SUSPENDED, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -37,6 +37,15 @@ pthread_cancel(pthread_t pthread)
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
break;
|
||||
|
||||
case PS_SUSPENDED:
|
||||
/*
|
||||
* This thread isn't in any scheduling
|
||||
* queues; just change it's state:
|
||||
*/
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
PTHREAD_SET_STATE(pthread, PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
@ -52,7 +61,6 @@ pthread_cancel(pthread_t pthread)
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
/* Interrupt and resume: */
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
|
@ -282,8 +282,11 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
break;
|
||||
}
|
||||
|
||||
if (interrupted != 0 && _thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
if (interrupted != 0) {
|
||||
if (_thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
rval = EINTR;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
@ -449,8 +452,11 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
break;
|
||||
}
|
||||
|
||||
if (interrupted != 0 && _thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
if (interrupted != 0) {
|
||||
if (_thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
rval = EINTR;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
@ -299,10 +299,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
/* Add the thread to the linked list of all threads: */
|
||||
TAILQ_INSERT_HEAD(&_thread_list, new_thread, tle);
|
||||
|
||||
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
|
||||
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED)
|
||||
new_thread->state = PS_SUSPENDED;
|
||||
PTHREAD_WAITQ_INSERT(new_thread);
|
||||
} else {
|
||||
else {
|
||||
new_thread->state = PS_RUNNING;
|
||||
PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
|
||||
}
|
||||
|
@ -184,8 +184,10 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
switch (_thread_run->state) {
|
||||
case PS_DEAD:
|
||||
case PS_STATE_MAX: /* to silence -Wall */
|
||||
case PS_SUSPENDED:
|
||||
/*
|
||||
* Dead threads are not placed in any queue:
|
||||
* Dead and suspended threads are not placed
|
||||
* in any queue:
|
||||
*/
|
||||
break;
|
||||
|
||||
@ -227,7 +229,6 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
case PS_WAIT_WAIT:
|
||||
/* No timeouts for these states: */
|
||||
_thread_run->wakeup_time.tv_sec = -1;
|
||||
|
@ -610,8 +610,10 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
* Check to see if this thread was interrupted and
|
||||
* is still in the mutex queue of waiting threads:
|
||||
*/
|
||||
if (_thread_run->interrupted != 0)
|
||||
if (_thread_run->interrupted != 0) {
|
||||
mutex_queue_remove(*mutex, _thread_run);
|
||||
ret = EINTR;
|
||||
}
|
||||
|
||||
/* Unlock the mutex structure: */
|
||||
_SPINUNLOCK(&(*mutex)->lock);
|
||||
|
@ -105,7 +105,7 @@
|
||||
else \
|
||||
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
|
||||
} \
|
||||
(thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \
|
||||
(thrd)->flags |= PTHREAD_FLAGS_IN_WAITQ; \
|
||||
} while (0)
|
||||
#define PTHREAD_WAITQ_CLEARACTIVE()
|
||||
#define PTHREAD_WAITQ_SETACTIVE()
|
||||
@ -576,6 +576,8 @@ struct pthread {
|
||||
#define PTHREAD_CANCEL_NEEDED 0x0010
|
||||
int cancelflags;
|
||||
|
||||
int suspended;
|
||||
|
||||
thread_continuation_t continuation;
|
||||
|
||||
/*
|
||||
|
@ -44,8 +44,11 @@ pthread_resume_np(pthread_t thread)
|
||||
|
||||
/* Find the thread in the list of active threads: */
|
||||
if ((ret = _find_thread(thread)) == 0) {
|
||||
/* The thread exists. Is it suspended? */
|
||||
if (thread->state != PS_SUSPENDED) {
|
||||
/* Cancel any pending suspensions: */
|
||||
thread->suspended = 0;
|
||||
|
||||
/* Is it currently suspended? */
|
||||
if (thread->state == PS_SUSPENDED) {
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues
|
||||
* from access by the signal handler:
|
||||
@ -53,7 +56,8 @@ pthread_resume_np(pthread_t thread)
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Allow the thread to run. */
|
||||
PTHREAD_NEW_STATE(thread,PS_RUNNING);
|
||||
PTHREAD_SET_STATE(thread,PS_RUNNING);
|
||||
PTHREAD_PRIOQ_INSERT_TAIL(thread);
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
|
@ -149,7 +149,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
|
||||
signal_lock.access_lock = 0;
|
||||
else {
|
||||
sigaddset(&pthread->sigmask, sig);
|
||||
|
||||
|
||||
/*
|
||||
* Make sure not to deliver the same signal to
|
||||
* the thread twice. sigpend is potentially
|
||||
@ -160,7 +160,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
|
||||
*/
|
||||
if (sigismember(&pthread->sigpend, sig))
|
||||
sigdelset(&pthread->sigpend, sig);
|
||||
|
||||
|
||||
signal_lock.access_lock = 0;
|
||||
_thread_sig_deliver(pthread, sig);
|
||||
sigdelset(&pthread->sigmask, sig);
|
||||
@ -461,6 +461,7 @@ handle_state_change(pthread_t pthread)
|
||||
case PS_RUNNING:
|
||||
case PS_SIGTHREAD:
|
||||
case PS_STATE_MAX:
|
||||
case PS_SUSPENDED:
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -492,7 +493,6 @@ handle_state_change(pthread_t pthread)
|
||||
case PS_SIGWAIT:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_SPINBLOCK:
|
||||
case PS_SUSPENDED:
|
||||
case PS_WAIT_WAIT:
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
|
||||
PTHREAD_WAITQ_REMOVE(pthread);
|
||||
@ -628,10 +628,12 @@ _thread_sig_send(pthread_t pthread, int sig)
|
||||
!sigismember(&pthread->sigmask, sig)) {
|
||||
/* Perform any state changes due to signal arrival: */
|
||||
thread_sig_check_state(pthread, sig);
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
} else {
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
}
|
||||
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
static void finish_suspension(void *arg);
|
||||
|
||||
/* Suspend a thread: */
|
||||
int
|
||||
pthread_suspend_np(pthread_t thread)
|
||||
@ -44,22 +46,81 @@ pthread_suspend_np(pthread_t thread)
|
||||
|
||||
/* Find the thread in the list of active threads: */
|
||||
if ((ret = _find_thread(thread)) == 0) {
|
||||
/* The thread exists. Is it running? */
|
||||
if (thread->state != PS_RUNNING &&
|
||||
thread->state != PS_SUSPENDED) {
|
||||
/* The thread operation has been interrupted */
|
||||
_thread_seterrno(thread,EINTR);
|
||||
thread->interrupted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues from
|
||||
* access by the signal handler:
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Suspend the thread. */
|
||||
PTHREAD_NEW_STATE(thread,PS_SUSPENDED);
|
||||
switch (thread->state) {
|
||||
case PS_RUNNING:
|
||||
/*
|
||||
* Remove the thread from the priority queue and
|
||||
* set the state to suspended:
|
||||
*/
|
||||
PTHREAD_PRIOQ_REMOVE(thread);
|
||||
PTHREAD_SET_STATE(thread, PS_SUSPENDED);
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
case PS_POLL_WAIT:
|
||||
case PS_SELECT_WAIT:
|
||||
/*
|
||||
* Remove these threads from the work queue
|
||||
* and mark the operation as interrupted:
|
||||
*/
|
||||
if ((thread->flags & PTHREAD_FLAGS_IN_WORKQ) != 0)
|
||||
PTHREAD_WORKQ_REMOVE(thread);
|
||||
_thread_seterrno(thread,EINTR);
|
||||
thread->interrupted = 1;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
/*
|
||||
* Remove these threads from the waiting queue and
|
||||
* set their state to suspended:
|
||||
*/
|
||||
PTHREAD_WAITQ_REMOVE(thread);
|
||||
PTHREAD_SET_STATE(thread, PS_SUSPENDED);
|
||||
break;
|
||||
|
||||
case PS_MUTEX_WAIT:
|
||||
case PS_COND_WAIT:
|
||||
case PS_FDLR_WAIT:
|
||||
case PS_FDLW_WAIT:
|
||||
case PS_FILE_WAIT:
|
||||
case PS_JOIN:
|
||||
/* Mark the thread as suspended: */
|
||||
thread->suspended = 1;
|
||||
|
||||
/*
|
||||
* Threads in these states may be in queues.
|
||||
* In order to preserve queue integrity, the
|
||||
* cancelled thread must remove itself from the
|
||||
* queue. Mark the thread as interrupted and
|
||||
* set the state to running. When the thread
|
||||
* resumes, it will remove itself from the queue
|
||||
* and call the suspension completion routine.
|
||||
*/
|
||||
thread->interrupted = 1;
|
||||
_thread_seterrno(thread, EINTR);
|
||||
PTHREAD_NEW_STATE(thread, PS_RUNNING);
|
||||
thread->continuation = finish_suspension;
|
||||
break;
|
||||
|
||||
case PS_DEAD:
|
||||
case PS_DEADLOCK:
|
||||
case PS_STATE_MAX:
|
||||
case PS_SUSPENDED:
|
||||
/* Nothing needs to be done: */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
@ -69,4 +130,13 @@ pthread_suspend_np(pthread_t thread)
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
finish_suspension(void *arg)
|
||||
{
|
||||
if (_thread_run->suspended != 0)
|
||||
_thread_kern_sched_state(PS_SUSPENDED, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -37,6 +37,15 @@ pthread_cancel(pthread_t pthread)
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
break;
|
||||
|
||||
case PS_SUSPENDED:
|
||||
/*
|
||||
* This thread isn't in any scheduling
|
||||
* queues; just change it's state:
|
||||
*/
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
PTHREAD_SET_STATE(pthread, PS_RUNNING);
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
@ -52,7 +61,6 @@ pthread_cancel(pthread_t pthread)
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
/* Interrupt and resume: */
|
||||
pthread->interrupted = 1;
|
||||
pthread->cancelflags |= PTHREAD_CANCELLING;
|
||||
|
@ -282,8 +282,11 @@ pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex)
|
||||
break;
|
||||
}
|
||||
|
||||
if (interrupted != 0 && _thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
if (interrupted != 0) {
|
||||
if (_thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
rval = EINTR;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
@ -449,8 +452,11 @@ pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
|
||||
break;
|
||||
}
|
||||
|
||||
if (interrupted != 0 && _thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
if (interrupted != 0) {
|
||||
if (_thread_run->continuation != NULL)
|
||||
_thread_run->continuation((void *) _thread_run);
|
||||
rval = EINTR;
|
||||
}
|
||||
|
||||
_thread_leave_cancellation_point();
|
||||
}
|
||||
|
@ -299,10 +299,9 @@ pthread_create(pthread_t * thread, const pthread_attr_t * attr,
|
||||
/* Add the thread to the linked list of all threads: */
|
||||
TAILQ_INSERT_HEAD(&_thread_list, new_thread, tle);
|
||||
|
||||
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED) {
|
||||
if (pattr->suspend == PTHREAD_CREATE_SUSPENDED)
|
||||
new_thread->state = PS_SUSPENDED;
|
||||
PTHREAD_WAITQ_INSERT(new_thread);
|
||||
} else {
|
||||
else {
|
||||
new_thread->state = PS_RUNNING;
|
||||
PTHREAD_PRIOQ_INSERT_TAIL(new_thread);
|
||||
}
|
||||
|
@ -184,8 +184,10 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
switch (_thread_run->state) {
|
||||
case PS_DEAD:
|
||||
case PS_STATE_MAX: /* to silence -Wall */
|
||||
case PS_SUSPENDED:
|
||||
/*
|
||||
* Dead threads are not placed in any queue:
|
||||
* Dead and suspended threads are not placed
|
||||
* in any queue:
|
||||
*/
|
||||
break;
|
||||
|
||||
@ -227,7 +229,6 @@ __asm__("fnsave %0": :"m"(*fdata));
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SIGWAIT:
|
||||
case PS_SUSPENDED:
|
||||
case PS_WAIT_WAIT:
|
||||
/* No timeouts for these states: */
|
||||
_thread_run->wakeup_time.tv_sec = -1;
|
||||
|
@ -610,8 +610,10 @@ pthread_mutex_lock(pthread_mutex_t * mutex)
|
||||
* Check to see if this thread was interrupted and
|
||||
* is still in the mutex queue of waiting threads:
|
||||
*/
|
||||
if (_thread_run->interrupted != 0)
|
||||
if (_thread_run->interrupted != 0) {
|
||||
mutex_queue_remove(*mutex, _thread_run);
|
||||
ret = EINTR;
|
||||
}
|
||||
|
||||
/* Unlock the mutex structure: */
|
||||
_SPINUNLOCK(&(*mutex)->lock);
|
||||
|
@ -105,7 +105,7 @@
|
||||
else \
|
||||
TAILQ_INSERT_BEFORE(tid,thrd,pqe); \
|
||||
} \
|
||||
(thrd)->flags | PTHREAD_FLAGS_IN_WAITQ; \
|
||||
(thrd)->flags |= PTHREAD_FLAGS_IN_WAITQ; \
|
||||
} while (0)
|
||||
#define PTHREAD_WAITQ_CLEARACTIVE()
|
||||
#define PTHREAD_WAITQ_SETACTIVE()
|
||||
@ -576,6 +576,8 @@ struct pthread {
|
||||
#define PTHREAD_CANCEL_NEEDED 0x0010
|
||||
int cancelflags;
|
||||
|
||||
int suspended;
|
||||
|
||||
thread_continuation_t continuation;
|
||||
|
||||
/*
|
||||
|
@ -44,8 +44,11 @@ pthread_resume_np(pthread_t thread)
|
||||
|
||||
/* Find the thread in the list of active threads: */
|
||||
if ((ret = _find_thread(thread)) == 0) {
|
||||
/* The thread exists. Is it suspended? */
|
||||
if (thread->state != PS_SUSPENDED) {
|
||||
/* Cancel any pending suspensions: */
|
||||
thread->suspended = 0;
|
||||
|
||||
/* Is it currently suspended? */
|
||||
if (thread->state == PS_SUSPENDED) {
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues
|
||||
* from access by the signal handler:
|
||||
@ -53,7 +56,8 @@ pthread_resume_np(pthread_t thread)
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Allow the thread to run. */
|
||||
PTHREAD_NEW_STATE(thread,PS_RUNNING);
|
||||
PTHREAD_SET_STATE(thread,PS_RUNNING);
|
||||
PTHREAD_PRIOQ_INSERT_TAIL(thread);
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
|
@ -149,7 +149,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
|
||||
signal_lock.access_lock = 0;
|
||||
else {
|
||||
sigaddset(&pthread->sigmask, sig);
|
||||
|
||||
|
||||
/*
|
||||
* Make sure not to deliver the same signal to
|
||||
* the thread twice. sigpend is potentially
|
||||
@ -160,7 +160,7 @@ _thread_sig_handler(int sig, int code, ucontext_t * scp)
|
||||
*/
|
||||
if (sigismember(&pthread->sigpend, sig))
|
||||
sigdelset(&pthread->sigpend, sig);
|
||||
|
||||
|
||||
signal_lock.access_lock = 0;
|
||||
_thread_sig_deliver(pthread, sig);
|
||||
sigdelset(&pthread->sigmask, sig);
|
||||
@ -461,6 +461,7 @@ handle_state_change(pthread_t pthread)
|
||||
case PS_RUNNING:
|
||||
case PS_SIGTHREAD:
|
||||
case PS_STATE_MAX:
|
||||
case PS_SUSPENDED:
|
||||
break;
|
||||
|
||||
/*
|
||||
@ -492,7 +493,6 @@ handle_state_change(pthread_t pthread)
|
||||
case PS_SIGWAIT:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_SPINBLOCK:
|
||||
case PS_SUSPENDED:
|
||||
case PS_WAIT_WAIT:
|
||||
if ((pthread->flags & PTHREAD_FLAGS_IN_WAITQ) != 0) {
|
||||
PTHREAD_WAITQ_REMOVE(pthread);
|
||||
@ -628,10 +628,12 @@ _thread_sig_send(pthread_t pthread, int sig)
|
||||
!sigismember(&pthread->sigmask, sig)) {
|
||||
/* Perform any state changes due to signal arrival: */
|
||||
thread_sig_check_state(pthread, sig);
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
} else {
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
}
|
||||
|
||||
/* Increment the pending signal count. */
|
||||
sigaddset(&pthread->sigpend,sig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,8 @@
|
||||
#include <pthread.h>
|
||||
#include "pthread_private.h"
|
||||
|
||||
static void finish_suspension(void *arg);
|
||||
|
||||
/* Suspend a thread: */
|
||||
int
|
||||
pthread_suspend_np(pthread_t thread)
|
||||
@ -44,22 +46,81 @@ pthread_suspend_np(pthread_t thread)
|
||||
|
||||
/* Find the thread in the list of active threads: */
|
||||
if ((ret = _find_thread(thread)) == 0) {
|
||||
/* The thread exists. Is it running? */
|
||||
if (thread->state != PS_RUNNING &&
|
||||
thread->state != PS_SUSPENDED) {
|
||||
/* The thread operation has been interrupted */
|
||||
_thread_seterrno(thread,EINTR);
|
||||
thread->interrupted = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Defer signals to protect the scheduling queues from
|
||||
* access by the signal handler:
|
||||
*/
|
||||
_thread_kern_sig_defer();
|
||||
|
||||
/* Suspend the thread. */
|
||||
PTHREAD_NEW_STATE(thread,PS_SUSPENDED);
|
||||
switch (thread->state) {
|
||||
case PS_RUNNING:
|
||||
/*
|
||||
* Remove the thread from the priority queue and
|
||||
* set the state to suspended:
|
||||
*/
|
||||
PTHREAD_PRIOQ_REMOVE(thread);
|
||||
PTHREAD_SET_STATE(thread, PS_SUSPENDED);
|
||||
break;
|
||||
|
||||
case PS_SPINBLOCK:
|
||||
case PS_FDR_WAIT:
|
||||
case PS_FDW_WAIT:
|
||||
case PS_POLL_WAIT:
|
||||
case PS_SELECT_WAIT:
|
||||
/*
|
||||
* Remove these threads from the work queue
|
||||
* and mark the operation as interrupted:
|
||||
*/
|
||||
if ((thread->flags & PTHREAD_FLAGS_IN_WORKQ) != 0)
|
||||
PTHREAD_WORKQ_REMOVE(thread);
|
||||
_thread_seterrno(thread,EINTR);
|
||||
thread->interrupted = 1;
|
||||
|
||||
/* FALLTHROUGH */
|
||||
case PS_SIGTHREAD:
|
||||
case PS_SLEEP_WAIT:
|
||||
case PS_WAIT_WAIT:
|
||||
case PS_SIGSUSPEND:
|
||||
case PS_SIGWAIT:
|
||||
/*
|
||||
* Remove these threads from the waiting queue and
|
||||
* set their state to suspended:
|
||||
*/
|
||||
PTHREAD_WAITQ_REMOVE(thread);
|
||||
PTHREAD_SET_STATE(thread, PS_SUSPENDED);
|
||||
break;
|
||||
|
||||
case PS_MUTEX_WAIT:
|
||||
case PS_COND_WAIT:
|
||||
case PS_FDLR_WAIT:
|
||||
case PS_FDLW_WAIT:
|
||||
case PS_FILE_WAIT:
|
||||
case PS_JOIN:
|
||||
/* Mark the thread as suspended: */
|
||||
thread->suspended = 1;
|
||||
|
||||
/*
|
||||
* Threads in these states may be in queues.
|
||||
* In order to preserve queue integrity, the
|
||||
* cancelled thread must remove itself from the
|
||||
* queue. Mark the thread as interrupted and
|
||||
* set the state to running. When the thread
|
||||
* resumes, it will remove itself from the queue
|
||||
* and call the suspension completion routine.
|
||||
*/
|
||||
thread->interrupted = 1;
|
||||
_thread_seterrno(thread, EINTR);
|
||||
PTHREAD_NEW_STATE(thread, PS_RUNNING);
|
||||
thread->continuation = finish_suspension;
|
||||
break;
|
||||
|
||||
case PS_DEAD:
|
||||
case PS_DEADLOCK:
|
||||
case PS_STATE_MAX:
|
||||
case PS_SUSPENDED:
|
||||
/* Nothing needs to be done: */
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Undefer and handle pending signals, yielding if
|
||||
@ -69,4 +130,13 @@ pthread_suspend_np(pthread_t thread)
|
||||
}
|
||||
return(ret);
|
||||
}
|
||||
|
||||
static void
|
||||
finish_suspension(void *arg)
|
||||
{
|
||||
if (_thread_run->suspended != 0)
|
||||
_thread_kern_sched_state(PS_SUSPENDED, __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user