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 _wapi_timed_thread_suspend (thread);
94 _wapi_timed_thread_exit(thread->start_routine(thread->arg));
97 /* Allocate a thread which can be used with timed_thread_join().
99 int _wapi_timed_thread_create(TimedThread **threadp,
100 const pthread_attr_t *attr,
101 guint32 create_flags,
102 guint32 (*start_routine)(gpointer),
103 void (*exit_routine)(guint32, gpointer),
104 gpointer arg, gpointer exit_userdata)
109 thread=(TimedThread *)g_new0(TimedThread, 1);
111 mono_mutex_init(&thread->join_mutex, NULL);
112 pthread_cond_init(&thread->exit_cond, NULL);
113 thread->create_flags = create_flags;
114 sem_init (&thread->suspend_sem, 0, 0);
115 thread->start_routine = start_routine;
116 thread->exit_routine = exit_routine;
118 thread->exit_userdata = exit_userdata;
119 thread->exitstatus = 0;
120 thread->exiting = FALSE;
124 if((result = pthread_create(&thread->id, attr,
125 timed_thread_start_routine,
126 (void *)thread)) != 0) {
134 int _wapi_timed_thread_attach(TimedThread **threadp,
135 void (*exit_routine)(guint32, gpointer),
136 gpointer exit_userdata)
140 thread=(TimedThread *)g_new0(TimedThread, 1);
142 mono_mutex_init(&thread->join_mutex, NULL);
143 pthread_cond_init(&thread->exit_cond, NULL);
144 sem_init (&thread->suspend_sem, 0, 0);
145 thread->exit_routine = exit_routine;
146 thread->exit_userdata = exit_userdata;
147 thread->exitstatus = 0;
148 thread->exiting = FALSE;
149 thread->id = pthread_self();
151 /* Make sure the timed-thread initialisation that the start
152 * routing does happens here too (we might be first to be
155 mono_once(&timed_thread_once, timed_thread_init);
156 pthread_setspecific(timed_thread_key, (void *)thread);
163 int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout,
168 mono_mutex_lock(&thread->join_mutex);
171 /* Wait until the thread announces that it's exiting, or until
174 while(result == 0 && !thread->exiting) {
175 if(timeout == NULL) {
176 result = pthread_cond_wait(&thread->exit_cond,
177 &thread->join_mutex);
179 result = pthread_cond_timedwait(&thread->exit_cond,
185 mono_mutex_unlock(&thread->join_mutex);
186 if(result == 0 && thread->exiting) {
187 if(exitstatus!=NULL) {
188 *exitstatus = thread->exitstatus;
194 void _wapi_timed_thread_destroy (TimedThread *thread)
196 mono_mutex_destroy (&thread->join_mutex);
197 pthread_cond_destroy (&thread->exit_cond);
198 sem_destroy (&thread->suspend_sem);
203 /* I was going to base thread suspending on the algorithm presented at
204 * http://home.earthlink.net/~anneart/family/Threads/code/susp.c
206 * Unfortunately the Boehm GC library also wants to use this technique
207 * to stop the world, and will deadlock if a thread has already been
208 * suspended when it tries.
210 * While Mono is still using libgc this will just have to be a kludge
211 * to implement suspended creation of threads, rather than the general
212 * purpose thread suspension.
214 void _wapi_timed_thread_suspend (TimedThread *thread)
219 if((specific = pthread_getspecific (timed_thread_key))==NULL) {
220 g_warning (G_GNUC_PRETTY_FUNCTION ": thread lookup failed");
223 self=(TimedThread *)specific;
226 g_error (G_GNUC_PRETTY_FUNCTION
227 ": attempt to suspend a different thread!");
231 sem_wait (&thread->suspend_sem);
234 void _wapi_timed_thread_resume (TimedThread *thread)
236 sem_post (&thread->suspend_sem);