Remove K&R compatibility.
[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   THREAD_SETMEM(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   __pthread_lock(THREAD_GETMEM(self, p_lock), self);
39   THREAD_SETMEM(self, p_retval, retval);
40   /* Say that we've terminated */
41   THREAD_SETMEM(self, p_terminated, 1);
42   /* See if someone is joining on us */
43   joining = THREAD_GETMEM(self, p_joining);
44   __pthread_unlock(THREAD_GETMEM(self, p_lock));
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   __pthread_lock(&handle->h_lock, self);
69   if (invalid_handle(handle, thread_id)) {
70     __pthread_unlock(&handle->h_lock);
71     return ESRCH;
72   }
73   th = handle->h_descr;
74   if (th == self) {
75     __pthread_unlock(&handle->h_lock);
76     return EDEADLK;
77   }
78   /* If detached or already joined, error */
79   if (th->p_detached || th->p_joining != NULL) {
80     __pthread_unlock(&handle->h_lock);
81     return EINVAL;
82   }
83   /* If not terminated yet, suspend ourselves. */
84   if (! th->p_terminated) {
85     th->p_joining = self;
86     __pthread_unlock(&handle->h_lock);
87     suspend_with_cancellation(self);
88     /* This is a cancellation point */
89     if (THREAD_GETMEM(self, p_canceled)
90         && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
91       th->p_joining = NULL;
92       pthread_exit(PTHREAD_CANCELED);
93     }
94     __pthread_lock(&handle->h_lock, self);
95   }
96   /* Get return value */
97   if (thread_return != NULL) *thread_return = th->p_retval;
98   __pthread_unlock(&handle->h_lock);
99   /* Send notification to thread manager */
100   if (__pthread_manager_request >= 0) {
101     request.req_thread = self;
102     request.req_kind = REQ_FREE;
103     request.req_args.free.thread_id = thread_id;
104     __libc_write(__pthread_manager_request,
105                  (char *) &request, sizeof(request));
106   }
107   return 0;
108 }
109
110 int pthread_detach(pthread_t thread_id)
111 {
112   int terminated;
113   struct pthread_request request;
114   pthread_handle handle = thread_handle(thread_id);
115   pthread_descr th;
116
117   __pthread_lock(&handle->h_lock, NULL);
118   if (invalid_handle(handle, thread_id)) {
119     __pthread_unlock(&handle->h_lock);
120     return ESRCH;
121   }
122   th = handle->h_descr;
123   /* If already detached, error */
124   if (th->p_detached) {
125     __pthread_unlock(&handle->h_lock);
126     return EINVAL;
127   }
128   /* If already joining, don't do anything. */
129   if (th->p_joining != NULL) {
130     __pthread_unlock(&handle->h_lock);
131     return 0;
132   }
133   /* Mark as detached */
134   th->p_detached = 1;
135   terminated = th->p_terminated;
136   __pthread_unlock(&handle->h_lock);
137   /* If already terminated, notify thread manager to reclaim resources */
138   if (terminated && __pthread_manager_request >= 0) {
139     request.req_thread = thread_self();
140     request.req_kind = REQ_FREE;
141     request.req_args.free.thread_id = thread_id;
142     __libc_write(__pthread_manager_request,
143                  (char *) &request, sizeof(request));
144   }
145   return 0;
146 }