2002-04-30 Dick Porter <dick@ximian.com>
[mono.git] / mono / io-layer / timed-thread.c
1 #include <config.h>
2 #include <glib.h>
3 #if HAVE_BOEHM_GC
4 #include <gc/gc.h>
5 #endif
6 #include <pthread.h>
7
8 #include "timed-thread.h"
9
10 #include "mono-mutex.h"
11
12 #undef DEBUG
13
14 /*
15  * Implementation of timed thread joining from the P1003.1d/D14 (July 1999)
16  * draft spec, figure B-6.
17  */
18
19 static pthread_key_t timed_thread_key;
20 static pthread_once_t timed_thread_once = PTHREAD_ONCE_INIT;
21
22 static void timed_thread_init(void)
23 {
24         pthread_key_create(&timed_thread_key, NULL);
25 }
26
27 void _wapi_timed_thread_exit(guint32 exitstatus)
28 {
29         TimedThread *thread;
30         void *specific;
31         
32         if((specific = pthread_getspecific(timed_thread_key)) == NULL) {
33                 /* Handle cases which won't happen with correct usage.
34                  */
35                 pthread_exit(NULL);
36         }
37         
38         thread=(TimedThread *)specific;
39         
40         mono_mutex_lock(&thread->join_mutex);
41         
42         /* Tell a joiner that we're exiting.
43          */
44 #ifdef DEBUG
45         g_message(G_GNUC_PRETTY_FUNCTION
46                   ": Setting thread %p id %ld exit status to %d",
47                   thread, thread->id, exitstatus);
48 #endif
49
50         thread->exitstatus=exitstatus;
51         thread->exiting=TRUE;
52
53         if(thread->exit_routine!=NULL) {
54                 thread->exit_routine(exitstatus, thread->exit_userdata);
55         }
56         
57         pthread_cond_signal(&thread->exit_cond);
58         mono_mutex_unlock(&thread->join_mutex);
59         
60         /* Call pthread_exit() to call destructors and really exit the
61          * thread.
62          */
63         pthread_exit(NULL);
64 }
65
66 /* Routine to establish thread specific data value and run the actual
67  * thread start routine which was supplied to timed_thread_create()
68  */
69 static void *timed_thread_start_routine(gpointer args) G_GNUC_NORETURN;
70 static void *timed_thread_start_routine(gpointer args)
71 {
72         TimedThread *thread = (TimedThread *)args;
73         
74         pthread_once(&timed_thread_once, timed_thread_init);
75         pthread_setspecific(timed_thread_key, (void *)thread);
76         pthread_detach(thread->id);
77         _wapi_timed_thread_exit(thread->start_routine(thread->arg));
78 }
79
80 /* Allocate a thread which can be used with timed_thread_join().
81  */
82 int _wapi_timed_thread_create(TimedThread **threadp,
83                               const pthread_attr_t *attr,
84                               guint32 (*start_routine)(gpointer),
85                               void (*exit_routine)(guint32, gpointer),
86                               gpointer arg, gpointer exit_userdata)
87 {
88         TimedThread *thread;
89         int result;
90         
91         thread=(TimedThread *)g_new0(TimedThread, 1);
92         
93         mono_mutex_init(&thread->join_mutex, NULL);
94         pthread_cond_init(&thread->exit_cond, NULL);
95         thread->start_routine = start_routine;
96         thread->exit_routine = exit_routine;
97         thread->arg = arg;
98         thread->exit_userdata = exit_userdata;
99         thread->exitstatus = 0;
100         thread->exiting = FALSE;
101         
102         *threadp = thread;
103
104         if((result = pthread_create(&thread->id, attr,
105                                     timed_thread_start_routine,
106                                     (void *)thread)) != 0) {
107                 g_free(thread);
108                 return(result);
109         }
110         
111         return(0);
112 }
113
114 int _wapi_timed_thread_join(TimedThread *thread, struct timespec *timeout,
115                             guint32 *exitstatus)
116 {
117         int result;
118         
119         mono_mutex_lock(&thread->join_mutex);
120         result=0;
121         
122         /* Wait until the thread announces that it's exiting, or until
123          * timeout.
124          */
125         while(result == 0 && !thread->exiting) {
126                 if(timeout == NULL) {
127                         result = pthread_cond_wait(&thread->exit_cond,
128                                                    &thread->join_mutex);
129                 } else {
130                         result = pthread_cond_timedwait(&thread->exit_cond,
131                                                         &thread->join_mutex,
132                                                         timeout);
133                 }
134         }
135         
136         mono_mutex_unlock(&thread->join_mutex);
137         if(result == 0 && thread->exiting) {
138                 if(exitstatus!=NULL) {
139                         *exitstatus = thread->exitstatus;
140                 }
141         }
142         return(result);
143 }
144
145 void _wapi_timed_thread_destroy (TimedThread *thread)
146 {
147         mono_mutex_destroy (&thread->join_mutex);
148         pthread_cond_destroy (&thread->exit_cond);
149         
150         g_free(thread);
151 }