diff options
Diffstat (limited to 'libpthread/linuxthreads/join.c')
-rw-r--r-- | libpthread/linuxthreads/join.c | 195 |
1 files changed, 172 insertions, 23 deletions
diff --git a/libpthread/linuxthreads/join.c b/libpthread/linuxthreads/join.c index 57ea54e34..c7e547951 100644 --- a/libpthread/linuxthreads/join.c +++ b/libpthread/linuxthreads/join.c @@ -14,31 +14,38 @@ /* Thread termination and joining */ +#include <features.h> #include <errno.h> #include <sched.h> -#include <stdlib.h> #include <unistd.h> +#include <stdlib.h> #include "pthread.h" #include "internals.h" #include "spinlock.h" #include "restart.h" -#include <not-cancel.h> +#include "debug.h" /* PDEBUG, added by StS */ -void __pthread_exit(void * retval) +libpthread_hidden_proto(pthread_exit) +void pthread_exit(void * retval) { __pthread_do_exit (retval, CURRENT_STACK_FRAME); } -strong_alias (__pthread_exit, pthread_exit) +libpthread_hidden_def (pthread_exit) void __pthread_do_exit(void *retval, char *currentframe) { pthread_descr self = thread_self(); pthread_descr joining; struct pthread_request request; + PDEBUG("self=%p, pid=%d\n", self, self->p_pid); + + /* obey POSIX behavior and prevent cancellation functions from + * being called more than once. + * http://sourceware.org/ml/libc-ports/2006-10/msg00043.html + */ + THREAD_SETMEM(self, p_cancelstate, PTHREAD_CANCEL_DISABLE); + THREAD_SETMEM(self, p_canceltype, PTHREAD_CANCEL_DEFERRED); - /* Reset the cancellation flag to avoid looping if the cleanup handlers - contain cancellation points */ - THREAD_SETMEM(self, p_canceled, 0); /* Call cleanup functions and destroy the thread-specific data */ __pthread_perform_cleanup(currentframe); __pthread_destroy_specifics(); @@ -54,7 +61,7 @@ void __pthread_do_exit(void *retval, char *currentframe) if ((mask & (__pthread_threads_events.event_bits[idx] | THREAD_GETMEM_NC(self, - p_eventbuf.eventmask.event_bits[idx]))) + p_eventbuf.eventmask).event_bits[idx])) != 0) { /* Yep, we have to signal the death. */ @@ -70,6 +77,7 @@ void __pthread_do_exit(void *retval, char *currentframe) THREAD_SETMEM(self, p_terminated, 1); /* See if someone is joining on us */ joining = THREAD_GETMEM(self, p_joining); + PDEBUG("joining = %p, pid=%d\n", joining, joining ? joining->p_pid : 0); __pthread_unlock(THREAD_GETMEM(self, p_lock)); /* Restart joining thread if any */ if (joining != NULL) restart(joining); @@ -79,25 +87,25 @@ void __pthread_do_exit(void *retval, char *currentframe) if (self == __pthread_main_thread && __pthread_manager_request >= 0) { request.req_thread = self; request.req_kind = REQ_MAIN_THREAD_EXIT; - TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, - (char *)&request, sizeof(request))); + TEMP_FAILURE_RETRY(write(__pthread_manager_request, + (char *)&request, sizeof(request))); suspend(self); /* Main thread flushes stdio streams and runs atexit functions. - It also calls a handler within LinuxThreads which sends a process exit - request to the thread manager. */ + * It also calls a handler within LinuxThreads which sends a process exit + * request to the thread manager. */ exit(0); } - /* Threads other than the main one terminate without flushing stdio streams - or running atexit functions. */ + /* Exit the process (but don't flush stdio streams, and don't run + atexit functions). */ _exit(0); } /* Function called by pthread_cancel to remove the thread from waiting on a condition variable queue. */ -static int join_extricate_func(void *obj, pthread_descr th) +static int join_extricate_func(void *obj, pthread_descr th attribute_unused) { - __volatile__ pthread_descr self = thread_self(); + volatile pthread_descr self = thread_self(); pthread_handle handle = obj; pthread_descr jo; int did_remove = 0; @@ -113,19 +121,20 @@ static int join_extricate_func(void *obj, pthread_descr th) int pthread_join(pthread_t thread_id, void ** thread_return) { - __volatile__ pthread_descr self = thread_self(); + volatile pthread_descr self = thread_self(); struct pthread_request request; pthread_handle handle = thread_handle(thread_id); pthread_descr th; pthread_extricate_if extr; int already_canceled = 0; + PDEBUG("\n"); /* Set up extrication interface */ extr.pu_object = handle; extr.pu_extricate_func = join_extricate_func; __pthread_lock(&handle->h_lock, self); - if (nonexisting_handle(handle, thread_id)) { + if (invalid_handle(handle, thread_id)) { __pthread_unlock(&handle->h_lock); return ESRCH; } @@ -155,7 +164,9 @@ int pthread_join(pthread_t thread_id, void ** thread_return) __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); } + PDEBUG("before suspend\n"); suspend(self); + PDEBUG("after suspend\n"); /* Deregister extrication interface */ __pthread_set_own_extricate_if(self, 0); @@ -175,10 +186,148 @@ int pthread_join(pthread_t thread_id, void ** thread_return) request.req_thread = self; request.req_kind = REQ_FREE; request.req_args.free.thread_id = thread_id; - TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, - (char *) &request, sizeof(request))); + TEMP_FAILURE_RETRY(write(__pthread_manager_request, + (char *) &request, sizeof(request))); + } + return 0; +} + +int pthread_tryjoin_np(pthread_t thread_id, void ** thread_return) +{ + volatile pthread_descr self = thread_self(); + struct pthread_request request; + pthread_handle handle = thread_handle(thread_id); + pthread_descr th; + int result = 0; + + /* Make sure the descriptor is valid. */ + __pthread_lock(&handle->h_lock, self); + if (invalid_handle(handle, thread_id)) { + result = ESRCH; + goto err; + } + th = handle->h_descr; + /* Is the thread joinable?. */ + if (th->p_detached || th->p_joining != NULL) { + result = EINVAL; + goto err; + } + if (th == self) { + result = EDEADLK; + goto err; + } + /* Return right away if the thread hasn't terminated yet. */ + if (! th->p_terminated) { + result = EBUSY; + goto err; + } + + /* Get return value */ + if (thread_return != NULL) *thread_return = th->p_retval; + __pthread_unlock(&handle->h_lock); + /* Send notification to thread manager */ + if (__pthread_manager_request >= 0) { + request.req_thread = self; + request.req_kind = REQ_FREE; + request.req_args.free.thread_id = thread_id; + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); } return 0; + +err: + __pthread_unlock(&handle->h_lock); + return result; +} + +int pthread_timedjoin_np(pthread_t thread_id, void ** thread_return, + const struct timespec *abstime) +{ + volatile pthread_descr self = thread_self(); + struct pthread_request request; + pthread_handle handle = thread_handle(thread_id); + pthread_descr th; + pthread_extricate_if extr; + int already_canceled = 0; + int result = 0; + PDEBUG("\n"); + + /* Set up extrication interface */ + extr.pu_object = handle; + extr.pu_extricate_func = join_extricate_func; + + __pthread_lock(&handle->h_lock, self); + if (invalid_handle(handle, thread_id)) { + result = ESRCH; + goto err; + } + th = handle->h_descr; + if (th == self) { + result = EDEADLK; + goto err; + } + /* If detached or already joined, error */ + if (th->p_detached || th->p_joining != NULL) { + result = EINVAL; + goto err; + } + /* If not terminated yet, suspend ourselves. */ + if (! th->p_terminated) { + /* Register extrication interface */ + __pthread_set_own_extricate_if(self, &extr); + if (!(THREAD_GETMEM(self, p_canceled) + && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE)) + th->p_joining = self; + else + already_canceled = 1; + __pthread_unlock(&handle->h_lock); + + if (already_canceled) { + __pthread_set_own_extricate_if(self, 0); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); + } + + PDEBUG("before suspend\n"); + result = (timedsuspend(self, abstime) == 0) ? ETIMEDOUT : 0; + PDEBUG("after suspend\n"); + /* Deregister extrication interface */ + __pthread_set_own_extricate_if(self, 0); + + /* This is a cancellation point */ + if (result == 0 + && THREAD_GETMEM(self, p_woken_by_cancel) + && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) { + THREAD_SETMEM(self, p_woken_by_cancel, 0); + __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME); + } + __pthread_lock(&handle->h_lock, self); + } + + /* We might have timed out. */ + if (result == 0) { + /* Get return value */ + if (thread_return != NULL) *thread_return = th->p_retval; + } + else + th->p_joining = NULL; + + __pthread_unlock(&handle->h_lock); + + if (result == 0) { + /* Send notification to thread manager */ + if (__pthread_manager_request >= 0) { + request.req_thread = self; + request.req_kind = REQ_FREE; + request.req_args.free.thread_id = thread_id; + TEMP_FAILURE_RETRY(__libc_write(__pthread_manager_request, + (char *) &request, sizeof(request))); + } + } + return result; + +err: + __pthread_unlock(&handle->h_lock); + return result; } int pthread_detach(pthread_t thread_id) @@ -189,7 +338,7 @@ int pthread_detach(pthread_t thread_id) pthread_descr th; __pthread_lock(&handle->h_lock, NULL); - if (nonexisting_handle(handle, thread_id)) { + if (invalid_handle(handle, thread_id)) { __pthread_unlock(&handle->h_lock); return ESRCH; } @@ -213,8 +362,8 @@ int pthread_detach(pthread_t thread_id) request.req_thread = thread_self(); request.req_kind = REQ_FREE; request.req_args.free.thread_id = thread_id; - TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request, - (char *) &request, sizeof(request))); + TEMP_FAILURE_RETRY(write(__pthread_manager_request, + (char *) &request, sizeof(request))); } return 0; } |