pthread_cancelable.c [plain text]
#include "pthread_internals.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/sysctl.h>
#include <sys/queue.h>
#include <machine/vmparam.h>
#include <mach/vm_statistics.h>
extern int __unix_conforming;
extern void __posix_join_cleanup(void *arg);
extern pthread_lock_t _pthread_list_lock;
extern void _pthread_testcancel(pthread_t thread, int isconforming);
extern int _pthread_reap_thread(pthread_t th, mach_port_t kernel_thread, void **value_ptr, int conforming);
extern int _pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *abstime,
int isRelative,
int isconforming);
extern int __sigwait(const sigset_t *set, int *sig);
int
pthread_join(pthread_t thread,
void **value_ptr)
{
kern_return_t kern_res;
int res = 0;
pthread_t self = pthread_self();
mach_port_t ignore;
mach_port_t kthport;
int conforming = 0;
task_t tself = mach_task_self();
#if __DARWIN_UNIX03
if (__unix_conforming == 0)
__unix_conforming = 1;
#ifdef VARIANT_CANCELABLE
_pthread_testcancel(self, 1);
#endif
#endif
if ((res = _pthread_lookup_thread(thread, &kthport, 1)) != 0)
return(res);
if (thread->sig == _PTHREAD_SIG)
{
if (thread->newstyle == 0) {
semaphore_t death = new_sem_from_pool();
LOCK(thread->lock);
if ((thread->detached & PTHREAD_CREATE_JOINABLE) &&
thread->death == SEMAPHORE_NULL)
{
assert(thread->joiner == NULL);
if (thread != self && (self == NULL || self->joiner != thread))
{
int already_exited = (thread->detached & _PTHREAD_EXITED);
thread->death = death;
thread->joiner = self;
UNLOCK(thread->lock);
if (!already_exited)
{
#if __DARWIN_UNIX03
pthread_cleanup_push(__posix_join_cleanup, (void *)thread);
do {
res = __semwait_signal(death, 0, 0, 0, 0, 0);
} while ((res < 0) && (errno == EINTR));
pthread_cleanup_pop(0);
#else
do {
PTHREAD_MACH_CALL(semaphore_wait(death), kern_res);
} while (kern_res != KERN_SUCCESS);
#endif
}
LOCK(_pthread_list_lock);
TAILQ_REMOVE(&__pthread_head, thread, plist);
#if WQ_TRACE
__kdebug_trace(0x9000010, thread, 0, 0, 16, 0);
#endif
UNLOCK(_pthread_list_lock);
while ((res = _pthread_reap_thread(thread,
thread->kernel_thread,
value_ptr, __unix_conforming)) == EAGAIN)
{
sched_yield();
}
} else {
UNLOCK(thread->lock);
res = EDEADLK;
}
} else {
UNLOCK(thread->lock);
res = EINVAL;
}
restore_sem_to_pool(death);
return res;
} else {
semaphore_t death = SEMAPHORE_NULL;
semaphore_t joinsem = SEMAPHORE_NULL;
if (thread->joiner_notify == NULL)
death = new_sem_from_pool();
LOCK(thread->lock);
if ((thread->detached & PTHREAD_CREATE_JOINABLE) &&
(thread->joiner == NULL))
{
assert(thread->kernel_thread == kthport);
if (thread != self && (self == NULL || self->joiner != thread))
{
int already_exited;
if (thread->joiner_notify == NULL) {
if (death == SEMAPHORE_NULL)
abort();
thread->joiner_notify = death;
death = SEMAPHORE_NULL;
}
joinsem = thread->joiner_notify;
thread->joiner = self;
UNLOCK(thread->lock);
if (death != SEMAPHORE_NULL) {
restore_sem_to_pool(death);
death = SEMAPHORE_NULL;
}
#if __DARWIN_UNIX03
pthread_cleanup_push(__posix_join_cleanup, (void *)thread);
do {
res = __semwait_signal(joinsem, 0, 0, 0, 0, 0);
} while ((res < 0) && (errno == EINTR));
pthread_cleanup_pop(0);
#else
do {
PTHREAD_MACH_CALL(semaphore_wait(joinsem), kern_res);
} while (kern_res != KERN_SUCCESS);
#endif
restore_sem_to_pool(joinsem);
res = _pthread_join_cleanup(thread, value_ptr, conforming);
} else {
UNLOCK(thread->lock);
res = EDEADLK;
}
} else {
UNLOCK(thread->lock);
res = EINVAL;
}
if (death != SEMAPHORE_NULL)
restore_sem_to_pool(death);
return res;
}
}
return ESRCH;
}
int
pthread_cond_wait(pthread_cond_t *cond,
pthread_mutex_t *mutex)
{
int conforming;
#if __DARWIN_UNIX03
if (__unix_conforming == 0)
__unix_conforming = 1;
#ifdef VARIANT_CANCELABLE
conforming = 1;
#else
conforming = -1;
#endif
#else
conforming = 0;
#endif
return (_pthread_cond_wait(cond, mutex, (struct timespec *)NULL, 0, conforming));
}
int
pthread_cond_timedwait(pthread_cond_t *cond,
pthread_mutex_t *mutex,
const struct timespec *abstime)
{
int conforming;
#if __DARWIN_UNIX03
if (__unix_conforming == 0)
__unix_conforming = 1;
#ifdef VARIANT_CANCELABLE
conforming = 1;
#else
conforming = -1;
#endif
#else
conforming = 0;
#endif
return (_pthread_cond_wait(cond, mutex, abstime, 0, conforming));
}
int
sigwait(const sigset_t * set, int * sig)
{
#if __DARWIN_UNIX03
int err = 0;
if (__unix_conforming == 0)
__unix_conforming = 1;
#ifdef VARIANT_CANCELABLE
_pthread_testcancel(pthread_self(), 1);
#endif
if (__sigwait(set, sig) == -1) {
err = errno;
}
return(err);
#else
return(__sigwait(set, sig));
#endif
}