Revert until we track down the RH9 bug
[mono.git] / mono / io-layer / timed-thread.c
1 /*
2  * timed-thread.c:  Implementation of timed thread joining
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <mono/os/gc_wrapper.h>
13 #include <pthread.h>
14 #ifdef HAVE_SEMAPHORE_H
15 #include <semaphore.h>
16 #endif
17
18 #include <mono/io-layer/processes.h>
19
20 #include "timed-thread.h"
21
22 #include "mono-mutex.h"
23
24 #undef DEBUG
25
26 /*
27  * Implementation of timed thread joining from the P1003.1d/D14 (July 1999)
28  * draft spec, figure B-6.
29  */
30
31 static pthread_key_t timed_thread_key;
32 static mono_once_t timed_thread_once = MONO_ONCE_INIT;
33
34 static void timed_thread_init(void)
35 {
36         pthread_key_create(&timed_thread_key, NULL);
37 }
38
39 void _wapi_timed_thread_exit(guint32 exitstatus)
40 {
41         TimedThread *thread;
42         void *specific;
43         
44         if((specific = pthread_getspecific(timed_thread_key)) == NULL) {
45                 /* Handle cases which won't happen with correct usage.
46                  */
47                 pthread_exit(NULL);
48         }
49         
50         thread=(TimedThread *)specific;
51         
52         mono_mutex_lock(&thread->join_mutex);
53         
54         /* Tell a joiner that we're exiting.
55          */
56 #ifdef DEBUG
57         g_message(G_GNUC_PRETTY_FUNCTION
58                   ": Setting thread %p id %ld exit status to %d",
59                   thread, thread->id, exitstatus);
60 #endif
61
62         thread->exitstatus=exitstatus;
63         thread->exiting=TRUE;
64
65         if(thread->exit_routine!=NULL) {
66                 thread->exit_routine(exitstatus, thread->exit_userdata);
67         }
68         
69         pthread_cond_signal(&thread->exit_cond);
70         mono_mutex_unlock(&thread->join_mutex);
71         
72         /* Call pthread_exit() to call destructors and really exit the
73          * thread.
74          */
75         pthread_exit(NULL);
76 }
77
78 /* Routine to establish thread specific data value and run the actual
79  * thread start routine which was supplied to timed_thread_create()
80  */
81 static void *timed_thread_start_routine(gpointer args) G_GNUC_NORETURN;
82 static void *timed_thread_start_routine(gpointer args)
83 {
84         TimedThread *thread = (TimedThread *)args;
85         
86         mono_once(&timed_thread_once, timed_thread_init);
87         pthread_setspecific(timed_thread_key, (void *)thread);
88         pthread_detach(thread->id);
89
90         if(thread->create_flags & CREATE_SUSPENDED) {
91                 thread->suspend_count = 1;
92                 _wapi_timed_thread_suspend (thread);
93         }
94         
95         _wapi_timed_thread_exit(thread->start_routine(thread->arg));
96 }
97
98 /* Allocate a thread which can be used with timed_thread_join().
99  */
100 int _wapi_timed_thread_create(TimedThread **threadp,
101                               const pthread_attr_t *attr,
102                               guint32 create_flags,
103                               guint32 (*start_routine)(gpointer),
104                               void (*exit_routine)(guint32, gpointer),
105                               gpointer arg, gpointer exit_userdata)
106 {
107         TimedThread *thread;
108         int result;
109         
110         thread=(TimedThread *)g_new0(TimedThread, 1);
111         
112         mono_mutex_init(&thread->join_mutex, NULL);
113         pthread_cond_init(&thread->exit_cond, NULL);
114         thread->create_flags = create_flags;
115         sem_init (&thread->suspend_sem, 0, 0);
116         sem_init (&thread->suspended_sem, 0, 0);
117         thread->start_routine = start_routine;
118         thread->exit_routine = exit_routine;
119         thread->arg = arg;
120         thread->exit_userdata = exit_userdata;
121         thread->exitstatus = 0;
122         thread->exiting = FALSE;
123         
124         *threadp = thread;
125
126         if((result = pthread_create(&thread->id, attr,
127                                     timed_thread_start_routine,
128                                     (void *)thread)) != 0) {
129                 g_free(thread);
130                 return(result);
131         }
132         
133         return(0);
134 }
135
136 int _wapi_timed_thread_attach(TimedThread **threadp,
137                               void (*exit_routine)(guint32, gpointer),
138                               gpointer exit_userdata)
139 {
140         TimedThread *thread;
141
142         thread=(TimedThread *)g_new0(TimedThread, 1);
143
144         mono_mutex_init(&thread->join_mutex, NULL);
145         pthread_cond_init(&thread->exit_cond, NULL);
146         sem_init (&thread->suspend_sem, 0, 0);
147         sem_init (&thread->suspended_sem, 0, 0);
148         thread->exit_routine = exit_routine;
149         thread->exit_userdata = exit_userdata;
150         thread->exitstatus = 0;
151         thread->exiting = FALSE;
152         thread->id = pthread_self();
153
154         /* Make sure the timed-thread initialisation that the start
155          * routing does happens here too (we might be first to be
156          * called)
157          */
158         mono_once(&timed_thread_once, timed_thread_init);
159         pthread_setspecific(timed_thread_key, (void *)thread);
160
161         *threadp = thread;
162
163         return(0);
164 }
165
166 int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout,
167                             guint32 *exitstatus)
168 {
169         int result;
170         
171         mono_mutex_lock(&thread->join_mutex);
172         result=0;
173         
174         /* Wait until the thread announces that it's exiting, or until
175          * timeout.
176          */
177         while(result == 0 && !thread->exiting) {
178                 if(timeout == NULL) {
179                         result = pthread_cond_wait(&thread->exit_cond,
180                                                    &thread->join_mutex);
181                 } else {
182                         result = pthread_cond_timedwait(&thread->exit_cond,
183                                                         &thread->join_mutex,
184                                                         timeout);
185                 }
186         }
187         
188         mono_mutex_unlock(&thread->join_mutex);
189         if(result == 0 && thread->exiting) {
190                 if(exitstatus!=NULL) {
191                         *exitstatus = thread->exitstatus;
192                 }
193         }
194         return(result);
195 }
196
197 void _wapi_timed_thread_destroy (TimedThread *thread)
198 {
199         mono_mutex_destroy (&thread->join_mutex);
200         pthread_cond_destroy (&thread->exit_cond);
201         sem_destroy (&thread->suspend_sem);
202         sem_destroy (&thread->suspended_sem);
203         
204         g_free(thread);
205 }
206
207 /* I was going to base thread suspending on the algorithm presented at
208  * http://home.earthlink.net/~anneart/family/Threads/code/susp.c
209  *
210  * Unfortunately the Boehm GC library also wants to use this technique
211  * to stop the world, and will deadlock if a thread has already been
212  * suspended when it tries.
213  *
214  * While Mono is still using libgc this will just have to be a kludge
215  * to implement suspended creation of threads, rather than the general
216  * purpose thread suspension.
217  */
218 void _wapi_timed_thread_suspend (TimedThread *thread)
219 {
220         TimedThread *self;
221         void *specific;
222         
223         if((specific = pthread_getspecific (timed_thread_key))==NULL) {
224                 g_warning (G_GNUC_PRETTY_FUNCTION ": thread lookup failed");
225                 return;
226         }
227         self=(TimedThread *)specific;
228         
229         if(thread != self) {
230                 g_error (G_GNUC_PRETTY_FUNCTION
231                          ": attempt to suspend a different thread!");
232                 exit (-1);
233         }
234
235         sem_wait (&thread->suspend_sem);
236 }
237
238 void _wapi_timed_thread_resume (TimedThread *thread)
239 {
240         sem_post (&thread->suspend_sem);
241 }