Fix a warning.
[mono.git] / mono / utils / mono-threads.c
1 /*
2  * mono-threads.c: Low-level threading
3  *
4  * Author:
5  *      Rodrigo Kumpera (kumpera@gmail.com)
6  *
7  * (C) 2011 Novell, Inc
8  */
9
10 #include <mono/utils/mono-compiler.h>
11 #include <mono/utils/mono-semaphore.h>
12 #include <mono/utils/mono-threads.h>
13 #include <mono/utils/mono-tls.h>
14 #include <mono/utils/hazard-pointer.h>
15 #include <mono/metadata/gc-internal.h>
16 #include <mono/metadata/appdomain.h>
17
18 #include <errno.h>
19
20
21 #define THREADS_DEBUG(...)
22 //#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
23
24 typedef struct {
25         void *(*start_routine) (void *);
26         void *arg;
27         int flags;
28         MonoSemType registered;
29 } ThreadStartInfo;
30
31 static int thread_info_size;
32 static MonoThreadInfoCallbacks threads_callbacks;
33 static MonoNativeTlsKey thread_info_key;
34 static MonoLinkedListSet thread_list;
35
36 static inline void
37 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
38 {
39         if (retain != 0)
40                 mono_hazard_pointer_clear (hp, 0);
41         if (retain != 1)
42                 mono_hazard_pointer_clear (hp, 1);
43         if (retain != 2)
44                 mono_hazard_pointer_clear (hp, 2);
45 }
46
47 static gboolean
48 mono_thread_info_insert (MonoThreadInfo *info)
49 {
50         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
51
52         if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
53                 mono_hazard_pointer_clear_all (hp, -1);
54                 return FALSE;
55         } 
56
57         mono_hazard_pointer_clear_all (hp, -1);
58         return TRUE;
59 }
60
61 static gboolean
62 mono_thread_info_remove (MonoThreadInfo *info)
63 {
64         /*TLS is gone by now, so we can't rely on it to retrieve hp*/
65         MonoThreadHazardPointers *hp = mono_hazard_pointer_get_by_id (info->small_id);
66         gboolean res;
67
68         THREADS_DEBUG ("removing info %p\n", info);
69         res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
70         mono_hazard_pointer_clear_all (hp, -1);
71         return res;
72 }
73
74 static void
75 free_thread_info (gpointer mem)
76 {
77         MonoThreadInfo *info = mem;
78
79         g_free (info);
80 }
81
82 static void*
83 register_thread (MonoThreadInfo *info, gpointer baseptr)
84 {
85         gboolean result;
86         mono_thread_info_set_tid (info, mono_native_thread_id_get ());
87         info->small_id = mono_thread_small_id_alloc ();
88
89         THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
90
91         if (threads_callbacks.thread_register) {
92                 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
93                         g_warning ("thread registation failed\n");
94                         g_free (info);
95                         return NULL;
96                 }
97         }
98
99         mono_native_tls_set_value (thread_info_key, info);
100
101         /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
102         result = mono_thread_info_insert (info);
103         g_assert (result);
104         return info;
105 }
106
107 static void
108 unregister_thread (void *arg)
109 {
110         gboolean result;
111         MonoThreadInfo *info = arg;
112         int small_id = info->small_id;
113         g_assert (info);
114
115         THREADS_DEBUG ("unregistering info %p\n", info);
116
117         if (threads_callbacks.thread_unregister)
118                 threads_callbacks.thread_unregister (info);
119
120         result = mono_thread_info_remove (info);
121         g_assert (result);
122
123         mono_thread_small_id_free (small_id);
124 }
125
126 static void*
127 inner_start_thread (void *arg)
128 {
129         ThreadStartInfo *start_info = arg;
130         MonoThreadInfo* info;
131         void *t_arg = start_info->arg;
132         void *(*start_func) (void*) = start_info->start_routine;
133         void *result;
134         int post_result;
135
136         info = g_malloc0 (thread_info_size);
137
138         register_thread (info, &post_result);
139
140         post_result = MONO_SEM_POST (&(start_info->registered));
141         g_assert (!post_result);
142
143         result = start_func (t_arg);
144         g_assert (!mono_domain_get ());
145
146         return result;
147 }
148
149 MonoThreadInfo*
150 mono_thread_info_current (void)
151 {
152         return mono_native_tls_get_value (thread_info_key);
153 }
154
155 MonoLinkedListSet*
156 mono_thread_info_list_head (void)
157 {
158         return &thread_list;
159 }
160
161 MonoThreadInfo*
162 mono_thread_info_attach (void *baseptr)
163 {
164         MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
165         if (!info) {
166                 info = g_malloc0 (thread_info_size);
167                 if (!register_thread (info, baseptr))
168                         return NULL;
169         } else if (threads_callbacks.thread_attach) {
170                 threads_callbacks.thread_attach (info);
171         }
172         return info;
173 }
174
175 void
176 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
177 {
178         gboolean res;
179         threads_callbacks = *callbacks;
180         thread_info_size = info_size;
181         res = mono_native_tls_alloc (&thread_info_key, unregister_thread);
182         mono_lls_init (&thread_list, free_thread_info);
183         mono_thread_smr_init ();
184
185         g_assert (res);
186         g_assert (sizeof (MonoNativeThreadId) == sizeof (uintptr_t));
187 }
188
189 int
190 mono_threads_pthread_create (pthread_t *new_thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg)
191 {
192         ThreadStartInfo *start_info;
193         int result;
194
195         start_info = g_malloc0 (sizeof (ThreadStartInfo));
196         if (!start_info)
197                 return ENOMEM;
198         MONO_SEM_INIT (&(start_info->registered), 0);
199         start_info->arg = arg;
200         start_info->start_routine = start_routine;
201
202         result = pthread_create (new_thread, attr, inner_start_thread, start_info);
203         if (result == 0) {
204                 while (MONO_SEM_WAIT (&(start_info->registered)) != 0) {
205                         /*if (EINTR != errno) ABORT("sem_wait failed"); */
206                 }
207         }
208         MONO_SEM_DESTROY (&(start_info->registered));
209         g_free (start_info);
210         return result;
211 }