mirror of
https://github.com/freebsd/freebsd-src.git
synced 2024-11-30 17:23:35 +00:00
7285bccf1a
eischen (Daniel Eischen) added wrappers to protect against cancled threads orphaning internal resources. the cancelability code is still a bit fuzzy but works for test programs of my own, OpenBSD's and some examples from ORA's books. add readdir_r to both libc and libc_r add some 'const' attributes to function parameters Reviewed by: eischen, jasone
180 lines
4.0 KiB
C
180 lines
4.0 KiB
C
/*
|
|
* David Leonard <d@openbsd.org>, 1999. Public domain.
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
#include <sys/errno.h>
|
|
#include <pthread.h>
|
|
#include "pthread_private.h"
|
|
|
|
int
|
|
pthread_cancel(pthread_t pthread)
|
|
{
|
|
int ret;
|
|
|
|
if ((ret = _find_thread(pthread)) != 0) {
|
|
/* NOTHING */
|
|
} else if (pthread->state == PS_DEAD || pthread->state == PS_DEADLOCK) {
|
|
ret = 0;
|
|
} else {
|
|
/* Protect the scheduling queues: */
|
|
_thread_kern_sig_defer();
|
|
|
|
/* Check if we need to kick it back into the run queue: */
|
|
if ((pthread->cancelflags & PTHREAD_CANCEL_DISABLE) == 0)
|
|
switch (pthread->state) {
|
|
case PS_RUNNING:
|
|
/* No need to resume: */
|
|
pthread->cancelflags |= PTHREAD_CANCELLING;
|
|
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: */
|
|
if ((pthread->flags & PTHREAD_FLAGS_IN_WORKQ)
|
|
!= 0)
|
|
PTHREAD_WORKQ_REMOVE(pthread);
|
|
/* Fall through: */
|
|
case PS_SIGTHREAD:
|
|
case PS_SLEEP_WAIT:
|
|
case PS_WAIT_WAIT:
|
|
case PS_SIGSUSPEND:
|
|
case PS_SIGWAIT:
|
|
case PS_SUSPENDED:
|
|
/* Interrupt and resume: */
|
|
pthread->interrupted = 1;
|
|
pthread->cancelflags |= PTHREAD_CANCELLING;
|
|
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
|
break;
|
|
|
|
case PS_MUTEX_WAIT:
|
|
case PS_COND_WAIT:
|
|
case PS_FDLR_WAIT:
|
|
case PS_FDLW_WAIT:
|
|
case PS_FILE_WAIT:
|
|
case PS_JOIN:
|
|
/*
|
|
* 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
|
|
* needing cancellation, and set the state to
|
|
* running. When the thread resumes, it will
|
|
* exit after removing itself from the queue.
|
|
*/
|
|
pthread->interrupted = 1;
|
|
pthread->cancelflags |= PTHREAD_CANCEL_NEEDED;
|
|
PTHREAD_NEW_STATE(pthread,PS_RUNNING);
|
|
break;
|
|
|
|
case PS_DEAD:
|
|
case PS_DEADLOCK:
|
|
case PS_STATE_MAX:
|
|
/* Ignore - only here to silence -Wall: */
|
|
break;
|
|
}
|
|
/* Unprotect the scheduling queues: */
|
|
_thread_kern_sig_undefer();
|
|
|
|
ret = 0;
|
|
}
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
pthread_setcancelstate(int state, int *oldstate)
|
|
{
|
|
int ostate;
|
|
int ret;
|
|
|
|
ostate = _thread_run->cancelflags & PTHREAD_CANCEL_DISABLE;
|
|
|
|
switch (state) {
|
|
case PTHREAD_CANCEL_ENABLE:
|
|
if (oldstate != NULL)
|
|
*oldstate = ostate;
|
|
_thread_run->cancelflags &= PTHREAD_CANCEL_ENABLE;
|
|
if ((_thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS) != 0)
|
|
pthread_testcancel();
|
|
ret = 0;
|
|
break;
|
|
case PTHREAD_CANCEL_DISABLE:
|
|
if (oldstate != NULL)
|
|
*oldstate = ostate;
|
|
_thread_run->cancelflags |= PTHREAD_CANCEL_DISABLE;
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
ret = EINVAL;
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
int
|
|
pthread_setcanceltype(int type, int *oldtype)
|
|
{
|
|
int otype;
|
|
int ret;
|
|
|
|
otype = _thread_run->cancelflags & PTHREAD_CANCEL_ASYNCHRONOUS;
|
|
switch (type) {
|
|
case PTHREAD_CANCEL_ASYNCHRONOUS:
|
|
if (oldtype != NULL)
|
|
*oldtype = otype;
|
|
_thread_run->cancelflags |= PTHREAD_CANCEL_ASYNCHRONOUS;
|
|
pthread_testcancel();
|
|
ret = 0;
|
|
break;
|
|
case PTHREAD_CANCEL_DEFERRED:
|
|
if (oldtype != NULL)
|
|
*oldtype = otype;
|
|
_thread_run->cancelflags &= ~PTHREAD_CANCEL_ASYNCHRONOUS;
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
ret = EINVAL;
|
|
}
|
|
|
|
return (ret);
|
|
}
|
|
|
|
void
|
|
pthread_testcancel(void)
|
|
{
|
|
|
|
if (((_thread_run->cancelflags & PTHREAD_CANCEL_DISABLE) == 0) &&
|
|
((_thread_run->cancelflags & PTHREAD_CANCELLING) != 0)) {
|
|
/*
|
|
* It is possible for this thread to be swapped out
|
|
* while performing cancellation; do not allow it
|
|
* to be cancelled again.
|
|
*/
|
|
_thread_run->cancelflags &= ~PTHREAD_CANCELLING;
|
|
_thread_exit_cleanup();
|
|
pthread_exit(PTHREAD_CANCELED);
|
|
PANIC("cancel");
|
|
}
|
|
}
|
|
|
|
void
|
|
_thread_enter_cancellation_point(void)
|
|
{
|
|
|
|
/* Look for a cancellation before we block: */
|
|
pthread_testcancel();
|
|
_thread_run->cancelflags |= PTHREAD_AT_CANCEL_POINT;
|
|
}
|
|
|
|
void
|
|
_thread_leave_cancellation_point(void)
|
|
{
|
|
|
|
_thread_run->cancelflags &= ~PTHREAD_AT_CANCEL_POINT;
|
|
/* Look for a cancellation after we unblock: */
|
|
pthread_testcancel();
|
|
}
|