Update.
[kopensolaris-gnu/glibc.git] / linuxthreads / join.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /*                                                                      */
5 /* This program is free software; you can redistribute it and/or        */
6 /* modify it under the terms of the GNU Library General Public License  */
7 /* as published by the Free Software Foundation; either version 2       */
8 /* of the License, or (at your option) any later version.               */
9 /*                                                                      */
10 /* This program is distributed in the hope that it will be useful,      */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of       */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the        */
13 /* GNU Library General Public License for more details.                 */
14
15 /* Thread termination and joining */
16
17 #include <errno.h>
18 #include <sched.h>
19 #include <unistd.h>
20 #include "pthread.h"
21 #include "internals.h"
22 #include "spinlock.h"
23 #include "restart.h"
24
25 void pthread_exit(void * retval)
26 {
27   pthread_descr self = thread_self();
28   pthread_descr joining;
29   struct pthread_request request;
30
31   /* Reset the cancellation flag to avoid looping if the cleanup handlers
32      contain cancellation points */
33   self->p_canceled = 0;
34   /* Call cleanup functions and destroy the thread-specific data */
35   __pthread_perform_cleanup();
36   __pthread_destroy_specifics();
37   /* Store return value */
38   acquire(self->p_spinlock);
39   self->p_retval = retval;
40   /* Say that we've terminated */
41   self->p_terminated = 1;
42   /* See if someone is joining on us */
43   joining = self->p_joining;
44   release(self->p_spinlock);
45   /* Restart joining thread if any */
46   if (joining != NULL) restart(joining);
47   /* If this is the initial thread, block until all threads have terminated.
48      If another thread calls exit, we'll be terminated from our signal
49      handler. */
50   if (self == __pthread_main_thread && __pthread_manager_request >= 0) {
51     request.req_thread = self;
52     request.req_kind = REQ_MAIN_THREAD_EXIT;
53     __libc_write(__pthread_manager_request, (char *)&request, sizeof(request));
54     suspend(self);
55   }
56   /* Exit the process (but don't flush stdio streams, and don't run
57      atexit functions). */
58   _exit(0);
59 }
60
61 int pthread_join(pthread_t thread_id, void ** thread_return)
62 {
63   volatile pthread_descr self = thread_self();
64   struct pthread_request request;
65   pthread_handle handle = thread_handle(thread_id);
66   pthread_descr th;
67
68   acquire(&handle->h_spinlock);
69   if (invalid_handle(handle, thread_id)) {
70     release(&handle->h_spinlock);
71     return ESRCH;
72   }
73   th = handle->h_descr;
74   if (th == self) {
75     release(&handle->h_spinlock);
76     return EDEADLK;
77   }
78   /* If detached or already joined, error */
79   if (th->p_detached || th->p_joining != NULL) {
80     release(&handle->h_spinlock);
81     return EINVAL;
82   }
83   /* If not terminated yet, suspend ourselves. */
84   if (! th->p_terminated) {
85     th->p_joining = self;
86     release(&handle->h_spinlock);
87     suspend_with_cancellation(self);
88     /* This is a cancellation point */
89     if (self->p_canceled && self->p_cancelstate == PTHREAD_CANCEL_ENABLE) {
90       th->p_joining = NULL;
91       pthread_exit(PTHREAD_CANCELED);
92     }
93     acquire(&handle->h_spinlock);
94   }
95   /* Get return value */
96   if (thread_return != NULL) *thread_return = th->p_retval;
97   release(&handle->h_spinlock);
98   /* Send notification to thread manager */
99   if (__pthread_manager_request >= 0) {
100     request.req_thread = self;
101     request.req_kind = REQ_FREE;
102     request.req_args.free.thread = th;
103     __libc_write(__pthread_manager_request,
104                  (char *) &request, sizeof(request));
105   }
106   return 0;
107 }
108
109 int pthread_detach(pthread_t thread_id)
110 {
111   int terminated;
112   struct pthread_request request;
113   pthread_handle handle = thread_handle(thread_id);
114   pthread_descr th;
115
116   acquire(&handle->h_spinlock);
117   if (invalid_handle(handle, thread_id)) {
118     release(&handle->h_spinlock);
119     return ESRCH;
120   }
121   th = handle->h_descr;
122   /* If already detached, error */
123   if (th->p_detached) {
124     release(&handle->h_spinlock);
125     return EINVAL;
126   }
127   /* If already joining, don't do anything. */
128   if (th->p_joining != NULL) {
129     release(&handle->h_spinlock);
130     return 0;
131   }
132   /* Mark as detached */
133   th->p_detached = 1;
134   terminated = th->p_terminated;
135   release(&handle->h_spinlock);
136   /* If already terminated, notify thread manager to reclaim resources */
137   if (terminated && __pthread_manager_request >= 0) {
138     request.req_thread = thread_self();
139     request.req_kind = REQ_FREE;
140     request.req_args.free.thread = th;
141     __libc_write(__pthread_manager_request,
142                  (char *) &request, sizeof(request));
143   }
144   return 0;
145 }