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 /* Call pthread_exit() to call destructors and really exit the
78 /* Routine to establish thread specific data value and run the actual
79 * thread start routine which was supplied to timed_thread_create()
81 static void *timed_thread_start_routine(gpointer args) G_GNUC_NORETURN;
82 static void *timed_thread_start_routine(gpointer args)
84 TimedThread *thread = (TimedThread *)args;
86 mono_once(&timed_thread_once, timed_thread_init);
87 pthread_setspecific(timed_thread_key, (void *)thread);
88 pthread_detach(thread->id);
90 if(thread->create_flags & CREATE_SUSPENDED) {
91 thread->suspend_count = 1;
92 _wapi_timed_thread_suspend (thread);
95 _wapi_timed_thread_exit(thread->start_routine(thread->arg));
98 /* Allocate a thread which can be used with timed_thread_join().
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)
110 thread=(TimedThread *)g_new0(TimedThread, 1);
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;
120 thread->exit_userdata = exit_userdata;
121 thread->exitstatus = 0;
122 thread->exiting = FALSE;
126 if((result = pthread_create(&thread->id, attr,
127 timed_thread_start_routine,
128 (void *)thread)) != 0) {
136 int _wapi_timed_thread_attach(TimedThread **threadp,
137 void (*exit_routine)(guint32, gpointer),
138 gpointer exit_userdata)
142 thread=(TimedThread *)g_new0(TimedThread, 1);
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();
154 /* Make sure the timed-thread initialisation that the start
155 * routing does happens here too (we might be first to be
158 mono_once(&timed_thread_once, timed_thread_init);
159 pthread_setspecific(timed_thread_key, (void *)thread);
166 int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout,
171 mono_mutex_lock(&thread->join_mutex);
174 /* Wait until the thread announces that it's exiting, or until
177 while(result == 0 && !thread->exiting) {
178 if(timeout == NULL) {
179 result = pthread_cond_wait(&thread->exit_cond,
180 &thread->join_mutex);
182 result = pthread_cond_timedwait(&thread->exit_cond,
188 mono_mutex_unlock(&thread->join_mutex);
189 if(result == 0 && thread->exiting) {
190 if(exitstatus!=NULL) {
191 *exitstatus = thread->exitstatus;
197 void _wapi_timed_thread_destroy (TimedThread *thread)
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);
207 /* I was going to base thread suspending on the algorithm presented at
208 * http://home.earthlink.net/~anneart/family/Threads/code/susp.c
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.
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.
218 void _wapi_timed_thread_suspend (TimedThread *thread)
223 if((specific = pthread_getspecific (timed_thread_key))==NULL) {
224 g_warning (G_GNUC_PRETTY_FUNCTION ": thread lookup failed");
227 self=(TimedThread *)specific;
230 g_error (G_GNUC_PRETTY_FUNCTION
231 ": attempt to suspend a different thread!");
235 sem_wait (&thread->suspend_sem);
238 void _wapi_timed_thread_resume (TimedThread *thread)
240 sem_post (&thread->suspend_sem);