[threads] Rework MonoThreadInfoCallbacks.thread_{register,detach,unregister} callback...
[mono.git] / mono / utils / mono-threads.c
1 /**
2  * \file
3  * Low-level threading
4  *
5  * Author:
6  *      Rodrigo Kumpera (kumpera@gmail.com)
7  *
8  * Copyright 2011 Novell, Inc (http://www.novell.com)
9  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10  * Licensed under the MIT license. See LICENSE file in the project root for full license information.
11  */
12
13 #include <config.h>
14
15 /* enable pthread extensions */
16 #ifdef TARGET_MACH
17 #define _DARWIN_C_SOURCE
18 #endif
19
20 #include <mono/utils/mono-compiler.h>
21 #include <mono/utils/mono-os-semaphore.h>
22 #include <mono/utils/mono-threads.h>
23 #include <mono/utils/mono-tls.h>
24 #include <mono/utils/hazard-pointer.h>
25 #include <mono/utils/mono-memory-model.h>
26 #include <mono/utils/mono-mmap.h>
27 #include <mono/utils/atomic.h>
28 #include <mono/utils/mono-time.h>
29 #include <mono/utils/mono-lazy-init.h>
30 #include <mono/utils/mono-coop-mutex.h>
31 #include <mono/utils/mono-coop-semaphore.h>
32 #include <mono/utils/mono-threads-coop.h>
33 #include <mono/utils/mono-threads-debug.h>
34 #include <mono/utils/os-event.h>
35 #include <mono/utils/w32api.h>
36
37 #include <errno.h>
38
39 #if defined(__MACH__)
40 #include <mono/utils/mach-support.h>
41 #endif
42
43 /*
44 Mutex that makes sure only a single thread can be suspending others.
45 Suspend is a very racy operation since it requires restarting until
46 the target thread is not on an unsafe region.
47
48 We could implement this using critical regions, but would be much much
49 harder for an operation that is hardly performance critical.
50
51 The GC has to acquire this lock before starting a STW to make sure
52 a runtime suspend won't make it wronly see a thread in a safepoint
53 when it is in fact not.
54
55 This has to be a naked locking primitive, and not a coop aware one, as
56 it needs to be usable when destroying thread_info_key, the TLS key for
57 the current MonoThreadInfo. In this case, mono_thread_info_current_unchecked,
58 (which is used inside MONO_ENTER_GC_SAFE), would return NULL, leading
59 to an assertion error. We then simply switch state manually in
60 mono_thread_info_suspend_lock_with_info.
61 */
62 static MonoSemType global_suspend_semaphore;
63
64 static size_t thread_info_size;
65 static MonoThreadInfoCallbacks threads_callbacks;
66 static MonoThreadInfoRuntimeCallbacks runtime_callbacks;
67 static MonoNativeTlsKey thread_info_key, thread_exited_key;
68 #ifdef HAVE_KW_THREAD
69 static __thread guint32 tls_small_id;
70 #else
71 static MonoNativeTlsKey small_id_key;
72 #endif
73 static MonoLinkedListSet thread_list;
74 static gboolean mono_threads_inited = FALSE;
75
76 static MonoSemType suspend_semaphore;
77 static size_t pending_suspends;
78
79 static mono_mutex_t join_mutex;
80
81 #define mono_thread_info_run_state(info) (((MonoThreadInfo*)info)->thread_state & THREAD_STATE_MASK)
82
83 /*warn at 50 ms*/
84 #define SLEEP_DURATION_BEFORE_WARNING (50)
85 /*never aborts */
86 #define SLEEP_DURATION_BEFORE_ABORT MONO_INFINITE_WAIT
87
88 static guint32 sleepWarnDuration = SLEEP_DURATION_BEFORE_WARNING,
89             sleepAbortDuration = SLEEP_DURATION_BEFORE_ABORT;
90
91 static int suspend_posts, resume_posts, abort_posts, waits_done, pending_ops;
92
93 void
94 mono_threads_notify_initiator_of_abort (MonoThreadInfo* info)
95 {
96         THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-ABORT] %p\n", mono_thread_info_get_tid (info));
97         InterlockedIncrement (&abort_posts);
98         mono_os_sem_post (&suspend_semaphore);
99 }
100
101 void
102 mono_threads_notify_initiator_of_suspend (MonoThreadInfo* info)
103 {
104         THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-SUSPEND] %p\n", mono_thread_info_get_tid (info));
105         InterlockedIncrement (&suspend_posts);
106         mono_os_sem_post (&suspend_semaphore);
107 }
108
109 void
110 mono_threads_notify_initiator_of_resume (MonoThreadInfo* info)
111 {
112         THREADS_SUSPEND_DEBUG ("[INITIATOR-NOTIFY-RESUME] %p\n", mono_thread_info_get_tid (info));
113         InterlockedIncrement (&resume_posts);
114         mono_os_sem_post (&suspend_semaphore);
115 }
116
117 static gboolean
118 begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
119 {
120         if (mono_threads_is_coop_enabled ()) {
121                 /* There's nothing else to do after we async request the thread to suspend */
122                 mono_threads_add_to_pending_operation_set (info);
123                 return TRUE;
124         }
125
126         return mono_threads_suspend_begin_async_suspend (info, interrupt_kernel);
127 }
128
129 static gboolean
130 check_async_suspend (MonoThreadInfo *info)
131 {
132         if (mono_threads_is_coop_enabled ()) {
133                 /* Async suspend can't async fail on coop */
134                 return TRUE;
135         }
136
137         return mono_threads_suspend_check_suspend_result (info);
138 }
139
140 static void
141 resume_async_suspended (MonoThreadInfo *info)
142 {
143         if (mono_threads_is_coop_enabled ())
144                 g_assert_not_reached ();
145
146         g_assert (mono_threads_suspend_begin_async_resume (info));
147 }
148
149 static void
150 resume_self_suspended (MonoThreadInfo* info)
151 {
152         THREADS_SUSPEND_DEBUG ("**BEGIN self-resume %p\n", mono_thread_info_get_tid (info));
153         mono_os_sem_post (&info->resume_semaphore);
154 }
155
156 void
157 mono_thread_info_wait_for_resume (MonoThreadInfo* info)
158 {
159         int res;
160         THREADS_SUSPEND_DEBUG ("**WAIT self-resume %p\n", mono_thread_info_get_tid (info));
161         res = mono_os_sem_wait (&info->resume_semaphore, MONO_SEM_FLAGS_NONE);
162         g_assert (res != -1);
163 }
164
165 static void
166 resume_blocking_suspended (MonoThreadInfo* info)
167 {
168         THREADS_SUSPEND_DEBUG ("**BEGIN blocking-resume %p\n", mono_thread_info_get_tid (info));
169         mono_os_sem_post (&info->resume_semaphore);
170 }
171
172 void
173 mono_threads_add_to_pending_operation_set (MonoThreadInfo* info)
174 {
175         THREADS_SUSPEND_DEBUG ("added %p to pending suspend\n", mono_thread_info_get_tid (info));
176         ++pending_suspends;
177         InterlockedIncrement (&pending_ops);
178 }
179
180 void
181 mono_threads_begin_global_suspend (void)
182 {
183         size_t ps = pending_suspends;
184         if (G_UNLIKELY (ps != 0))
185                 g_error ("pending_suspends = %d, but must be 0", ps);
186         THREADS_SUSPEND_DEBUG ("------ BEGIN GLOBAL OP sp %d rp %d ap %d wd %d po %d (sp + rp + ap == wd) (wd == po)\n", suspend_posts, resume_posts,
187                 abort_posts, waits_done, pending_ops);
188         g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
189         mono_threads_coop_begin_global_suspend ();
190 }
191
192 void
193 mono_threads_end_global_suspend (void) 
194 {
195         size_t ps = pending_suspends;
196         if (G_UNLIKELY (ps != 0))
197                 g_error ("pending_suspends = %d, but must be 0", ps);
198         THREADS_SUSPEND_DEBUG ("------ END GLOBAL OP sp %d rp %d ap %d wd %d po %d\n", suspend_posts, resume_posts,
199                 abort_posts, waits_done, pending_ops);
200         g_assert ((suspend_posts + resume_posts + abort_posts) == waits_done);
201         mono_threads_coop_end_global_suspend ();
202 }
203
204 static void
205 dump_threads (void)
206 {
207         MonoThreadInfo *cur = mono_thread_info_current ();
208
209         MOSTLY_ASYNC_SAFE_PRINTF ("STATE CUE CARD: (? means a positive number, usually 1 or 2, * means any number)\n");
210         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x0\t- starting (GOOD, unless the thread is running managed code)\n");
211         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x1\t- running (BAD, unless it's the gc thread)\n");
212         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x2\t- detached (GOOD, unless the thread is running managed code)\n");
213         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?03\t- async suspended (GOOD)\n");
214         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?04\t- self suspended (GOOD)\n");
215         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?05\t- async suspend requested (BAD)\n");
216         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?06\t- self suspend requested (BAD)\n");
217         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x*07\t- blocking (GOOD)\n");
218         MOSTLY_ASYNC_SAFE_PRINTF ("\t0x?08\t- blocking with pending suspend (GOOD)\n");
219
220         FOREACH_THREAD_SAFE (info) {
221 #ifdef TARGET_MACH
222                 char thread_name [256] = { 0 };
223                 pthread_getname_np (mono_thread_info_get_tid (info), thread_name, 255);
224
225                 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] (%s) state %x  %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, thread_name, info->thread_state, info == cur ? "GC INITIATOR" : "" );
226 #else
227                 MOSTLY_ASYNC_SAFE_PRINTF ("--thread %p id %p [%p] state %x  %s\n", info, (void *) mono_thread_info_get_tid (info), (void*)(size_t)info->native_handle, info->thread_state, info == cur ? "GC INITIATOR" : "" );
228 #endif
229         } FOREACH_THREAD_SAFE_END
230 }
231
232 gboolean
233 mono_threads_wait_pending_operations (void)
234 {
235         int i;
236         int c = pending_suspends;
237
238         /* Wait threads to park */
239         THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-COUNT] %d\n", c);
240         if (pending_suspends) {
241                 MonoStopwatch suspension_time;
242                 mono_stopwatch_start (&suspension_time);
243                 for (i = 0; i < pending_suspends; ++i) {
244                         THREADS_SUSPEND_DEBUG ("[INITIATOR-WAIT-WAITING]\n");
245                         InterlockedIncrement (&waits_done);
246                         if (mono_os_sem_timedwait (&suspend_semaphore, sleepAbortDuration, MONO_SEM_FLAGS_NONE) == MONO_SEM_TIMEDWAIT_RET_SUCCESS)
247                                 continue;
248                         mono_stopwatch_stop (&suspension_time);
249
250                         dump_threads ();
251
252                         MOSTLY_ASYNC_SAFE_PRINTF ("WAITING for %d threads, got %d suspended\n", (int)pending_suspends, i);
253                         g_error ("suspend_thread suspend took %d ms, which is more than the allowed %d ms", (int)mono_stopwatch_elapsed_ms (&suspension_time), sleepAbortDuration);
254                 }
255                 mono_stopwatch_stop (&suspension_time);
256                 THREADS_SUSPEND_DEBUG ("Suspending %d threads took %d ms.\n", (int)pending_suspends, (int)mono_stopwatch_elapsed_ms (&suspension_time));
257
258         }
259
260         pending_suspends = 0;
261
262         return c > 0;
263 }
264
265
266 //Thread initialization code
267
268 static inline void
269 mono_hazard_pointer_clear_all (MonoThreadHazardPointers *hp, int retain)
270 {
271         if (retain != 0)
272                 mono_hazard_pointer_clear (hp, 0);
273         if (retain != 1)
274                 mono_hazard_pointer_clear (hp, 1);
275         if (retain != 2)
276                 mono_hazard_pointer_clear (hp, 2);
277 }
278
279 /*
280 If return non null Hazard Pointer 1 holds the return value.
281 */
282 MonoThreadInfo*
283 mono_thread_info_lookup (MonoNativeThreadId id)
284 {
285         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
286
287         if (!mono_lls_find (&thread_list, hp, (uintptr_t)id)) {
288                 mono_hazard_pointer_clear_all (hp, -1);
289                 return NULL;
290         } 
291
292         mono_hazard_pointer_clear_all (hp, 1);
293         return (MonoThreadInfo *) mono_hazard_pointer_get_val (hp, 1);
294 }
295
296 static gboolean
297 mono_thread_info_insert (MonoThreadInfo *info)
298 {
299         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
300
301         if (!mono_lls_insert (&thread_list, hp, (MonoLinkedListSetNode*)info)) {
302                 mono_hazard_pointer_clear_all (hp, -1);
303                 return FALSE;
304         } 
305
306         mono_hazard_pointer_clear_all (hp, -1);
307         return TRUE;
308 }
309
310 static gboolean
311 mono_thread_info_remove (MonoThreadInfo *info)
312 {
313         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
314         gboolean res;
315
316         THREADS_DEBUG ("removing info %p\n", info);
317         res = mono_lls_remove (&thread_list, hp, (MonoLinkedListSetNode*)info);
318         mono_hazard_pointer_clear_all (hp, -1);
319         return res;
320 }
321
322 static void
323 free_thread_info (gpointer mem)
324 {
325         MonoThreadInfo *info = (MonoThreadInfo *) mem;
326
327         mono_os_sem_destroy (&info->resume_semaphore);
328         mono_threads_suspend_free (info);
329
330         g_free (info);
331 }
332
333 int
334 mono_thread_info_register_small_id (void)
335 {
336         int small_id = mono_thread_small_id_alloc ();
337 #ifdef HAVE_KW_THREAD
338         tls_small_id = small_id;
339 #else
340         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (small_id + 1));
341 #endif
342         return small_id;
343 }
344
345 static void
346 thread_handle_destroy (gpointer data)
347 {
348         MonoThreadHandle *thread_handle;
349
350         thread_handle = (MonoThreadHandle*) data;
351
352         mono_os_event_destroy (&thread_handle->event);
353         g_free (thread_handle);
354 }
355
356 static gboolean
357 register_thread (MonoThreadInfo *info)
358 {
359         size_t stsize = 0;
360         guint8 *staddr = NULL;
361         gboolean result;
362
363         info->small_id = mono_thread_info_register_small_id ();
364         mono_thread_info_set_tid (info, mono_native_thread_id_get ());
365
366         info->handle = g_new0 (MonoThreadHandle, 1);
367         mono_refcount_init (info->handle, thread_handle_destroy);
368         mono_os_event_init (&info->handle->event, FALSE);
369
370         mono_os_sem_init (&info->resume_semaphore, 0);
371
372         /*set TLS early so SMR works */
373         mono_native_tls_set_value (thread_info_key, info);
374
375         mono_thread_info_get_stack_bounds (&staddr, &stsize);
376         g_assert (staddr);
377         g_assert (stsize);
378         info->stack_start_limit = staddr;
379         info->stack_end = staddr + stsize;
380
381         info->stackdata = g_byte_array_new ();
382
383         mono_threads_suspend_register (info);
384
385         THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
386
387         if (threads_callbacks.thread_attach) {
388                 if (!threads_callbacks.thread_attach (info)) {
389                         // g_warning ("thread registation failed\n");
390                         mono_native_tls_set_value (thread_info_key, NULL);
391                         return FALSE;
392                 }
393         }
394
395         /*
396         Transition it before taking any locks or publishing itself to reduce the chance
397         of others witnessing a detached thread.
398         We can reasonably expect that until this thread gets published, no other thread will
399         try to manipulate it.
400         */
401         mono_threads_transition_attach (info);
402         mono_thread_info_suspend_lock ();
403         /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
404         result = mono_thread_info_insert (info);
405         g_assert (result);
406         mono_thread_info_suspend_unlock ();
407
408         return TRUE;
409 }
410
411 static void
412 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
413
414 static void
415 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
416
417 static void
418 unregister_thread (void *arg)
419 {
420         gpointer gc_unsafe_stackdata;
421         MonoThreadInfo *info;
422         int small_id;
423         gboolean result;
424         gpointer handle;
425
426         info = (MonoThreadInfo *) arg;
427         g_assert (info);
428         g_assert (mono_thread_info_is_current (info));
429         g_assert (mono_thread_info_is_live (info));
430
431         /* Pump the HP queue while the thread is alive.*/
432         mono_thread_hazardous_try_free_some ();
433
434         small_id = info->small_id;
435
436         /* We only enter the GC unsafe region, as when exiting this function, the thread
437          * will be detached, and the current MonoThreadInfo* will be destroyed. */
438         mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
439
440         THREADS_DEBUG ("unregistering info %p\n", info);
441
442         mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
443
444         /*
445          * TLS destruction order is not reliable so small_id might be cleaned up
446          * before us.
447          */
448 #ifndef HAVE_KW_THREAD
449         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
450 #endif
451
452         /* we need to duplicate it, as the info->handle is going
453          * to be closed when unregistering from the platform */
454         handle = mono_threads_open_thread_handle (info->handle);
455
456         /*
457         First perform the callback that requires no locks.
458         This callback has the potential of taking other locks, so we do it before.
459         After it completes, the thread remains functional.
460         */
461         if (threads_callbacks.thread_detach)
462                 threads_callbacks.thread_detach (info);
463
464         mono_thread_info_suspend_lock_with_info (info);
465
466         /*
467         Now perform the callback that must be done under locks.
468         This will render the thread useless and non-suspendable, so it must
469         be done while holding the suspend lock to give no other thread chance
470         to suspend it.
471         */
472         if (threads_callbacks.thread_detach_with_lock)
473                 threads_callbacks.thread_detach_with_lock (info);
474
475         /* The thread is no longer active, so unref its handle */
476         mono_threads_close_thread_handle (info->handle);
477         info->handle = NULL;
478
479         result = mono_thread_info_remove (info);
480         g_assert (result);
481         mono_threads_transition_detach (info);
482
483         mono_thread_info_suspend_unlock ();
484
485         g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
486
487         /*now it's safe to free the thread info.*/
488         mono_thread_hazardous_try_free (info, free_thread_info);
489
490         mono_thread_small_id_free (small_id);
491
492         mono_threads_signal_thread_handle (handle);
493
494         mono_threads_close_thread_handle (handle);
495
496         mono_native_tls_set_value (thread_info_key, NULL);
497 }
498
499 static void
500 thread_exited_dtor (void *arg)
501 {
502 #if defined(__MACH__)
503         /*
504          * Since we use pthread dtors to clean up thread data, if a thread
505          * is attached to the runtime by another pthread dtor after our dtor
506          * has ran, it will never be detached, leading to various problems
507          * since the thread ids etc. will be reused while they are still in
508          * the threads hashtables etc.
509          * Dtors are called in a loop until all user tls entries are 0,
510          * but the loop has a maximum count (4), so if we set the tls
511          * variable every time, it will remain set when system tls dtors
512          * are ran. This allows mono_thread_info_is_exiting () to detect
513          * whenever the thread is exiting, even if it is executed from a
514          * system tls dtor (i.e. obj-c dealloc methods).
515          */
516         mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
517 #endif
518 }
519
520 MonoThreadInfo*
521 mono_thread_info_current_unchecked (void)
522 {
523         return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
524 }
525
526
527 MonoThreadInfo*
528 mono_thread_info_current (void)
529 {
530         MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
531         if (info)
532                 return info;
533
534         info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
535
536         /*
537         We might be called during thread cleanup, but we cannot be called after cleanup as happened.
538         The way to distinguish between before, during and after cleanup is the following:
539
540         -If the TLS key is set, cleanup has not begun;
541         -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
542         -If the thread is nowhere to be found, cleanup has finished.
543
544         We cannot function after cleanup since there's no way to ensure what will happen.
545         */
546         g_assert (info);
547
548         /*We're looking up the current thread which will not be freed until we finish running, so no need to keep it on a HP */
549         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
550
551         return info;
552 }
553
554 int
555 mono_thread_info_get_small_id (void)
556 {
557 #ifdef HAVE_KW_THREAD
558         return tls_small_id;
559 #else
560         gpointer val = mono_native_tls_get_value (small_id_key);
561         if (!val)
562                 return -1;
563         return GPOINTER_TO_INT (val) - 1;
564 #endif
565 }
566
567 MonoLinkedListSet*
568 mono_thread_info_list_head (void)
569 {
570         return &thread_list;
571 }
572
573 /**
574  * mono_threads_attach_tools_thread
575  *
576  * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
577  *
578  * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
579  * not be counted as a regular thread for high order facilities such as executing managed code or accessing
580  * the managed heap.
581  *
582  * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
583  * doing things like resolving backtraces in their background processing thread.
584  */
585 void
586 mono_threads_attach_tools_thread (void)
587 {
588         MonoThreadInfo *info;
589
590         /* Must only be called once */
591         g_assert (!mono_native_tls_get_value (thread_info_key));
592         
593         while (!mono_threads_inited) { 
594                 mono_thread_info_usleep (10);
595         }
596
597         info = mono_thread_info_attach ();
598         g_assert (info);
599
600         info->tools_thread = TRUE;
601 }
602
603 MonoThreadInfo*
604 mono_thread_info_attach (void)
605 {
606         MonoThreadInfo *info;
607
608 #ifdef HOST_WIN32
609         if (!mono_threads_inited)
610         {
611                 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
612                  * thread is created before an embedding API user initialized Mono. */
613                 THREADS_DEBUG ("mono_thread_info_attach called before mono_threads_init\n");
614                 return NULL;
615         }
616 #endif
617
618         g_assert (mono_threads_inited);
619
620         info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
621         if (!info) {
622                 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
623                 THREADS_DEBUG ("attaching %p\n", info);
624                 if (!register_thread (info)) {
625                         g_free (info);
626                         return NULL;
627                 }
628         }
629
630         return info;
631 }
632
633 void
634 mono_thread_info_detach (void)
635 {
636         MonoThreadInfo *info;
637
638 #ifdef HOST_WIN32
639         if (!mono_threads_inited)
640         {
641                 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
642                  * is created before an embedding API user initialized Mono. */
643                 THREADS_DEBUG ("mono_thread_info_detach called before mono_threads_init\n");
644                 return;
645         }
646 #endif
647
648         g_assert (mono_threads_inited);
649
650         info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
651         if (info) {
652                 THREADS_DEBUG ("detaching %p\n", info);
653                 unregister_thread (info);
654         }
655 }
656
657 /*
658  * mono_thread_info_is_exiting:
659  *
660  *   Return whenever the current thread is exiting, i.e. it is running pthread
661  * dtors.
662  */
663 gboolean
664 mono_thread_info_is_exiting (void)
665 {
666 #if defined(__MACH__)
667         if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
668                 return TRUE;
669 #endif
670         return FALSE;
671 }
672
673 #ifndef HOST_WIN32
674 static void
675 thread_info_key_dtor (void *arg)
676 {
677         /* Put the MonoThreadInfo back for the duration of the
678          * unregister code.  In some circumstances the thread needs to
679          * take the GC lock which may block which requires a coop
680          * state transition. */
681         mono_native_tls_set_value (thread_info_key, arg);
682         unregister_thread (arg);
683         mono_native_tls_set_value (thread_info_key, NULL);
684 }
685 #endif
686
687 void
688 mono_threads_init (MonoThreadInfoCallbacks *callbacks, size_t info_size)
689 {
690         gboolean res;
691         threads_callbacks = *callbacks;
692         thread_info_size = info_size;
693         char *sleepLimit;
694 #ifdef HOST_WIN32
695         res = mono_native_tls_alloc (&thread_info_key, NULL);
696         res = mono_native_tls_alloc (&thread_exited_key, NULL);
697 #else
698         res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
699         res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
700 #endif
701
702         g_assert (res);
703
704 #ifndef HAVE_KW_THREAD
705         res = mono_native_tls_alloc (&small_id_key, NULL);
706 #endif
707         g_assert (res);
708
709         if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
710                 errno = 0;
711                 long threshold = strtol(sleepLimit, NULL, 10);
712                 if ((errno == 0) && (threshold >= 40))  {
713                         sleepAbortDuration = threshold;
714                         sleepWarnDuration = threshold / 20;
715                 } else
716                         g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
717                 g_free (sleepLimit);
718         }
719
720         mono_os_sem_init (&global_suspend_semaphore, 1);
721         mono_os_sem_init (&suspend_semaphore, 0);
722         mono_os_mutex_init (&join_mutex);
723
724         mono_lls_init (&thread_list, NULL);
725         mono_thread_smr_init ();
726         mono_threads_suspend_init ();
727         mono_threads_coop_init ();
728         mono_threads_platform_init ();
729
730 #if defined(__MACH__)
731         mono_mach_init (thread_info_key);
732 #endif
733
734         mono_threads_inited = TRUE;
735
736         g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
737 }
738
739 void
740 mono_threads_signals_init (void)
741 {
742         mono_threads_suspend_init_signals ();
743 }
744
745 void
746 mono_threads_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
747 {
748         runtime_callbacks = *callbacks;
749 }
750
751 MonoThreadInfoRuntimeCallbacks *
752 mono_threads_get_runtime_callbacks (void)
753 {
754         return &runtime_callbacks;
755 }
756
757 static gboolean
758 mono_thread_info_core_resume (MonoThreadInfo *info)
759 {
760         gboolean res = FALSE;
761
762         switch (mono_threads_transition_request_resume (info)) {
763         case ResumeError:
764                 res = FALSE;
765                 break;
766         case ResumeOk:
767                 res = TRUE;
768                 break;
769         case ResumeInitSelfResume:
770                 resume_self_suspended (info);
771                 res = TRUE;
772                 break;
773         case ResumeInitAsyncResume:
774                 resume_async_suspended (info);
775                 res = TRUE;
776                 break;
777         case ResumeInitBlockingResume:
778                 resume_blocking_suspended (info);
779                 res = TRUE;
780                 break;
781         }
782
783         return res;
784 }
785
786 gboolean
787 mono_thread_info_resume (MonoNativeThreadId tid)
788 {
789         gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
790         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
791         MonoThreadInfo *info;
792
793         THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
794
795         mono_thread_info_suspend_lock ();
796
797         info = mono_thread_info_lookup (tid); /*info on HP1*/
798         if (!info) {
799                 result = FALSE;
800                 goto cleanup;
801         }
802
803         result = mono_thread_info_core_resume (info);
804
805         //Wait for the pending resume to finish
806         mono_threads_wait_pending_operations ();
807
808 cleanup:
809         mono_thread_info_suspend_unlock ();
810         mono_hazard_pointer_clear (hp, 1);
811         return result;
812 }
813
814 gboolean
815 mono_thread_info_begin_suspend (MonoThreadInfo *info)
816 {
817         switch (mono_threads_transition_request_async_suspension (info)) {
818         case AsyncSuspendAlreadySuspended:
819         case AsyncSuspendBlocking:
820                 return TRUE;
821         case AsyncSuspendWait:
822                 mono_threads_add_to_pending_operation_set (info);
823                 return TRUE;
824         case AsyncSuspendInitSuspend:
825                 return begin_async_suspend (info, FALSE);
826         default:
827                 g_assert_not_reached ();
828         }
829 }
830
831 gboolean
832 mono_thread_info_begin_resume (MonoThreadInfo *info)
833 {
834         return mono_thread_info_core_resume (info);
835 }
836
837 /*
838 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
839 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
840 */
841 static gboolean
842 is_thread_in_critical_region (MonoThreadInfo *info)
843 {
844         MonoMethod *method;
845         MonoJitInfo *ji;
846         gpointer stack_start;
847         MonoThreadUnwindState *state;
848
849         if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
850                 return TRUE;
851
852         /* Are we inside a system critical region? */
853         if (info->inside_critical_region)
854                 return TRUE;
855
856         /* Are we inside a GC critical region? */
857         if (threads_callbacks.mono_thread_in_critical_region && threads_callbacks.mono_thread_in_critical_region (info)) {
858                 return TRUE;
859         }
860
861         /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
862         state = mono_thread_info_get_suspend_state (info);
863         if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
864                 return FALSE;
865
866         stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
867         /* altstack signal handler, sgen can't handle them, so we treat them as critical */
868         if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
869                 return TRUE;
870
871         if (threads_callbacks.ip_in_critical_region)
872                 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
873
874         ji = mono_jit_info_table_find (
875                 (MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN],
876                 (char *) MONO_CONTEXT_GET_IP (&state->ctx));
877
878         if (!ji)
879                 return FALSE;
880
881         method = mono_jit_info_get_method (ji);
882
883         return threads_callbacks.mono_method_is_critical (method);
884 }
885
886 gboolean
887 mono_thread_info_in_critical_location (MonoThreadInfo *info)
888 {
889         return is_thread_in_critical_region (info);
890 }
891
892 /*
893 The return value is only valid until a matching mono_thread_info_resume is called
894 */
895 static MonoThreadInfo*
896 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
897 {
898         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
899         MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
900         if (!info)
901                 return NULL;
902
903         switch (mono_threads_transition_request_async_suspension (info)) {
904         case AsyncSuspendAlreadySuspended:
905                 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
906                 return info;
907         case AsyncSuspendWait:
908                 mono_threads_add_to_pending_operation_set (info);
909                 break;
910         case AsyncSuspendInitSuspend:
911                 if (!begin_async_suspend (info, interrupt_kernel)) {
912                         mono_hazard_pointer_clear (hp, 1);
913                         return NULL;
914                 }
915                 break;
916         case AsyncSuspendBlocking:
917                 if (interrupt_kernel)
918                         mono_threads_suspend_abort_syscall (info);
919
920                 break;
921         default:
922                 g_assert_not_reached ();
923         }
924
925         //Wait for the pending suspend to finish
926         mono_threads_wait_pending_operations ();
927
928         if (!check_async_suspend (info)) {
929                 mono_thread_info_core_resume (info);
930                 mono_threads_wait_pending_operations ();
931                 mono_hazard_pointer_clear (hp, 1);
932                 return NULL;
933         }
934         return info;
935 }
936
937 static MonoThreadInfo*
938 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
939 {
940         MonoThreadInfo *info = NULL;
941         int sleep_duration = 0;
942         for (;;) {
943                 if (!(info = suspend_sync (id, interrupt_kernel))) {
944                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
945                         return NULL;
946                 }
947
948                 /*WARNING: We now are in interrupt context until we resume the thread. */
949                 if (!is_thread_in_critical_region (info))
950                         break;
951
952                 if (!mono_thread_info_core_resume (info)) {
953                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
954                         return NULL;
955                 }
956                 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
957
958                 /* Wait for the pending resume to finish */
959                 mono_threads_wait_pending_operations ();
960
961                 if (sleep_duration == 0)
962                         mono_thread_info_yield ();
963                 else
964                         g_usleep (sleep_duration);
965
966                 sleep_duration += 10;
967         }
968         return info;
969 }
970
971 void
972 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
973 {
974         int result;
975         MonoThreadInfo *info = NULL;
976         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
977
978         THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
979         /*FIXME: unify this with self-suspend*/
980         g_assert (id != mono_native_thread_id_get ());
981
982         /* This can block during stw */
983         mono_thread_info_suspend_lock ();
984         mono_threads_begin_global_suspend ();
985
986         info = suspend_sync_nolock (id, interrupt_kernel);
987         if (!info)
988                 goto done;
989
990         switch (result = callback (info, user_data)) {
991         case MonoResumeThread:
992                 mono_hazard_pointer_set (hp, 1, info);
993                 mono_thread_info_core_resume (info);
994                 mono_threads_wait_pending_operations ();
995                 break;
996         case KeepSuspended:
997                 g_assert (!mono_threads_is_coop_enabled ());
998                 break;
999         default:
1000                 g_error ("Invalid suspend_and_run callback return value %d", result);
1001         }
1002
1003 done:
1004         mono_hazard_pointer_clear (hp, 1);
1005         mono_threads_end_global_suspend ();
1006         mono_thread_info_suspend_unlock ();
1007 }
1008
1009 /**
1010 Inject an assynchronous call into the target thread. The target thread must be suspended and
1011 only a single async call can be setup for a given suspend cycle.
1012 This async call must cause stack unwinding as the current implementation doesn't save enough state
1013 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1014 currently used only to deliver exceptions.
1015 */
1016 void
1017 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1018 {
1019         if (!mono_threads_is_coop_enabled ()) {
1020                 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1021                  * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1022                  * region or entering a gc unsafe region */
1023                 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1024         }
1025         /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1026         g_assert (!info->async_target);
1027         info->async_target = target_func;
1028         /* This is not GC tracked */
1029         info->user_data = user_data;
1030 }
1031
1032 /*
1033 The suspend lock is held during any suspend in progress.
1034 A GC that has safepoints must take this lock as part of its
1035 STW to make sure no unsafe pending suspend is in progress.   
1036 */
1037
1038 static void
1039 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1040 {
1041         if (mono_threads_is_coop_enabled ()) {
1042                 g_assert (info);
1043                 g_assert (mono_thread_info_is_current (info));
1044                 g_assert (mono_thread_info_is_live (info));
1045
1046                 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1047
1048                 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1049                 g_assert (res != -1);
1050
1051                 MONO_EXIT_GC_SAFE_WITH_INFO;
1052         } else {
1053                 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1054                 g_assert (res != -1);
1055         }
1056 }
1057
1058 void
1059 mono_thread_info_suspend_lock (void)
1060 {
1061         mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1062 }
1063
1064 void
1065 mono_thread_info_suspend_unlock (void)
1066 {
1067         mono_os_sem_post (&global_suspend_semaphore);
1068 }
1069
1070 /*
1071  * This is a very specific function whose only purpose is to
1072  * break a given thread from socket syscalls.
1073  *
1074  * This only exists because linux won't fail a call to connect
1075  * if the underlying is closed.
1076  *
1077  * TODO We should cleanup and unify this with the other syscall abort
1078  * facility.
1079  */
1080 void
1081 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1082 {
1083         MonoThreadHazardPointers *hp;
1084         MonoThreadInfo *info;
1085
1086         if (tid == mono_native_thread_id_get ())
1087                 return;
1088
1089         hp = mono_hazard_pointer_get ();
1090         info = mono_thread_info_lookup (tid);
1091         if (!info)
1092                 return;
1093
1094         if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1095                 mono_hazard_pointer_clear (hp, 1);
1096                 return;
1097         }
1098
1099         mono_thread_info_suspend_lock ();
1100         mono_threads_begin_global_suspend ();
1101
1102         mono_threads_suspend_abort_syscall (info);
1103         mono_threads_wait_pending_operations ();
1104
1105         mono_hazard_pointer_clear (hp, 1);
1106
1107         mono_threads_end_global_suspend ();
1108         mono_thread_info_suspend_unlock ();
1109 }
1110
1111 /*
1112  * mono_thread_info_set_is_async_context:
1113  *
1114  *   Set whenever the current thread is in an async context. Some runtime functions might behave
1115  * differently while in an async context in order to be async safe.
1116  */
1117 void
1118 mono_thread_info_set_is_async_context (gboolean async_context)
1119 {
1120         MonoThreadInfo *info = mono_thread_info_current ();
1121
1122         if (info)
1123                 info->is_async_context = async_context;
1124 }
1125
1126 gboolean
1127 mono_thread_info_is_async_context (void)
1128 {
1129         MonoThreadInfo *info = mono_thread_info_current ();
1130
1131         if (info)
1132                 return info->is_async_context;
1133         else
1134                 return FALSE;
1135 }
1136
1137 /*
1138  * mono_thread_info_get_stack_bounds:
1139  *
1140  *   Return the address and size of the current threads stack. Return NULL as the 
1141  * stack address if the stack address cannot be determined.
1142  */
1143 void
1144 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1145 {
1146         guint8 *current = (guint8 *)&stsize;
1147         mono_threads_platform_get_stack_bounds (staddr, stsize);
1148         if (!*staddr)
1149                 return;
1150
1151         /* Sanity check the result */
1152         g_assert ((current > *staddr) && (current < *staddr + *stsize));
1153
1154         /* When running under emacs, sometimes staddr is not aligned to a page size */
1155         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1156 }
1157
1158 gboolean
1159 mono_thread_info_yield (void)
1160 {
1161         return mono_threads_platform_yield ();
1162 }
1163
1164 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1165 static MonoCoopMutex sleep_mutex;
1166 static MonoCoopCond sleep_cond;
1167
1168 static void
1169 sleep_initialize (void)
1170 {
1171         mono_coop_mutex_init (&sleep_mutex);
1172         mono_coop_cond_init (&sleep_cond);
1173 }
1174
1175 static void
1176 sleep_interrupt (gpointer data)
1177 {
1178         mono_coop_mutex_lock (&sleep_mutex);
1179         mono_coop_cond_broadcast (&sleep_cond);
1180         mono_coop_mutex_unlock (&sleep_mutex);
1181 }
1182
1183 static inline guint32
1184 sleep_interruptable (guint32 ms, gboolean *alerted)
1185 {
1186         gint64 now, end;
1187
1188         g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1189
1190         g_assert (alerted);
1191         *alerted = FALSE;
1192
1193         if (ms != MONO_INFINITE_WAIT)
1194                 end = mono_msec_ticks() + ms;
1195
1196         mono_lazy_initialize (&sleep_init, sleep_initialize);
1197
1198         mono_coop_mutex_lock (&sleep_mutex);
1199
1200         for (;;) {
1201                 if (ms != MONO_INFINITE_WAIT) {
1202                         now = mono_msec_ticks();
1203                         if (now >= end)
1204                                 break;
1205                 }
1206
1207                 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1208                 if (*alerted) {
1209                         mono_coop_mutex_unlock (&sleep_mutex);
1210                         return WAIT_IO_COMPLETION;
1211                 }
1212
1213                 if (ms != MONO_INFINITE_WAIT)
1214                         mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1215                 else
1216                         mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1217
1218                 mono_thread_info_uninstall_interrupt (alerted);
1219                 if (*alerted) {
1220                         mono_coop_mutex_unlock (&sleep_mutex);
1221                         return WAIT_IO_COMPLETION;
1222                 }
1223         }
1224
1225         mono_coop_mutex_unlock (&sleep_mutex);
1226
1227         return 0;
1228 }
1229
1230 gint
1231 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1232 {
1233         if (ms == 0) {
1234                 MonoThreadInfo *info;
1235
1236                 mono_thread_info_yield ();
1237
1238                 info = mono_thread_info_current ();
1239                 if (info && mono_thread_info_is_interrupt_state (info))
1240                         return WAIT_IO_COMPLETION;
1241
1242                 return 0;
1243         }
1244
1245         if (alerted)
1246                 return sleep_interruptable (ms, alerted);
1247
1248         MONO_ENTER_GC_SAFE;
1249
1250         if (ms == MONO_INFINITE_WAIT) {
1251                 do {
1252 #ifdef HOST_WIN32
1253                         Sleep (G_MAXUINT32);
1254 #else
1255                         sleep (G_MAXUINT32);
1256 #endif
1257                 } while (1);
1258         } else {
1259                 int ret;
1260 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1261                 struct timespec start, target;
1262
1263                 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1264                 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1265                 g_assert (ret == 0);
1266
1267                 target = start;
1268                 target.tv_sec += ms / 1000;
1269                 target.tv_nsec += (ms % 1000) * 1000000;
1270                 if (target.tv_nsec > 999999999) {
1271                         target.tv_nsec -= 999999999;
1272                         target.tv_sec ++;
1273                 }
1274
1275                 do {
1276                         ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1277                 } while (ret != 0);
1278 #elif HOST_WIN32
1279                 Sleep (ms);
1280 #else
1281                 struct timespec req, rem;
1282
1283                 req.tv_sec = ms / 1000;
1284                 req.tv_nsec = (ms % 1000) * 1000000;
1285
1286                 do {
1287                         memset (&rem, 0, sizeof (rem));
1288                         ret = nanosleep (&req, &rem);
1289                 } while (ret != 0);
1290 #endif /* __linux__ */
1291         }
1292
1293         MONO_EXIT_GC_SAFE;
1294
1295         return 0;
1296 }
1297
1298 gint
1299 mono_thread_info_usleep (guint64 us)
1300 {
1301         MONO_ENTER_GC_SAFE;
1302         g_usleep (us);
1303         MONO_EXIT_GC_SAFE;
1304         return 0;
1305 }
1306
1307 gpointer
1308 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1309 {
1310         return ((MonoThreadInfo*)info)->tls [key];
1311 }
1312
1313 /*
1314  * mono_threads_info_tls_set:
1315  *
1316  *   Set the TLS key to VALUE in the info structure. This can be used to obtain
1317  * values of TLS variables for threads other than the current thread.
1318  * This should only be used for infrequently changing TLS variables, and it should
1319  * be paired with setting the real TLS variable since this provides no GC tracking.
1320  */
1321 void
1322 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1323 {
1324         ((MonoThreadInfo*)info)->tls [key] = value;
1325 }
1326
1327 /*
1328  * mono_thread_info_exit:
1329  *
1330  *   Exit the current thread.
1331  * This function doesn't return.
1332  */
1333 void
1334 mono_thread_info_exit (gsize exit_code)
1335 {
1336         mono_thread_info_detach ();
1337
1338         mono_threads_platform_exit (0);
1339 }
1340
1341 /*
1342  * mono_threads_open_thread_handle:
1343  *
1344  *  Duplicate the handle. The handle needs to be closed by calling
1345  *  mono_threads_close_thread_handle () when it is no longer needed.
1346  */
1347 MonoThreadHandle*
1348 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1349 {
1350         return mono_refcount_inc (thread_handle);
1351 }
1352
1353 void
1354 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1355 {
1356         mono_refcount_dec (thread_handle);
1357 }
1358
1359 static void
1360 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1361 {
1362         mono_os_event_set (&thread_handle->event);
1363 }
1364
1365 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1366
1367 struct _MonoThreadInfoInterruptToken {
1368         void (*callback) (gpointer data);
1369         gpointer data;
1370 };
1371
1372 /*
1373  * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1374  *
1375  *  - @callback: must be able to be called from another thread and always cancel the wait
1376  *  - @data: passed to the callback
1377  *  - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1378  *     if set to TRUE, it must mean that the thread is in interrupted state
1379  */
1380 void
1381 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1382 {
1383         MonoThreadInfo *info;
1384         MonoThreadInfoInterruptToken *previous_token, *token;
1385
1386         g_assert (callback);
1387
1388         g_assert (interrupted);
1389         *interrupted = FALSE;
1390
1391         info = mono_thread_info_current ();
1392         g_assert (info);
1393
1394         /* The memory of this token can be freed at 2 places:
1395          *  - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1396          *     by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1397          *  - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1398          *     functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1399         token = g_new0 (MonoThreadInfoInterruptToken, 1);
1400         token->callback = callback;
1401         token->data = data;
1402
1403         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1404
1405         if (previous_token) {
1406                 if (previous_token != INTERRUPT_STATE)
1407                         g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1408
1409                 g_free (token);
1410
1411                 *interrupted = TRUE;
1412         }
1413
1414         THREADS_INTERRUPT_DEBUG ("interrupt install    tid %p token %p previous_token %p interrupted %s\n",
1415                 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1416 }
1417
1418 void
1419 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1420 {
1421         MonoThreadInfo *info;
1422         MonoThreadInfoInterruptToken *previous_token;
1423
1424         g_assert (interrupted);
1425         *interrupted = FALSE;
1426
1427         info = mono_thread_info_current ();
1428         g_assert (info);
1429
1430         previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1431
1432         /* only the installer can uninstall the token */
1433         g_assert (previous_token);
1434
1435         if (previous_token == INTERRUPT_STATE) {
1436                 /* if it is interrupted, then it is going to be freed in finish interrupt */
1437                 *interrupted = TRUE;
1438         } else {
1439                 g_free (previous_token);
1440         }
1441
1442         THREADS_INTERRUPT_DEBUG ("interrupt uninstall  tid %p previous_token %p interrupted %s\n",
1443                 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1444 }
1445
1446 static MonoThreadInfoInterruptToken*
1447 set_interrupt_state (MonoThreadInfo *info)
1448 {
1449         MonoThreadInfoInterruptToken *token, *previous_token;
1450
1451         g_assert (info);
1452
1453         /* Atomically obtain the token the thread is
1454         * waiting on, and change it to a flag value. */
1455
1456         do {
1457                 previous_token = info->interrupt_token;
1458
1459                 /* Already interrupted */
1460                 if (previous_token == INTERRUPT_STATE) {
1461                         token = NULL;
1462                         break;
1463                 }
1464
1465                 token = previous_token;
1466         } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1467
1468         return token;
1469 }
1470
1471 /*
1472  * mono_thread_info_prepare_interrupt:
1473  *
1474  * The state of the thread info interrupt token is set to 'interrupted' which means that :
1475  *  - if the thread calls one of the WaitFor functions, the function will return with
1476  *     WAIT_IO_COMPLETION instead of waiting
1477  *  - if the thread was waiting when this function was called, the wait will be broken
1478  *
1479  * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1480  * didn't receive the interrupt signal yet, in this case it should call the wait function
1481  * again. This essentially means that the target thread will busy wait until it is ready to
1482  * process the interruption.
1483  */
1484 MonoThreadInfoInterruptToken*
1485 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1486 {
1487         MonoThreadInfoInterruptToken *token;
1488
1489         token = set_interrupt_state (info);
1490
1491         THREADS_INTERRUPT_DEBUG ("interrupt prepare    tid %p token %p\n",
1492                 mono_thread_info_get_tid (info), token);
1493
1494         return token;
1495 }
1496
1497 void
1498 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1499 {
1500         THREADS_INTERRUPT_DEBUG ("interrupt finish     token %p\n", token);
1501
1502         if (token == NULL)
1503                 return;
1504
1505         g_assert (token->callback);
1506
1507         token->callback (token->data);
1508
1509         g_free (token);
1510 }
1511
1512 void
1513 mono_thread_info_self_interrupt (void)
1514 {
1515         MonoThreadInfo *info;
1516         MonoThreadInfoInterruptToken *token;
1517
1518         info = mono_thread_info_current ();
1519         g_assert (info);
1520
1521         token = set_interrupt_state (info);
1522         g_assert (!token);
1523
1524         THREADS_INTERRUPT_DEBUG ("interrupt self       tid %p\n",
1525                 mono_thread_info_get_tid (info));
1526 }
1527
1528 /* Clear the interrupted flag of the current thread, set with
1529  * mono_thread_info_self_interrupt, so it can wait again */
1530 void
1531 mono_thread_info_clear_self_interrupt ()
1532 {
1533         MonoThreadInfo *info;
1534         MonoThreadInfoInterruptToken *previous_token;
1535
1536         info = mono_thread_info_current ();
1537         g_assert (info);
1538
1539         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1540         g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1541
1542         THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1543 }
1544
1545 gboolean
1546 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1547 {
1548         g_assert (info);
1549         return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1550 }
1551
1552 void
1553 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1554 {
1555         g_assert (info);
1556
1557         if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1558                 g_string_append_printf (text, "not waiting");
1559         else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1560                 g_string_append_printf (text, "interrupted state");
1561         else
1562                 g_string_append_printf (text, "waiting");
1563 }
1564
1565 gboolean
1566 mono_thread_info_is_current (MonoThreadInfo *info)
1567 {
1568         return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1569 }
1570
1571 MonoThreadInfoWaitRet
1572 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1573 {
1574         MonoOSEventWaitRet res;
1575
1576         res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1577         if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1578                 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1579         else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1580                 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1581         else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1582                 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1583         else
1584                 g_error ("%s: unknown res value %d", __func__, res);
1585 }
1586
1587 MonoThreadInfoWaitRet
1588 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1589 {
1590         MonoOSEventWaitRet res;
1591         MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1592         gint i;
1593
1594         g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1595         if (background_change_event)
1596                 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1597
1598         for (i = 0; i < nhandles; ++i)
1599                 thread_events [i] = &thread_handles [i]->event;
1600
1601         if (background_change_event)
1602                 thread_events [nhandles ++] = background_change_event;
1603
1604         res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1605         if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1606                 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1607         else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1608                 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1609         else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1610                 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1611         else
1612                 g_error ("%s: unknown res value %d", __func__, res);
1613 }
1614
1615 /*
1616  * mono_threads_join_mutex:
1617  *
1618  *   This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1619  * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1620  * The code inside the lock should not block.
1621  */
1622 void
1623 mono_threads_join_lock (void)
1624 {
1625 #ifdef TARGET_OSX
1626         mono_os_mutex_lock (&join_mutex);
1627 #endif
1628 }
1629
1630 void
1631 mono_threads_join_unlock (void)
1632 {
1633 #ifdef TARGET_OSX
1634         mono_os_mutex_unlock (&join_mutex);
1635 #endif
1636 }