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