Implement vararg support for s390. Minor fix to atomic operation for s390.
[mono.git] / mono / io-layer / timed-thread.c
blob6599e0660f307150c3d0359bd34fce0f7687a4b6
1 /*
2 * timed-thread.c: Implementation of timed thread joining
4 * Author:
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
8 */
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 #include <errno.h>
19 #include <mono/io-layer/processes.h>
21 #include "timed-thread.h"
23 #include "mono-mutex.h"
25 #undef DEBUG
28 * Implementation of timed thread joining from the P1003.1d/D14 (July 1999)
29 * draft spec, figure B-6.
32 static pthread_key_t timed_thread_key;
33 static mono_once_t timed_thread_once = MONO_ONCE_INIT;
34 static mono_mutex_t apc_mutex;
37 static void timed_thread_init(void)
39 int thr_ret;
41 thr_ret = pthread_key_create(&timed_thread_key, NULL);
42 g_assert (thr_ret == 0);
44 thr_ret = mono_mutex_init(&apc_mutex, NULL);
45 g_assert (thr_ret == 0);
48 void _wapi_timed_thread_exit(guint32 exitstatus)
50 TimedThread *thread;
51 void *specific;
52 int thr_ret;
54 if((specific = pthread_getspecific(timed_thread_key)) == NULL) {
55 /* Handle cases which won't happen with correct usage.
57 pthread_exit(NULL);
60 thread=(TimedThread *)specific;
62 if(thread->exit_routine!=NULL) {
63 thread->exit_routine(exitstatus, thread->exit_userdata);
66 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
67 (void *)&thread->join_mutex);
68 thr_ret = mono_mutex_lock(&thread->join_mutex);
69 g_assert (thr_ret == 0);
71 /* Tell a joiner that we're exiting.
73 #ifdef DEBUG
74 g_message(G_GNUC_PRETTY_FUNCTION
75 ": Setting thread %p id %ld exit status to %d",
76 thread, thread->id, exitstatus);
77 #endif
79 thread->exitstatus=exitstatus;
80 thread->exiting=TRUE;
82 thr_ret = pthread_cond_signal(&thread->exit_cond);
83 g_assert (thr_ret == 0);
85 thr_ret = mono_mutex_unlock(&thread->join_mutex);
86 g_assert (thr_ret == 0);
87 pthread_cleanup_pop (0);
89 /* Call pthread_exit() to call destructors and really exit the
90 * thread.
92 pthread_exit(NULL);
95 /* Routine to establish thread specific data value and run the actual
96 * thread start routine which was supplied to timed_thread_create()
98 static void *timed_thread_start_routine(gpointer args) G_GNUC_NORETURN;
99 static void *timed_thread_start_routine(gpointer args)
101 TimedThread *thread = (TimedThread *)args;
102 int thr_ret;
104 mono_once(&timed_thread_once, timed_thread_init);
105 thr_ret = pthread_setspecific(timed_thread_key, (void *)thread);
106 g_assert (thr_ret == 0);
108 /* This used to be pthread_detach(thread->id);
110 * thread->id is set in _wapi_timed_thread_create:
112 * if((result = pthread_create(&thread->id, attr,
113 * timed_thread_start_routine,
114 * (void *)thread)) != 0) {
116 * Strangeness happened: if _wapi_timed_thread_create was
117 * called directly, then thread->id was always set here.
118 * However, if _wapi_timed_thread_create was called via
119 * another function that did nothing but call
120 * _wapi_timed_thread_create, thread->id was not ever set,
121 * leading to the thread's 2M stack being wasted as it was not
122 * detached.
124 * This was 100% reproducible on Debian Woody with gcc 2.95.4,
125 * and on Red Hat 9 with gcc 3.2.2.
127 thr_ret = pthread_detach(pthread_self ());
128 g_assert (thr_ret == 0);
130 if(thread->create_flags & CREATE_SUSPENDED) {
131 thread->suspend_count = 1;
132 _wapi_timed_thread_suspend (thread);
135 _wapi_timed_thread_exit(thread->start_routine(thread->arg));
138 /* Allocate a thread which can be used with timed_thread_join().
140 int _wapi_timed_thread_create(TimedThread **threadp,
141 const pthread_attr_t *attr,
142 guint32 create_flags,
143 guint32 (*start_routine)(gpointer),
144 void (*exit_routine)(guint32, gpointer),
145 gpointer arg, gpointer exit_userdata)
147 TimedThread *thread;
148 int result;
149 int thr_ret;
151 thread=(TimedThread *)g_new0(TimedThread, 1);
153 thr_ret = mono_mutex_init(&thread->join_mutex, NULL);
154 g_assert (thr_ret == 0);
156 thr_ret = pthread_cond_init(&thread->exit_cond, NULL);
157 g_assert (thr_ret == 0);
159 thread->create_flags = create_flags;
160 MONO_SEM_INIT (&thread->suspend_sem, 0);
161 MONO_SEM_INIT (&thread->suspended_sem, 0);
162 thread->start_routine = start_routine;
163 thread->exit_routine = exit_routine;
164 thread->arg = arg;
165 thread->exit_userdata = exit_userdata;
166 thread->exitstatus = 0;
167 thread->exiting = FALSE;
168 thread->apc_queue = NULL;
170 *threadp = thread;
172 if((result = pthread_create(&thread->id, attr,
173 timed_thread_start_routine,
174 (void *)thread)) != 0) {
175 g_free(thread);
176 return(result);
179 return(0);
182 int _wapi_timed_thread_attach(TimedThread **threadp,
183 void (*exit_routine)(guint32, gpointer),
184 gpointer exit_userdata)
186 TimedThread *thread;
187 int thr_ret;
189 thread=(TimedThread *)g_new0(TimedThread, 1);
191 thr_ret = mono_mutex_init(&thread->join_mutex, NULL);
192 g_assert (thr_ret == 0);
194 thr_ret = pthread_cond_init(&thread->exit_cond, NULL);
195 g_assert (thr_ret == 0);
197 thr_ret = MONO_SEM_INIT (&thread->suspend_sem, 0);
198 g_assert (thr_ret != -1);
200 thr_ret = MONO_SEM_INIT (&thread->suspended_sem, 0);
201 g_assert (thr_ret != -1);
203 thread->exit_routine = exit_routine;
204 thread->exit_userdata = exit_userdata;
205 thread->exitstatus = 0;
206 thread->exiting = FALSE;
207 thread->id = pthread_self();
209 /* Make sure the timed-thread initialisation that the start
210 * routing does happens here too (we might be first to be
211 * called)
213 mono_once(&timed_thread_once, timed_thread_init);
214 thr_ret = pthread_setspecific(timed_thread_key, (void *)thread);
215 g_assert (thr_ret == 0);
217 *threadp = thread;
219 return(0);
222 int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout,
223 guint32 *exitstatus)
225 int result;
226 int thr_ret;
228 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
229 (void *)&thread->join_mutex);
230 thr_ret = mono_mutex_lock(&thread->join_mutex);
231 g_assert (thr_ret == 0);
233 result=0;
235 /* Wait until the thread announces that it's exiting, or until
236 * timeout.
238 while(result == 0 && !thread->exiting) {
239 if(timeout == NULL) {
240 result = pthread_cond_wait(&thread->exit_cond,
241 &thread->join_mutex);
242 } else {
243 result = pthread_cond_timedwait(&thread->exit_cond,
244 &thread->join_mutex,
245 timeout);
249 thr_ret = mono_mutex_unlock(&thread->join_mutex);
250 g_assert (thr_ret == 0);
251 pthread_cleanup_pop (0);
253 if(result == 0 && thread->exiting) {
254 if(exitstatus!=NULL) {
255 *exitstatus = thread->exitstatus;
258 _wapi_timed_thread_destroy (thread);
260 return(result);
263 void _wapi_timed_thread_destroy (TimedThread *thread)
265 mono_mutex_destroy (&thread->join_mutex);
266 pthread_cond_destroy (&thread->exit_cond);
267 MONO_SEM_DESTROY (&thread->suspend_sem);
268 MONO_SEM_DESTROY (&thread->suspended_sem);
270 g_free(thread);
273 /* I was going to base thread suspending on the algorithm presented at
274 * http://home.earthlink.net/~anneart/family/Threads/code/susp.c
276 * Unfortunately the Boehm GC library also wants to use this technique
277 * to stop the world, and will deadlock if a thread has already been
278 * suspended when it tries.
280 * While Mono is still using libgc this will just have to be a kludge
281 * to implement suspended creation of threads, rather than the general
282 * purpose thread suspension.
284 void _wapi_timed_thread_suspend (TimedThread *thread)
286 TimedThread *self;
287 void *specific;
289 if((specific = pthread_getspecific (timed_thread_key))==NULL) {
290 g_warning (G_GNUC_PRETTY_FUNCTION ": thread lookup failed");
291 return;
293 self=(TimedThread *)specific;
295 if(thread != self) {
296 g_error (G_GNUC_PRETTY_FUNCTION
297 ": attempt to suspend a different thread!");
298 exit (-1);
301 while (MONO_SEM_WAIT (&thread->suspend_sem) != 0 && errno == EINTR);
304 void _wapi_timed_thread_resume (TimedThread *thread)
306 MONO_SEM_POST (&thread->suspend_sem);
309 void _wapi_timed_thread_queue_apc (TimedThread *thread,
310 guint32 (*apc_callback)(gpointer), gpointer param)
312 ApcInfo *apc;
313 int thr_ret;
315 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
316 (void *)&apc_mutex);
317 thr_ret = mono_mutex_lock(&apc_mutex);
318 g_assert (thr_ret == 0);
320 apc = (ApcInfo *)g_new(ApcInfo, 1);
321 apc->callback = apc_callback;
322 apc->param = param;
323 thread->apc_queue = g_slist_append (thread->apc_queue, apc);
325 thr_ret = mono_mutex_unlock(&apc_mutex);
326 g_assert (thr_ret == 0);
327 pthread_cleanup_pop (0);
330 gboolean _wapi_timed_thread_apc_pending (TimedThread *thread)
332 return thread->apc_queue != NULL;
335 void _wapi_timed_thread_dispatch_apc_queue (TimedThread *thread)
337 ApcInfo* apc;
338 GSList *list;
339 int thr_ret;
341 pthread_cleanup_push ((void(*)(void *))mono_mutex_unlock_in_cleanup,
342 (void *)&apc_mutex);
343 thr_ret = mono_mutex_lock(&apc_mutex);
344 g_assert (thr_ret == 0);
346 list = thread->apc_queue;
347 thread->apc_queue = NULL;
349 thr_ret = mono_mutex_unlock(&apc_mutex);
350 g_assert (thr_ret == 0);
351 pthread_cleanup_pop (0);
353 while (list != NULL) {
354 apc = (ApcInfo*)list->data;
355 apc->callback (apc->param);
356 g_free (apc);
357 list = g_slist_next (list);
359 g_slist_free (list);