4d6a374bdc84259efe923b9f8daf8f04c8440e5b
[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/appdomain.h>
16 #include <mono/metadata/domain-internals.h>
17
18 #include <errno.h>
19
20 #define THREADS_DEBUG(...)
21 //#define THREADS_DEBUG(...) g_message(__VA_ARGS__)
22
23 /*
24 Mutex that makes sure only a single thread can be suspending others.
25 Suspend is a very racy operation since it requires restarting until
26 the target thread is not on an unsafe region.
27
28 We could implement this using critical regions, but would be much much
29 harder for an operation that is hardly performance critical.
30
31 The GC has to acquire this lock before starting a STW to make sure
32 a runtime suspend won't make it wronly see a thread in a safepoint
33 when it is in fact not.
34 */
35 static CRITICAL_SECTION global_suspend_lock;
36
37
38 static int thread_info_size;
39 static MonoThreadInfoCallbacks threads_callbacks;
40 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
41 static MonoNativeTlsKey thread_info_key, small_id_key;
42 static MonoLinkedListSet thread_list;
43
44 static inline void
45 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
46 {
47         if (retain != 0)
48                 mono_hazard_pointer_clear (hp, 0);
49         if (retain != 1)
50                 mono_hazard_pointer_clear (hp, 1);
51         if (retain != 2)
52                 mono_hazard_pointer_clear (hp, 2);
53 }
54
55 /*
56 If return non null Hazard Pointer 1 holds the return value.
57 */
58 MonoThreadInfo*
59 mono_thread_info_lookup (MonoNativeThreadId id)
60 {
61                 MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
62
63         if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
64                 mono_hazard_pointer_clear_all (hp, -1);
65                 return NULL;
66         } 
67
68         mono_hazard_pointer_clear_all (hp, 1);
69         return mono_hazard_pointer_get_val (hp, 1);
70 }
71
72 static gboolean
73 mono_thread_info_insert (MonoThreadInfo *info)
74 {
75         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
76
77         if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
78                 mono_hazard_pointer_clear_all (hp, -1);
79                 return FALSE;
80         } 
81
82         mono_hazard_pointer_clear_all (hp, -1);
83         return TRUE;
84 }
85
86 static gboolean
87 mono_thread_info_remove (MonoThreadInfo *info)
88 {
89         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
90         gboolean res;
91
92         THREADS_DEBUG ("removing info %p\n", info);
93         res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
94         mono_hazard_pointer_clear_all (hp, -1);
95         return res;
96 }
97
98 static void
99 free_thread_info (gpointer mem)
100 {
101         MonoThreadInfo *info = mem;
102
103         DeleteCriticalSection (&info->suspend_lock);
104         mono_threads_platform_free (info);
105
106         g_free (info);
107 }
108
109 int
110 mono_thread_info_register_small_id (void)
111 {
112         int small_id = mono_thread_small_id_alloc ();
113         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
114         return small_id;
115 }
116
117 static void*
118 register_thread (MonoThreadInfo *info, gpointer baseptr)
119 {
120         int small_id = mono_thread_info_register_small_id ();
121         gboolean result;
122         mono_thread_info_set_tid (info, mono_native_thread_id_get ());
123         info->small_id = small_id;
124
125         InitializeCriticalSection (&info->suspend_lock);
126
127         /*set TLS early so SMR works */
128         mono_native_tls_set_value (thread_info_key, info);
129
130         THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
131
132         if (threads_callbacks.thread_register) {
133                 if (threads_callbacks.thread_register (info, baseptr) == NULL) {
134                         g_warning ("thread registation failed\n");
135                         g_free (info);
136                         return NULL;
137                 }
138         }
139
140         mono_threads_platform_register (info);
141
142         /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
143         result = mono_thread_info_insert (info);
144         g_assert (result);
145         return info;
146 }
147
148 static void
149 unregister_thread (void *arg)
150 {
151         MonoThreadInfo *info = arg;
152         int small_id = info->small_id;
153         g_assert (info);
154
155         THREADS_DEBUG ("unregistering info %p\n", info);
156
157         /*
158          * TLS destruction order is not reliable so small_id might be cleaned up
159          * before us.
160          */
161         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
162
163         /*
164         The unregister callback is reposible for calling mono_threads_unregister_current_thread
165         since it usually needs to be done in sync with the GC does a stop-the-world.
166         */
167         if (threads_callbacks.thread_unregister)
168                 threads_callbacks.thread_unregister (info);
169         else
170                 mono_threads_unregister_current_thread (info);
171
172         /*now it's safe to free the thread info.*/
173         mono_thread_hazardous_free_or_queue (info, free_thread_info);
174         mono_thread_small_id_free (small_id);
175 }
176
177 /**
178  * Removes the current thread from the thread list.
179  * This must be called from the thread unregister callback and nowhere else.
180  * The current thread must be passed as TLS might have already been cleaned up.
181 */
182 void
183 mono_threads_unregister_current_thread (MonoThreadInfo *info)
184 {
185         gboolean result;
186         g_assert (mono_thread_info_get_tid (info) == mono_native_thread_id_get ());
187         result = mono_thread_info_remove (info);
188         g_assert (result);
189
190 }
191
192 MonoThreadInfo*
193 mono_thread_info_current (void)
194 {
195         return mono_native_tls_get_value (thread_info_key);
196 }
197
198 int
199 mono_thread_info_get_small_id (void)
200 {
201         gpointer val = mono_native_tls_get_value (small_id_key);
202         if (!val)
203                 return -1;
204         return GPOINTER_TO_INT (val) - 1;
205 }
206
207 MonoLinkedListSet*
208 mono_thread_info_list_head (void)
209 {
210         return &thread_list;
211 }
212
213 MonoThreadInfo*
214 mono_thread_info_attach (void *baseptr)
215 {
216         MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
217         if (!info) {
218                 info = g_malloc0 (thread_info_size);
219                 THREADS_DEBUG ("attaching %p\n", info);
220                 if (!register_thread (info, baseptr))
221                         return NULL;
222         } else if (threads_callbacks.thread_attach) {
223                 threads_callbacks.thread_attach (info);
224         }
225         return info;
226 }
227
228 void
229 mono_thread_info_dettach (void)
230 {
231         MonoThreadInfo *info = mono_native_tls_get_value (thread_info_key);
232         if (info) {
233                 THREADS_DEBUG ("detaching %p\n", info);
234                 unregister_thread (info);
235         }
236 }
237
238 void
239 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
240 {
241         gboolean res;
242         threads_callbacks = *callbacks;
243         thread_info_size = info_size;
244 #ifdef HOST_WIN32
245         res = mono_native_tls_alloc (thread_info_key, NULL);
246 #else
247         res = mono_native_tls_alloc (thread_info_key, unregister_thread);
248 #endif
249         g_assert (res);
250
251         res = mono_native_tls_alloc (small_id_key, NULL);
252         g_assert (res);
253
254         InitializeCriticalSection (&global_suspend_lock);
255
256         mono_lls_init (&thread_list, NULL);
257         mono_thread_smr_init ();
258         mono_threads_init_platform ();
259
260         g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
261 }
262
263 void
264 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
265 {
266         runtime_callbacks = *callbacks;
267 }
268
269 MonoThreadInfoRuntimeCallbacks *
270 mono_threads_get_runtime_callbacks (void)
271 {
272         return &runtime_callbacks;
273 }
274
275 /*
276 The return value is only valid until a matching mono_thread_info_resume is called
277 */
278 static MonoThreadInfo*
279 mono_thread_info_suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
280 {
281         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();      
282         MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
283         if (!info)
284                 return NULL;
285
286         EnterCriticalSection (&info->suspend_lock);
287
288         /*thread is on the process of detaching*/
289         if (info->thread_state > STATE_RUNNING) {
290                 mono_hazard_pointer_clear (hp, 1);
291                 return NULL;
292         }
293
294         THREADS_DEBUG ("suspend %x IN COUNT %d\n", tid, info->suspend_count);
295
296         if (info->suspend_count) {
297                 ++info->suspend_count;
298                 mono_hazard_pointer_clear (hp, 1);
299                 LeaveCriticalSection (&info->suspend_lock);
300                 return info;
301         }
302
303         if (!mono_threads_core_suspend (info)) {
304                 LeaveCriticalSection (&info->suspend_lock);
305                 mono_hazard_pointer_clear (hp, 1);
306                 return NULL;
307         }
308
309         if (interrupt_kernel) 
310                 mono_threads_core_interrupt (info);
311
312         ++info->suspend_count;
313         LeaveCriticalSection (&info->suspend_lock);
314         mono_hazard_pointer_clear (hp, 1);
315
316         return info;
317 }
318
319 void
320 mono_thread_info_self_suspend ()
321 {
322         MonoThreadInfo *info = mono_thread_info_current ();
323         if (!info)
324                 return;
325
326         EnterCriticalSection (&info->suspend_lock);
327
328         THREADS_DEBUG ("self suspend IN COUNT %d\n", info->suspend_count);
329
330         g_assert (info->suspend_count == 0);
331         ++info->suspend_count;
332
333         /*
334         The internal API contract with this function is a bit out of the ordinary.
335         mono_threads_core_self_suspend executes with suspend_lock taken and must
336         release it after capturing the current context.
337         */
338         mono_threads_core_self_suspend (info);
339 }
340
341 gboolean
342 mono_thread_info_resume (MonoNativeThreadId tid)
343 {
344         gboolean result = TRUE;
345         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();      
346         MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
347         if (!info)
348                 return FALSE;
349
350         EnterCriticalSection (&info->suspend_lock);
351
352         THREADS_DEBUG ("resume %x IN COUNT %d\n",tid, info->suspend_count);
353
354         if (info->suspend_count <= 0) {
355                 LeaveCriticalSection (&info->suspend_lock);
356                 mono_hazard_pointer_clear (hp, 1);
357                 return FALSE;
358         }
359
360         /*
361          * The theory here is that if we manage to suspend the thread it means it did not
362          * start cleanup since it take the same lock. 
363         */
364         g_assert (mono_thread_info_get_tid (info));
365
366         if (--info->suspend_count == 0)
367                 result = mono_threads_core_resume (info);
368
369         LeaveCriticalSection (&info->suspend_lock);
370         mono_hazard_pointer_clear (hp, 1);
371
372         return result;
373 }
374
375 /*
376 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
377 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
378 */
379 static gboolean
380 is_thread_in_critical_region (MonoThreadInfo *info)
381 {
382         MonoMethod *method;
383         MonoJitInfo *ji = mono_jit_info_table_find (
384                 info->suspend_state.unwind_data [MONO_UNWIND_DATA_DOMAIN],
385                 MONO_CONTEXT_GET_IP (&info->suspend_state.ctx));
386
387         if (!ji)
388                 return FALSE;
389
390         method = ji->method;
391
392         return threads_callbacks.mono_method_is_critical (method);
393 }
394
395 /*
396 WARNING:
397 If we are trying to suspend a target that is on a critical region
398 and running a syscall we risk looping forever if @interrupt_kernel is FALSE.
399 So, be VERY carefull in calling this with @interrupt_kernel == FALSE.
400 */
401 MonoThreadInfo*
402 mono_thread_info_safe_suspend_sync (MonoNativeThreadId id, gboolean interrupt_kernel)
403 {
404         MonoThreadInfo *info = NULL;
405         int sleep_duration = 0;
406
407         /*FIXME: unify this with self-suspend*/
408         g_assert (id != mono_native_thread_id_get ());
409
410         mono_thread_info_suspend_lock ();
411
412         for (;;) {
413                 if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel))) {
414                         g_warning ("failed to suspend thread %p, hopefully it is dead", (gpointer)id);
415                         mono_thread_info_suspend_unlock ();
416                         return NULL;
417                 }
418                 /*WARNING: We now are in interrupt context until we resume the thread. */
419                 if (!is_thread_in_critical_region (info))
420                         break;
421
422                 if (!mono_thread_info_resume (id)) {
423                         g_warning ("failed to result thread %p, hopefully it is dead", (gpointer)id);
424                         mono_thread_info_suspend_unlock ();
425                         return NULL;
426                 }
427                 THREADS_DEBUG ("restarted thread %p\n", (gpointer)id);
428
429                 if (!sleep_duration) {
430 #ifdef HOST_WIN32
431                         SwitchToThread ();
432 #else
433                         sched_yield ();
434 #endif
435                 }
436                 else {
437                         g_usleep (sleep_duration);
438                 }
439                 sleep_duration += 10;
440         }
441
442         mono_thread_info_suspend_unlock ();
443         return info;
444 }
445
446 /**
447 Inject an assynchronous call into the target thread. The target thread must be suspended and
448 only a single async call can be setup for a given suspend cycle.
449 This async call must cause stack unwinding as the current implementation doesn't save enough state
450 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
451 currently used only to deliver exceptions.
452 */
453 void
454 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
455 {
456         g_assert (info->suspend_count);
457         /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
458         g_assert (!info->async_target);
459         info->async_target = target_func;
460         /* This is not GC tracked */
461         info->user_data = user_data;
462 }
463
464 /*
465 The suspend lock is held during any suspend in progress.
466 A GC that has safepoints must take this lock as part of its
467 STW to make sure no unsafe pending suspend is in progress.   
468 */
469 void
470 mono_thread_info_suspend_lock (void)
471 {
472         EnterCriticalSection (&global_suspend_lock);
473 }
474
475 void
476 mono_thread_info_suspend_unlock (void)
477 {
478         LeaveCriticalSection (&global_suspend_lock);
479 }
480
481 /*
482 Disabled by default for now.
483 To enable this we need mini to implement the callbacks by MonoThreadInfoRuntimeCallbacks
484 which means mono-context and setup_async_callback, and we need a mono-threads backend.
485 */
486 gboolean
487 mono_thread_info_new_interrupt_enabled (void)
488 {
489         /*We need STW gc events to work correctly*/
490 #if defined (HAVE_BOEHM_GC) && !defined (USE_INCLUDED_LIBGC)
491         return FALSE:
492 #endif
493         /*port not done*/
494 #if defined(HOST_WIN32)
495         return FALSE;
496 #endif
497 #if defined (__i386__)
498         return TRUE;
499 #endif
500         return FALSE;
501 }