2 * timed-thread.c: Implementation of timed thread joining
5 * Dick Porter (dick@ximian.com)
7 * (C) 2002 Ximian, Inc.
12 #include <mono/os/gc_wrapper.h>
14 #ifdef HAVE_SEMAPHORE_H
15 #include <semaphore.h>
18 #include <mono/io-layer/processes.h>
20 #include "timed-thread.h"
22 #include "mono-mutex.h"
27 * Implementation of timed thread joining from the P1003.1d/D14 (July 1999)
28 * draft spec, figure B-6.
31 static pthread_key_t timed_thread_key;
32 static mono_once_t timed_thread_once = MONO_ONCE_INIT;
34 static void timed_thread_init(void)
36 pthread_key_create(&timed_thread_key, NULL);
39 void _wapi_timed_thread_exit(guint32 exitstatus)
44 if((specific = pthread_getspecific(timed_thread_key)) == NULL) {
45 /* Handle cases which won't happen with correct usage.
50 thread=(TimedThread *)specific;
52 mono_mutex_lock(&thread->join_mutex);
54 /* Tell a joiner that we're exiting.
57 g_message(G_GNUC_PRETTY_FUNCTION
58 ": Setting thread %p id %ld exit status to %d",
59 thread, thread->id, exitstatus);
62 thread->exitstatus=exitstatus;
65 if(thread->exit_routine!=NULL) {
66 thread->exit_routine(exitstatus, thread->exit_userdata);
69 pthread_cond_signal(&thread->exit_cond);
70 mono_mutex_unlock(&thread->join_mutex);
72 _wapi_timed_thread_destroy (thread);
74 /* Call pthread_exit() to call destructors and really exit the
80 /* Routine to establish thread specific data value and run the actual
81 * thread start routine which was supplied to timed_thread_create()
83 static void *timed_thread_start_routine(gpointer args) G_GNUC_NORETURN;
84 static void *timed_thread_start_routine(gpointer args)
86 TimedThread *thread = (TimedThread *)args;
88 mono_once(&timed_thread_once, timed_thread_init);
89 pthread_setspecific(timed_thread_key, (void *)thread);
90 pthread_detach(thread->id);
92 if(thread->create_flags & CREATE_SUSPENDED) {
93 thread->suspend_count = 1;
94 _wapi_timed_thread_suspend (thread);
97 _wapi_timed_thread_exit(thread->start_routine(thread->arg));
100 /* Allocate a thread which can be used with timed_thread_join().
102 int _wapi_timed_thread_create(TimedThread **threadp,
103 const pthread_attr_t *attr,
104 guint32 create_flags,
105 guint32 (*start_routine)(gpointer),
106 void (*exit_routine)(guint32, gpointer),
107 gpointer arg, gpointer exit_userdata)
112 thread=(TimedThread *)g_new0(TimedThread, 1);
114 mono_mutex_init(&thread->join_mutex, NULL);
115 pthread_cond_init(&thread->exit_cond, NULL);
116 thread->create_flags = create_flags;
117 sem_init (&thread->suspend_sem, 0, 0);
118 sem_init (&thread->suspended_sem, 0, 0);
119 thread->start_routine = start_routine;
120 thread->exit_routine = exit_routine;
122 thread->exit_userdata = exit_userdata;
123 thread->exitstatus = 0;
124 thread->exiting = FALSE;
128 if((result = pthread_create(&thread->id, attr,
129 timed_thread_start_routine,
130 (void *)thread)) != 0) {
138 int _wapi_timed_thread_attach(TimedThread **threadp,
139 void (*exit_routine)(guint32, gpointer),
140 gpointer exit_userdata)
144 thread=(TimedThread *)g_new0(TimedThread, 1);
146 mono_mutex_init(&thread->join_mutex, NULL);
147 pthread_cond_init(&thread->exit_cond, NULL);
148 sem_init (&thread->suspend_sem, 0, 0);
149 sem_init (&thread->suspended_sem, 0, 0);
150 thread->exit_routine = exit_routine;
151 thread->exit_userdata = exit_userdata;
152 thread->exitstatus = 0;
153 thread->exiting = FALSE;
154 thread->id = pthread_self();
156 /* Make sure the timed-thread initialisation that the start
157 * routing does happens here too (we might be first to be
160 mono_once(&timed_thread_once, timed_thread_init);
161 pthread_setspecific(timed_thread_key, (void *)thread);
168 int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout,
173 mono_mutex_lock(&thread->join_mutex);
176 /* Wait until the thread announces that it's exiting, or until
179 while(result == 0 && !thread->exiting) {
180 if(timeout == NULL) {
181 result = pthread_cond_wait(&thread->exit_cond,
182 &thread->join_mutex);
184 result = pthread_cond_timedwait(&thread->exit_cond,
190 mono_mutex_unlock(&thread->join_mutex);
191 if(result == 0 && thread->exiting) {
192 if(exitstatus!=NULL) {
193 *exitstatus = thread->exitstatus;
199 void _wapi_timed_thread_destroy (TimedThread *thread)
201 mono_mutex_destroy (&thread->join_mutex);
202 pthread_cond_destroy (&thread->exit_cond);
203 sem_destroy (&thread->suspend_sem);
204 sem_destroy (&thread->suspended_sem);
209 /* I was going to base thread suspending on the algorithm presented at
210 * http://home.earthlink.net/~anneart/family/Threads/code/susp.c
212 * Unfortunately the Boehm GC library also wants to use this technique
213 * to stop the world, and will deadlock if a thread has already been
214 * suspended when it tries.
216 * While Mono is still using libgc this will just have to be a kludge
217 * to implement suspended creation of threads, rather than the general
218 * purpose thread suspension.
220 void _wapi_timed_thread_suspend (TimedThread *thread)
225 if((specific = pthread_getspecific (timed_thread_key))==NULL) {
226 g_warning (G_GNUC_PRETTY_FUNCTION ": thread lookup failed");
229 self=(TimedThread *)specific;
232 g_error (G_GNUC_PRETTY_FUNCTION
233 ": attempt to suspend a different thread!");
237 sem_wait (&thread->suspend_sem);
240 void _wapi_timed_thread_resume (TimedThread *thread)
242 sem_post (&thread->suspend_sem);