Back to index

glibc  2.9
join.c
Go to the documentation of this file.
00001 /* Linuxthreads - a simple clone()-based implementation of Posix        */
00002 /* threads for Linux.                                                   */
00003 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
00004 /*                                                                      */
00005 /* This program is free software; you can redistribute it and/or        */
00006 /* modify it under the terms of the GNU Library General Public License  */
00007 /* as published by the Free Software Foundation; either version 2       */
00008 /* of the License, or (at your option) any later version.               */
00009 /*                                                                      */
00010 /* This program is distributed in the hope that it will be useful,      */
00011 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
00012 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
00013 /* GNU Library General Public License for more details.                 */
00014 
00015 /* Thread termination and joining */
00016 
00017 #include <errno.h>
00018 #include <sched.h>
00019 #include <stdlib.h>
00020 #include <unistd.h>
00021 #include "pthread.h"
00022 #include "internals.h"
00023 #include "spinlock.h"
00024 #include "restart.h"
00025 #include <not-cancel.h>
00026 
00027 void __pthread_exit(void * retval)
00028 {
00029   __pthread_do_exit (retval, CURRENT_STACK_FRAME);
00030 }
00031 strong_alias (__pthread_exit, pthread_exit);
00032 
00033 void __pthread_do_exit(void *retval, char *currentframe)
00034 {
00035   pthread_descr self = thread_self();
00036   pthread_descr joining;
00037   struct pthread_request request;
00038 
00039   /* Reset the cancellation flag to avoid looping if the cleanup handlers
00040      contain cancellation points */
00041   THREAD_SETMEM(self, p_canceled, 0);
00042   /* Call cleanup functions and destroy the thread-specific data */
00043   __pthread_perform_cleanup(currentframe);
00044   __pthread_destroy_specifics();
00045   /* Store return value */
00046   __pthread_lock(THREAD_GETMEM(self, p_lock), self);
00047   THREAD_SETMEM(self, p_retval, retval);
00048   /* See whether we have to signal the death.  */
00049   if (THREAD_GETMEM(self, p_report_events))
00050     {
00051       /* See whether TD_DEATH is in any of the mask.  */
00052       int idx = __td_eventword (TD_DEATH);
00053       uint32_t mask = __td_eventmask (TD_DEATH);
00054 
00055       if ((mask & (__pthread_threads_events.event_bits[idx]
00056                  | THREAD_GETMEM_NC(self,
00057                                   p_eventbuf.eventmask.event_bits[idx])))
00058          != 0)
00059        {
00060          /* Yep, we have to signal the death.  */
00061          THREAD_SETMEM(self, p_eventbuf.eventnum, TD_DEATH);
00062          THREAD_SETMEM(self, p_eventbuf.eventdata, self);
00063          __pthread_last_event = self;
00064 
00065          /* Now call the function to signal the event.  */
00066          __linuxthreads_death_event();
00067        }
00068     }
00069   /* Say that we've terminated */
00070   THREAD_SETMEM(self, p_terminated, 1);
00071   /* See if someone is joining on us */
00072   joining = THREAD_GETMEM(self, p_joining);
00073   __pthread_unlock(THREAD_GETMEM(self, p_lock));
00074   /* Restart joining thread if any */
00075   if (joining != NULL) restart(joining);
00076   /* If this is the initial thread, block until all threads have terminated.
00077      If another thread calls exit, we'll be terminated from our signal
00078      handler. */
00079   if (self == __pthread_main_thread && __pthread_manager_request >= 0) {
00080     request.req_thread = self;
00081     request.req_kind = REQ_MAIN_THREAD_EXIT;
00082     TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
00083                                    (char *)&request, sizeof(request)));
00084     suspend(self);
00085     /* Main thread flushes stdio streams and runs atexit functions.
00086        It also calls a handler within LinuxThreads which sends a process exit
00087        request to the thread manager. */
00088     exit(0);
00089   }
00090   /* Threads other than the main one  terminate without flushing stdio streams
00091      or running atexit functions. */
00092   _exit(0);
00093 }
00094 
00095 /* Function called by pthread_cancel to remove the thread from
00096    waiting on a condition variable queue. */
00097 
00098 static int join_extricate_func(void *obj, pthread_descr th)
00099 {
00100   volatile pthread_descr self = thread_self();
00101   pthread_handle handle = obj;
00102   pthread_descr jo;
00103   int did_remove = 0;
00104 
00105   __pthread_lock(&handle->h_lock, self);
00106   jo = handle->h_descr;
00107   did_remove = jo->p_joining != NULL;
00108   jo->p_joining = NULL;
00109   __pthread_unlock(&handle->h_lock);
00110 
00111   return did_remove;
00112 }
00113 
00114 int pthread_join(pthread_t thread_id, void ** thread_return)
00115 {
00116   volatile pthread_descr self = thread_self();
00117   struct pthread_request request;
00118   pthread_handle handle = thread_handle(thread_id);
00119   pthread_descr th;
00120   pthread_extricate_if extr;
00121   int already_canceled = 0;
00122 
00123   /* Set up extrication interface */
00124   extr.pu_object = handle;
00125   extr.pu_extricate_func = join_extricate_func;
00126 
00127   __pthread_lock(&handle->h_lock, self);
00128   if (nonexisting_handle(handle, thread_id)) {
00129     __pthread_unlock(&handle->h_lock);
00130     return ESRCH;
00131   }
00132   th = handle->h_descr;
00133   if (th == self) {
00134     __pthread_unlock(&handle->h_lock);
00135     return EDEADLK;
00136   }
00137   /* If detached or already joined, error */
00138   if (th->p_detached || th->p_joining != NULL) {
00139     __pthread_unlock(&handle->h_lock);
00140     return EINVAL;
00141   }
00142   /* If not terminated yet, suspend ourselves. */
00143   if (! th->p_terminated) {
00144     /* Register extrication interface */
00145     __pthread_set_own_extricate_if(self, &extr);
00146     if (!(THREAD_GETMEM(self, p_canceled)
00147        && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
00148       th->p_joining = self;
00149     else
00150       already_canceled = 1;
00151     __pthread_unlock(&handle->h_lock);
00152 
00153     if (already_canceled) {
00154       __pthread_set_own_extricate_if(self, 0);
00155       __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00156     }
00157 
00158     suspend(self);
00159     /* Deregister extrication interface */
00160     __pthread_set_own_extricate_if(self, 0);
00161 
00162     /* This is a cancellation point */
00163     if (THREAD_GETMEM(self, p_woken_by_cancel)
00164        && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
00165       THREAD_SETMEM(self, p_woken_by_cancel, 0);
00166       __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
00167     }
00168     __pthread_lock(&handle->h_lock, self);
00169   }
00170   /* Get return value */
00171   if (thread_return != NULL) *thread_return = th->p_retval;
00172   __pthread_unlock(&handle->h_lock);
00173   /* Send notification to thread manager */
00174   if (__pthread_manager_request >= 0) {
00175     request.req_thread = self;
00176     request.req_kind = REQ_FREE;
00177     request.req_args.free.thread_id = thread_id;
00178     TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
00179                                    (char *) &request, sizeof(request)));
00180   }
00181   return 0;
00182 }
00183 
00184 int pthread_detach(pthread_t thread_id)
00185 {
00186   int terminated;
00187   struct pthread_request request;
00188   pthread_handle handle = thread_handle(thread_id);
00189   pthread_descr th;
00190 
00191   __pthread_lock(&handle->h_lock, NULL);
00192   if (nonexisting_handle(handle, thread_id)) {
00193     __pthread_unlock(&handle->h_lock);
00194     return ESRCH;
00195   }
00196   th = handle->h_descr;
00197   /* If already detached, error */
00198   if (th->p_detached) {
00199     __pthread_unlock(&handle->h_lock);
00200     return EINVAL;
00201   }
00202   /* If already joining, don't do anything. */
00203   if (th->p_joining != NULL) {
00204     __pthread_unlock(&handle->h_lock);
00205     return 0;
00206   }
00207   /* Mark as detached */
00208   th->p_detached = 1;
00209   terminated = th->p_terminated;
00210   __pthread_unlock(&handle->h_lock);
00211   /* If already terminated, notify thread manager to reclaim resources */
00212   if (terminated && __pthread_manager_request >= 0) {
00213     request.req_thread = thread_self();
00214     request.req_kind = REQ_FREE;
00215     request.req_args.free.thread_id = thread_id;
00216     TEMP_FAILURE_RETRY(write_not_cancel(__pthread_manager_request,
00217                                    (char *) &request, sizeof(request)));
00218   }
00219   return 0;
00220 }