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