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