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