[threads] Store MonoInternalThread in MonoThreadInfo for use when detaching (#5058)
[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         info->internal_thread_gchandle = G_MAXUINT32;
384
385         mono_threads_suspend_register (info);
386
387         THREADS_DEBUG ("registering info %p tid %p small id %x\n", info, mono_thread_info_get_tid (info), info->small_id);
388
389         if (threads_callbacks.thread_attach) {
390                 if (!threads_callbacks.thread_attach (info)) {
391                         // g_warning ("thread registation failed\n");
392                         mono_native_tls_set_value (thread_info_key, NULL);
393                         return FALSE;
394                 }
395         }
396
397         /*
398         Transition it before taking any locks or publishing itself to reduce the chance
399         of others witnessing a detached thread.
400         We can reasonably expect that until this thread gets published, no other thread will
401         try to manipulate it.
402         */
403         mono_threads_transition_attach (info);
404         mono_thread_info_suspend_lock ();
405         /*If this fail it means a given thread has been registered twice, which doesn't make sense. */
406         result = mono_thread_info_insert (info);
407         g_assert (result);
408         mono_thread_info_suspend_unlock ();
409
410         return TRUE;
411 }
412
413 static void
414 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info);
415
416 static void
417 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle);
418
419 static void
420 unregister_thread (void *arg)
421 {
422         gpointer gc_unsafe_stackdata;
423         MonoThreadInfo *info;
424         int small_id;
425         gboolean result;
426         gpointer handle;
427
428         info = (MonoThreadInfo *) arg;
429         g_assert (info);
430         g_assert (mono_thread_info_is_current (info));
431         g_assert (mono_thread_info_is_live (info));
432
433         /* Pump the HP queue while the thread is alive.*/
434         mono_thread_hazardous_try_free_some ();
435
436         small_id = info->small_id;
437
438         /* We only enter the GC unsafe region, as when exiting this function, the thread
439          * will be detached, and the current MonoThreadInfo* will be destroyed. */
440         mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, &gc_unsafe_stackdata);
441
442         THREADS_DEBUG ("unregistering info %p\n", info);
443
444         mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
445
446         /*
447          * TLS destruction order is not reliable so small_id might be cleaned up
448          * before us.
449          */
450 #ifndef HAVE_KW_THREAD
451         mono_native_tls_set_value (small_id_key, GUINT_TO_POINTER (info->small_id + 1));
452 #endif
453
454         /* we need to duplicate it, as the info->handle is going
455          * to be closed when unregistering from the platform */
456         handle = mono_threads_open_thread_handle (info->handle);
457
458         /*
459         First perform the callback that requires no locks.
460         This callback has the potential of taking other locks, so we do it before.
461         After it completes, the thread remains functional.
462         */
463         if (threads_callbacks.thread_detach)
464                 threads_callbacks.thread_detach (info);
465
466         mono_thread_info_suspend_lock_with_info (info);
467
468         /*
469         Now perform the callback that must be done under locks.
470         This will render the thread useless and non-suspendable, so it must
471         be done while holding the suspend lock to give no other thread chance
472         to suspend it.
473         */
474         if (threads_callbacks.thread_detach_with_lock)
475                 threads_callbacks.thread_detach_with_lock (info);
476
477         /* The thread is no longer active, so unref its handle */
478         mono_threads_close_thread_handle (info->handle);
479         info->handle = NULL;
480
481         result = mono_thread_info_remove (info);
482         g_assert (result);
483         mono_threads_transition_detach (info);
484
485         mono_thread_info_suspend_unlock ();
486
487         g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);
488
489         /*now it's safe to free the thread info.*/
490         mono_thread_hazardous_try_free (info, free_thread_info);
491
492         mono_thread_small_id_free (small_id);
493
494         mono_threads_signal_thread_handle (handle);
495
496         mono_threads_close_thread_handle (handle);
497
498         mono_native_tls_set_value (thread_info_key, NULL);
499 }
500
501 static void
502 thread_exited_dtor (void *arg)
503 {
504 #if defined(__MACH__)
505         /*
506          * Since we use pthread dtors to clean up thread data, if a thread
507          * is attached to the runtime by another pthread dtor after our dtor
508          * has ran, it will never be detached, leading to various problems
509          * since the thread ids etc. will be reused while they are still in
510          * the threads hashtables etc.
511          * Dtors are called in a loop until all user tls entries are 0,
512          * but the loop has a maximum count (4), so if we set the tls
513          * variable every time, it will remain set when system tls dtors
514          * are ran. This allows mono_thread_info_is_exiting () to detect
515          * whenever the thread is exiting, even if it is executed from a
516          * system tls dtor (i.e. obj-c dealloc methods).
517          */
518         mono_native_tls_set_value (thread_exited_key, GUINT_TO_POINTER (1));
519 #endif
520 }
521
522 MonoThreadInfo*
523 mono_thread_info_current_unchecked (void)
524 {
525         return mono_threads_inited ? (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key) : NULL;
526 }
527
528
529 MonoThreadInfo*
530 mono_thread_info_current (void)
531 {
532         MonoThreadInfo *info = (MonoThreadInfo*)mono_native_tls_get_value (thread_info_key);
533         if (info)
534                 return info;
535
536         info = mono_thread_info_lookup (mono_native_thread_id_get ()); /*info on HP1*/
537
538         /*
539         We might be called during thread cleanup, but we cannot be called after cleanup as happened.
540         The way to distinguish between before, during and after cleanup is the following:
541
542         -If the TLS key is set, cleanup has not begun;
543         -If the TLS key is clean, but the thread remains registered, cleanup is in progress;
544         -If the thread is nowhere to be found, cleanup has finished.
545
546         We cannot function after cleanup since there's no way to ensure what will happen.
547         */
548         g_assert (info);
549
550         /*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 */
551         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
552
553         return info;
554 }
555
556 int
557 mono_thread_info_get_small_id (void)
558 {
559 #ifdef HAVE_KW_THREAD
560         return tls_small_id;
561 #else
562         gpointer val = mono_native_tls_get_value (small_id_key);
563         if (!val)
564                 return -1;
565         return GPOINTER_TO_INT (val) - 1;
566 #endif
567 }
568
569 MonoLinkedListSet*
570 mono_thread_info_list_head (void)
571 {
572         return &thread_list;
573 }
574
575 /**
576  * mono_threads_attach_tools_thread
577  *
578  * Attach the current thread as a tool thread. DON'T USE THIS FUNCTION WITHOUT READING ALL DISCLAIMERS.
579  *
580  * A tools thread is a very special kind of thread that needs access to core runtime facilities but should
581  * not be counted as a regular thread for high order facilities such as executing managed code or accessing
582  * the managed heap.
583  *
584  * This is intended only to tools such as a profiler than needs to be able to use our lock-free support when
585  * doing things like resolving backtraces in their background processing thread.
586  */
587 void
588 mono_threads_attach_tools_thread (void)
589 {
590         MonoThreadInfo *info;
591
592         /* Must only be called once */
593         g_assert (!mono_native_tls_get_value (thread_info_key));
594         
595         while (!mono_threads_inited) { 
596                 mono_thread_info_usleep (10);
597         }
598
599         info = mono_thread_info_attach ();
600         g_assert (info);
601
602         info->tools_thread = TRUE;
603 }
604
605 MonoThreadInfo*
606 mono_thread_info_attach (void)
607 {
608         MonoThreadInfo *info;
609
610 #ifdef HOST_WIN32
611         if (!mono_threads_inited)
612         {
613                 /* This can happen from DllMain(DLL_THREAD_ATTACH) on Windows, if a
614                  * thread is created before an embedding API user initialized Mono. */
615                 THREADS_DEBUG ("mono_thread_info_attach called before mono_thread_info_init\n");
616                 return NULL;
617         }
618 #endif
619
620         g_assert (mono_threads_inited);
621
622         info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
623         if (!info) {
624                 info = (MonoThreadInfo *) g_malloc0 (thread_info_size);
625                 THREADS_DEBUG ("attaching %p\n", info);
626                 if (!register_thread (info)) {
627                         g_free (info);
628                         return NULL;
629                 }
630         }
631
632         return info;
633 }
634
635 void
636 mono_thread_info_detach (void)
637 {
638         MonoThreadInfo *info;
639
640 #ifdef HOST_WIN32
641         if (!mono_threads_inited)
642         {
643                 /* This can happen from DllMain(THREAD_DETACH) on Windows, if a thread
644                  * is created before an embedding API user initialized Mono. */
645                 THREADS_DEBUG ("mono_thread_info_detach called before mono_thread_info_init\n");
646                 return;
647         }
648 #endif
649
650         g_assert (mono_threads_inited);
651
652         info = (MonoThreadInfo *) mono_native_tls_get_value (thread_info_key);
653         if (info) {
654                 THREADS_DEBUG ("detaching %p\n", info);
655                 unregister_thread (info);
656         }
657 }
658
659 gboolean
660 mono_thread_info_try_get_internal_thread_gchandle (MonoThreadInfo *info, guint32 *gchandle)
661 {
662         g_assert (info);
663
664         if (info->internal_thread_gchandle == G_MAXUINT32)
665                 return FALSE;
666
667         *gchandle = info->internal_thread_gchandle;
668         return TRUE;
669 }
670
671 void
672 mono_thread_info_set_internal_thread_gchandle (MonoThreadInfo *info, guint32 gchandle)
673 {
674         g_assert (info);
675         g_assert (gchandle != G_MAXUINT32);
676         info->internal_thread_gchandle = gchandle;
677 }
678
679 void
680 mono_thread_info_unset_internal_thread_gchandle (THREAD_INFO_TYPE *info)
681 {
682         g_assert (info);
683         info->internal_thread_gchandle = G_MAXUINT32;
684 }
685
686 /*
687  * mono_thread_info_is_exiting:
688  *
689  *   Return whenever the current thread is exiting, i.e. it is running pthread
690  * dtors.
691  */
692 gboolean
693 mono_thread_info_is_exiting (void)
694 {
695 #if defined(__MACH__)
696         if (mono_native_tls_get_value (thread_exited_key) == GUINT_TO_POINTER (1))
697                 return TRUE;
698 #endif
699         return FALSE;
700 }
701
702 #ifndef HOST_WIN32
703 static void
704 thread_info_key_dtor (void *arg)
705 {
706         /* Put the MonoThreadInfo back for the duration of the
707          * unregister code.  In some circumstances the thread needs to
708          * take the GC lock which may block which requires a coop
709          * state transition. */
710         mono_native_tls_set_value (thread_info_key, arg);
711         unregister_thread (arg);
712         mono_native_tls_set_value (thread_info_key, NULL);
713 }
714 #endif
715
716 void
717 mono_thread_info_init (size_t info_size)
718 {
719         gboolean res;
720         thread_info_size = info_size;
721         char *sleepLimit;
722 #ifdef HOST_WIN32
723         res = mono_native_tls_alloc (&thread_info_key, NULL);
724         res = mono_native_tls_alloc (&thread_exited_key, NULL);
725 #else
726         res = mono_native_tls_alloc (&thread_info_key, (void *) thread_info_key_dtor);
727         res = mono_native_tls_alloc (&thread_exited_key, (void *) thread_exited_dtor);
728 #endif
729
730         g_assert (res);
731
732 #ifndef HAVE_KW_THREAD
733         res = mono_native_tls_alloc (&small_id_key, NULL);
734 #endif
735         g_assert (res);
736
737         if ((sleepLimit = g_getenv ("MONO_SLEEP_ABORT_LIMIT")) != NULL) {
738                 errno = 0;
739                 long threshold = strtol(sleepLimit, NULL, 10);
740                 if ((errno == 0) && (threshold >= 40))  {
741                         sleepAbortDuration = threshold;
742                         sleepWarnDuration = threshold / 20;
743                 } else
744                         g_warning("MONO_SLEEP_ABORT_LIMIT must be a number >= 40");
745                 g_free (sleepLimit);
746         }
747
748         mono_os_sem_init (&global_suspend_semaphore, 1);
749         mono_os_sem_init (&suspend_semaphore, 0);
750         mono_os_mutex_init (&join_mutex);
751
752         mono_lls_init (&thread_list, NULL);
753         mono_thread_smr_init ();
754         mono_threads_suspend_init ();
755         mono_threads_coop_init ();
756         mono_threads_platform_init ();
757
758 #if defined(__MACH__)
759         mono_mach_init (thread_info_key);
760 #endif
761
762         mono_threads_inited = TRUE;
763
764         g_assert (sizeof (MonoNativeThreadId) <= sizeof (uintptr_t));
765 }
766
767 void
768 mono_thread_info_callbacks_init (MonoThreadInfoCallbacks *callbacks)
769 {
770         threads_callbacks = *callbacks;
771 }
772
773 void
774 mono_thread_info_signals_init (void)
775 {
776         mono_threads_suspend_init_signals ();
777 }
778
779 void
780 mono_thread_info_runtime_init (MonoThreadInfoRuntimeCallbacks *callbacks)
781 {
782         runtime_callbacks = *callbacks;
783 }
784
785 MonoThreadInfoRuntimeCallbacks *
786 mono_threads_get_runtime_callbacks (void)
787 {
788         return &runtime_callbacks;
789 }
790
791 static gboolean
792 mono_thread_info_core_resume (MonoThreadInfo *info)
793 {
794         gboolean res = FALSE;
795
796         switch (mono_threads_transition_request_resume (info)) {
797         case ResumeError:
798                 res = FALSE;
799                 break;
800         case ResumeOk:
801                 res = TRUE;
802                 break;
803         case ResumeInitSelfResume:
804                 resume_self_suspended (info);
805                 res = TRUE;
806                 break;
807         case ResumeInitAsyncResume:
808                 resume_async_suspended (info);
809                 res = TRUE;
810                 break;
811         case ResumeInitBlockingResume:
812                 resume_blocking_suspended (info);
813                 res = TRUE;
814                 break;
815         }
816
817         return res;
818 }
819
820 gboolean
821 mono_thread_info_resume (MonoNativeThreadId tid)
822 {
823         gboolean result; /* don't initialize it so the compiler can catch unitilized paths. */
824         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
825         MonoThreadInfo *info;
826
827         THREADS_SUSPEND_DEBUG ("RESUMING tid %p\n", (void*)tid);
828
829         mono_thread_info_suspend_lock ();
830
831         info = mono_thread_info_lookup (tid); /*info on HP1*/
832         if (!info) {
833                 result = FALSE;
834                 goto cleanup;
835         }
836
837         result = mono_thread_info_core_resume (info);
838
839         //Wait for the pending resume to finish
840         mono_threads_wait_pending_operations ();
841
842 cleanup:
843         mono_thread_info_suspend_unlock ();
844         mono_hazard_pointer_clear (hp, 1);
845         return result;
846 }
847
848 gboolean
849 mono_thread_info_begin_suspend (MonoThreadInfo *info)
850 {
851         switch (mono_threads_transition_request_async_suspension (info)) {
852         case AsyncSuspendAlreadySuspended:
853         case AsyncSuspendBlocking:
854                 return TRUE;
855         case AsyncSuspendWait:
856                 mono_threads_add_to_pending_operation_set (info);
857                 return TRUE;
858         case AsyncSuspendInitSuspend:
859                 return begin_async_suspend (info, FALSE);
860         default:
861                 g_assert_not_reached ();
862         }
863 }
864
865 gboolean
866 mono_thread_info_begin_resume (MonoThreadInfo *info)
867 {
868         return mono_thread_info_core_resume (info);
869 }
870
871 /*
872 FIXME fix cardtable WB to be out of line and check with the runtime if the target is not the
873 WB trampoline. Another option is to encode wb ranges in MonoJitInfo, but that is somewhat hard.
874 */
875 static gboolean
876 is_thread_in_critical_region (MonoThreadInfo *info)
877 {
878         gpointer stack_start;
879         MonoThreadUnwindState *state;
880
881         if (mono_threads_platform_in_critical_region (mono_thread_info_get_tid (info)))
882                 return TRUE;
883
884         /* Are we inside a system critical region? */
885         if (info->inside_critical_region)
886                 return TRUE;
887
888         /* Are we inside a GC critical region? */
889         if (threads_callbacks.thread_in_critical_region && threads_callbacks.thread_in_critical_region (info)) {
890                 return TRUE;
891         }
892
893         /* The target thread might be shutting down and the domain might be null, which means no managed code left to run. */
894         state = mono_thread_info_get_suspend_state (info);
895         if (!state->unwind_data [MONO_UNWIND_DATA_DOMAIN])
896                 return FALSE;
897
898         stack_start = MONO_CONTEXT_GET_SP (&state->ctx);
899         /* altstack signal handler, sgen can't handle them, so we treat them as critical */
900         if (stack_start < info->stack_start_limit || stack_start >= info->stack_end)
901                 return TRUE;
902
903         if (threads_callbacks.ip_in_critical_region)
904                 return threads_callbacks.ip_in_critical_region ((MonoDomain *) state->unwind_data [MONO_UNWIND_DATA_DOMAIN], (char *) MONO_CONTEXT_GET_IP (&state->ctx));
905
906         return FALSE;
907 }
908
909 gboolean
910 mono_thread_info_in_critical_location (MonoThreadInfo *info)
911 {
912         return is_thread_in_critical_region (info);
913 }
914
915 /*
916 The return value is only valid until a matching mono_thread_info_resume is called
917 */
918 static MonoThreadInfo*
919 suspend_sync (MonoNativeThreadId tid, gboolean interrupt_kernel)
920 {
921         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
922         MonoThreadInfo *info = mono_thread_info_lookup (tid); /*info on HP1*/
923         if (!info)
924                 return NULL;
925
926         switch (mono_threads_transition_request_async_suspension (info)) {
927         case AsyncSuspendAlreadySuspended:
928                 mono_hazard_pointer_clear (hp, 1); //XXX this is questionable we got to clean the suspend/resume nonsense of critical sections
929                 return info;
930         case AsyncSuspendWait:
931                 mono_threads_add_to_pending_operation_set (info);
932                 break;
933         case AsyncSuspendInitSuspend:
934                 if (!begin_async_suspend (info, interrupt_kernel)) {
935                         mono_hazard_pointer_clear (hp, 1);
936                         return NULL;
937                 }
938                 break;
939         case AsyncSuspendBlocking:
940                 if (interrupt_kernel)
941                         mono_threads_suspend_abort_syscall (info);
942
943                 break;
944         default:
945                 g_assert_not_reached ();
946         }
947
948         //Wait for the pending suspend to finish
949         mono_threads_wait_pending_operations ();
950
951         if (!check_async_suspend (info)) {
952                 mono_thread_info_core_resume (info);
953                 mono_threads_wait_pending_operations ();
954                 mono_hazard_pointer_clear (hp, 1);
955                 return NULL;
956         }
957         return info;
958 }
959
960 static MonoThreadInfo*
961 suspend_sync_nolock (MonoNativeThreadId id, gboolean interrupt_kernel)
962 {
963         MonoThreadInfo *info = NULL;
964         int sleep_duration = 0;
965         for (;;) {
966                 if (!(info = suspend_sync (id, interrupt_kernel))) {
967                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
968                         return NULL;
969                 }
970
971                 /*WARNING: We now are in interrupt context until we resume the thread. */
972                 if (!is_thread_in_critical_region (info))
973                         break;
974
975                 if (!mono_thread_info_core_resume (info)) {
976                         mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
977                         return NULL;
978                 }
979                 THREADS_SUSPEND_DEBUG ("RESTARTED thread tid %p\n", (void*)id);
980
981                 /* Wait for the pending resume to finish */
982                 mono_threads_wait_pending_operations ();
983
984                 if (sleep_duration == 0)
985                         mono_thread_info_yield ();
986                 else
987                         g_usleep (sleep_duration);
988
989                 sleep_duration += 10;
990         }
991         return info;
992 }
993
994 void
995 mono_thread_info_safe_suspend_and_run (MonoNativeThreadId id, gboolean interrupt_kernel, MonoSuspendThreadCallback callback, gpointer user_data)
996 {
997         int result;
998         MonoThreadInfo *info = NULL;
999         MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
1000
1001         THREADS_SUSPEND_DEBUG ("SUSPENDING tid %p\n", (void*)id);
1002         /*FIXME: unify this with self-suspend*/
1003         g_assert (id != mono_native_thread_id_get ());
1004
1005         /* This can block during stw */
1006         mono_thread_info_suspend_lock ();
1007         mono_threads_begin_global_suspend ();
1008
1009         info = suspend_sync_nolock (id, interrupt_kernel);
1010         if (!info)
1011                 goto done;
1012
1013         switch (result = callback (info, user_data)) {
1014         case MonoResumeThread:
1015                 mono_hazard_pointer_set (hp, 1, info);
1016                 mono_thread_info_core_resume (info);
1017                 mono_threads_wait_pending_operations ();
1018                 break;
1019         case KeepSuspended:
1020                 g_assert (!mono_threads_is_coop_enabled ());
1021                 break;
1022         default:
1023                 g_error ("Invalid suspend_and_run callback return value %d", result);
1024         }
1025
1026 done:
1027         mono_hazard_pointer_clear (hp, 1);
1028         mono_threads_end_global_suspend ();
1029         mono_thread_info_suspend_unlock ();
1030 }
1031
1032 /**
1033 Inject an assynchronous call into the target thread. The target thread must be suspended and
1034 only a single async call can be setup for a given suspend cycle.
1035 This async call must cause stack unwinding as the current implementation doesn't save enough state
1036 to resume execution of the top-of-stack function. It's an acceptable limitation since this is
1037 currently used only to deliver exceptions.
1038 */
1039 void
1040 mono_thread_info_setup_async_call (MonoThreadInfo *info, void (*target_func)(void*), void *user_data)
1041 {
1042         if (!mono_threads_is_coop_enabled ()) {
1043                 /* In non-coop mode, an async call can only be setup on an async suspended thread, but in coop mode, a thread
1044                  * may be in blocking state, and will execute the async call when leaving the safepoint, leaving a gc safe
1045                  * region or entering a gc unsafe region */
1046                 g_assert (mono_thread_info_run_state (info) == STATE_ASYNC_SUSPENDED);
1047         }
1048         /*FIXME this is a bad assert, we probably should do proper locking and fail if one is already set*/
1049         g_assert (!info->async_target);
1050         info->async_target = target_func;
1051         /* This is not GC tracked */
1052         info->user_data = user_data;
1053 }
1054
1055 /*
1056 The suspend lock is held during any suspend in progress.
1057 A GC that has safepoints must take this lock as part of its
1058 STW to make sure no unsafe pending suspend is in progress.   
1059 */
1060
1061 static void
1062 mono_thread_info_suspend_lock_with_info (MonoThreadInfo *info)
1063 {
1064         if (mono_threads_is_coop_enabled ()) {
1065                 g_assert (info);
1066                 g_assert (mono_thread_info_is_current (info));
1067                 g_assert (mono_thread_info_is_live (info));
1068
1069                 MONO_ENTER_GC_SAFE_WITH_INFO(info);
1070
1071                 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1072                 g_assert (res != -1);
1073
1074                 MONO_EXIT_GC_SAFE_WITH_INFO;
1075         } else {
1076                 int res = mono_os_sem_wait (&global_suspend_semaphore, MONO_SEM_FLAGS_NONE);
1077                 g_assert (res != -1);
1078         }
1079 }
1080
1081 void
1082 mono_thread_info_suspend_lock (void)
1083 {
1084         mono_thread_info_suspend_lock_with_info (mono_thread_info_current_unchecked ());
1085 }
1086
1087 void
1088 mono_thread_info_suspend_unlock (void)
1089 {
1090         mono_os_sem_post (&global_suspend_semaphore);
1091 }
1092
1093 /*
1094  * This is a very specific function whose only purpose is to
1095  * break a given thread from socket syscalls.
1096  *
1097  * This only exists because linux won't fail a call to connect
1098  * if the underlying is closed.
1099  *
1100  * TODO We should cleanup and unify this with the other syscall abort
1101  * facility.
1102  */
1103 void
1104 mono_thread_info_abort_socket_syscall_for_close (MonoNativeThreadId tid)
1105 {
1106         MonoThreadHazardPointers *hp;
1107         MonoThreadInfo *info;
1108
1109         if (tid == mono_native_thread_id_get ())
1110                 return;
1111
1112         hp = mono_hazard_pointer_get ();
1113         info = mono_thread_info_lookup (tid);
1114         if (!info)
1115                 return;
1116
1117         if (mono_thread_info_run_state (info) == STATE_DETACHED) {
1118                 mono_hazard_pointer_clear (hp, 1);
1119                 return;
1120         }
1121
1122         mono_thread_info_suspend_lock ();
1123         mono_threads_begin_global_suspend ();
1124
1125         mono_threads_suspend_abort_syscall (info);
1126         mono_threads_wait_pending_operations ();
1127
1128         mono_hazard_pointer_clear (hp, 1);
1129
1130         mono_threads_end_global_suspend ();
1131         mono_thread_info_suspend_unlock ();
1132 }
1133
1134 /*
1135  * mono_thread_info_set_is_async_context:
1136  *
1137  *   Set whenever the current thread is in an async context. Some runtime functions might behave
1138  * differently while in an async context in order to be async safe.
1139  */
1140 void
1141 mono_thread_info_set_is_async_context (gboolean async_context)
1142 {
1143         MonoThreadInfo *info = mono_thread_info_current ();
1144
1145         if (info)
1146                 info->is_async_context = async_context;
1147 }
1148
1149 gboolean
1150 mono_thread_info_is_async_context (void)
1151 {
1152         MonoThreadInfo *info = mono_thread_info_current ();
1153
1154         if (info)
1155                 return info->is_async_context;
1156         else
1157                 return FALSE;
1158 }
1159
1160 /*
1161  * mono_thread_info_get_stack_bounds:
1162  *
1163  *   Return the address and size of the current threads stack. Return NULL as the 
1164  * stack address if the stack address cannot be determined.
1165  */
1166 void
1167 mono_thread_info_get_stack_bounds (guint8 **staddr, size_t *stsize)
1168 {
1169         guint8 *current = (guint8 *)&stsize;
1170         mono_threads_platform_get_stack_bounds (staddr, stsize);
1171         if (!*staddr)
1172                 return;
1173
1174         /* Sanity check the result */
1175         g_assert ((current > *staddr) && (current < *staddr + *stsize));
1176
1177         /* When running under emacs, sometimes staddr is not aligned to a page size */
1178         *staddr = (guint8*)((gssize)*staddr & ~(mono_pagesize () - 1));
1179 }
1180
1181 gboolean
1182 mono_thread_info_yield (void)
1183 {
1184         return mono_threads_platform_yield ();
1185 }
1186
1187 static mono_lazy_init_t sleep_init = MONO_LAZY_INIT_STATUS_NOT_INITIALIZED;
1188 static MonoCoopMutex sleep_mutex;
1189 static MonoCoopCond sleep_cond;
1190
1191 static void
1192 sleep_initialize (void)
1193 {
1194         mono_coop_mutex_init (&sleep_mutex);
1195         mono_coop_cond_init (&sleep_cond);
1196 }
1197
1198 static void
1199 sleep_interrupt (gpointer data)
1200 {
1201         mono_coop_mutex_lock (&sleep_mutex);
1202         mono_coop_cond_broadcast (&sleep_cond);
1203         mono_coop_mutex_unlock (&sleep_mutex);
1204 }
1205
1206 static inline guint32
1207 sleep_interruptable (guint32 ms, gboolean *alerted)
1208 {
1209         gint64 now, end;
1210
1211         g_assert (MONO_INFINITE_WAIT == G_MAXUINT32);
1212
1213         g_assert (alerted);
1214         *alerted = FALSE;
1215
1216         if (ms != MONO_INFINITE_WAIT)
1217                 end = mono_msec_ticks() + ms;
1218
1219         mono_lazy_initialize (&sleep_init, sleep_initialize);
1220
1221         mono_coop_mutex_lock (&sleep_mutex);
1222
1223         for (;;) {
1224                 if (ms != MONO_INFINITE_WAIT) {
1225                         now = mono_msec_ticks();
1226                         if (now >= end)
1227                                 break;
1228                 }
1229
1230                 mono_thread_info_install_interrupt (sleep_interrupt, NULL, alerted);
1231                 if (*alerted) {
1232                         mono_coop_mutex_unlock (&sleep_mutex);
1233                         return WAIT_IO_COMPLETION;
1234                 }
1235
1236                 if (ms != MONO_INFINITE_WAIT)
1237                         mono_coop_cond_timedwait (&sleep_cond, &sleep_mutex, end - now);
1238                 else
1239                         mono_coop_cond_wait (&sleep_cond, &sleep_mutex);
1240
1241                 mono_thread_info_uninstall_interrupt (alerted);
1242                 if (*alerted) {
1243                         mono_coop_mutex_unlock (&sleep_mutex);
1244                         return WAIT_IO_COMPLETION;
1245                 }
1246         }
1247
1248         mono_coop_mutex_unlock (&sleep_mutex);
1249
1250         return 0;
1251 }
1252
1253 gint
1254 mono_thread_info_sleep (guint32 ms, gboolean *alerted)
1255 {
1256         if (ms == 0) {
1257                 MonoThreadInfo *info;
1258
1259                 mono_thread_info_yield ();
1260
1261                 info = mono_thread_info_current ();
1262                 if (info && mono_thread_info_is_interrupt_state (info))
1263                         return WAIT_IO_COMPLETION;
1264
1265                 return 0;
1266         }
1267
1268         if (alerted)
1269                 return sleep_interruptable (ms, alerted);
1270
1271         MONO_ENTER_GC_SAFE;
1272
1273         if (ms == MONO_INFINITE_WAIT) {
1274                 do {
1275 #ifdef HOST_WIN32
1276                         Sleep (G_MAXUINT32);
1277 #else
1278                         sleep (G_MAXUINT32);
1279 #endif
1280                 } while (1);
1281         } else {
1282                 int ret;
1283 #if defined (__linux__) && !defined(PLATFORM_ANDROID)
1284                 struct timespec start, target;
1285
1286                 /* Use clock_nanosleep () to prevent time drifting problems when nanosleep () is interrupted by signals */
1287                 ret = clock_gettime (CLOCK_MONOTONIC, &start);
1288                 g_assert (ret == 0);
1289
1290                 target = start;
1291                 target.tv_sec += ms / 1000;
1292                 target.tv_nsec += (ms % 1000) * 1000000;
1293                 if (target.tv_nsec > 999999999) {
1294                         target.tv_nsec -= 999999999;
1295                         target.tv_sec ++;
1296                 }
1297
1298                 do {
1299                         ret = clock_nanosleep (CLOCK_MONOTONIC, TIMER_ABSTIME, &target, NULL);
1300                 } while (ret != 0);
1301 #elif HOST_WIN32
1302                 Sleep (ms);
1303 #else
1304                 struct timespec req, rem;
1305
1306                 req.tv_sec = ms / 1000;
1307                 req.tv_nsec = (ms % 1000) * 1000000;
1308
1309                 do {
1310                         memset (&rem, 0, sizeof (rem));
1311                         ret = nanosleep (&req, &rem);
1312                 } while (ret != 0);
1313 #endif /* __linux__ */
1314         }
1315
1316         MONO_EXIT_GC_SAFE;
1317
1318         return 0;
1319 }
1320
1321 gint
1322 mono_thread_info_usleep (guint64 us)
1323 {
1324         MONO_ENTER_GC_SAFE;
1325         g_usleep (us);
1326         MONO_EXIT_GC_SAFE;
1327         return 0;
1328 }
1329
1330 gpointer
1331 mono_thread_info_tls_get (THREAD_INFO_TYPE *info, MonoTlsKey key)
1332 {
1333         return ((MonoThreadInfo*)info)->tls [key];
1334 }
1335
1336 /*
1337  * mono_threads_info_tls_set:
1338  *
1339  *   Set the TLS key to VALUE in the info structure. This can be used to obtain
1340  * values of TLS variables for threads other than the current thread.
1341  * This should only be used for infrequently changing TLS variables, and it should
1342  * be paired with setting the real TLS variable since this provides no GC tracking.
1343  */
1344 void
1345 mono_thread_info_tls_set (THREAD_INFO_TYPE *info, MonoTlsKey key, gpointer value)
1346 {
1347         ((MonoThreadInfo*)info)->tls [key] = value;
1348 }
1349
1350 /*
1351  * mono_thread_info_exit:
1352  *
1353  *   Exit the current thread.
1354  * This function doesn't return.
1355  */
1356 void
1357 mono_thread_info_exit (gsize exit_code)
1358 {
1359         mono_thread_info_detach ();
1360
1361         mono_threads_platform_exit (0);
1362 }
1363
1364 /*
1365  * mono_threads_open_thread_handle:
1366  *
1367  *  Duplicate the handle. The handle needs to be closed by calling
1368  *  mono_threads_close_thread_handle () when it is no longer needed.
1369  */
1370 MonoThreadHandle*
1371 mono_threads_open_thread_handle (MonoThreadHandle *thread_handle)
1372 {
1373         return mono_refcount_inc (thread_handle);
1374 }
1375
1376 void
1377 mono_threads_close_thread_handle (MonoThreadHandle *thread_handle)
1378 {
1379         mono_refcount_dec (thread_handle);
1380 }
1381
1382 static void
1383 mono_threads_signal_thread_handle (MonoThreadHandle* thread_handle)
1384 {
1385         mono_os_event_set (&thread_handle->event);
1386 }
1387
1388 #define INTERRUPT_STATE ((MonoThreadInfoInterruptToken*) (size_t) -1)
1389
1390 struct _MonoThreadInfoInterruptToken {
1391         void (*callback) (gpointer data);
1392         gpointer data;
1393 };
1394
1395 /*
1396  * mono_thread_info_install_interrupt: install an interruption token for the current thread.
1397  *
1398  *  - @callback: must be able to be called from another thread and always cancel the wait
1399  *  - @data: passed to the callback
1400  *  - @interrupted: will be set to TRUE if a token is already installed, FALSE otherwise
1401  *     if set to TRUE, it must mean that the thread is in interrupted state
1402  */
1403 void
1404 mono_thread_info_install_interrupt (void (*callback) (gpointer data), gpointer data, gboolean *interrupted)
1405 {
1406         MonoThreadInfo *info;
1407         MonoThreadInfoInterruptToken *previous_token, *token;
1408
1409         g_assert (callback);
1410
1411         g_assert (interrupted);
1412         *interrupted = FALSE;
1413
1414         info = mono_thread_info_current ();
1415         g_assert (info);
1416
1417         /* The memory of this token can be freed at 2 places:
1418          *  - if the token is not interrupted: it will be freed in uninstall, as info->interrupt_token has not been replaced
1419          *     by the INTERRUPT_STATE flag value, and it still contains the pointer to the memory location
1420          *  - if the token is interrupted: it will be freed in finish, as the token is now owned by the prepare/finish
1421          *     functions, and info->interrupt_token does not contains a pointer to the memory anymore */
1422         token = g_new0 (MonoThreadInfoInterruptToken, 1);
1423         token->callback = callback;
1424         token->data = data;
1425
1426         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, token, NULL);
1427
1428         if (previous_token) {
1429                 if (previous_token != INTERRUPT_STATE)
1430                         g_error ("mono_thread_info_install_interrupt: previous_token should be INTERRUPT_STATE (%p), but it was %p", INTERRUPT_STATE, previous_token);
1431
1432                 g_free (token);
1433
1434                 *interrupted = TRUE;
1435         }
1436
1437         THREADS_INTERRUPT_DEBUG ("interrupt install    tid %p token %p previous_token %p interrupted %s\n",
1438                 mono_thread_info_get_tid (info), token, previous_token, *interrupted ? "TRUE" : "FALSE");
1439 }
1440
1441 void
1442 mono_thread_info_uninstall_interrupt (gboolean *interrupted)
1443 {
1444         MonoThreadInfo *info;
1445         MonoThreadInfoInterruptToken *previous_token;
1446
1447         g_assert (interrupted);
1448         *interrupted = FALSE;
1449
1450         info = mono_thread_info_current ();
1451         g_assert (info);
1452
1453         previous_token = (MonoThreadInfoInterruptToken *)InterlockedExchangePointer ((gpointer*) &info->interrupt_token, NULL);
1454
1455         /* only the installer can uninstall the token */
1456         g_assert (previous_token);
1457
1458         if (previous_token == INTERRUPT_STATE) {
1459                 /* if it is interrupted, then it is going to be freed in finish interrupt */
1460                 *interrupted = TRUE;
1461         } else {
1462                 g_free (previous_token);
1463         }
1464
1465         THREADS_INTERRUPT_DEBUG ("interrupt uninstall  tid %p previous_token %p interrupted %s\n",
1466                 mono_thread_info_get_tid (info), previous_token, *interrupted ? "TRUE" : "FALSE");
1467 }
1468
1469 static MonoThreadInfoInterruptToken*
1470 set_interrupt_state (MonoThreadInfo *info)
1471 {
1472         MonoThreadInfoInterruptToken *token, *previous_token;
1473
1474         g_assert (info);
1475
1476         /* Atomically obtain the token the thread is
1477         * waiting on, and change it to a flag value. */
1478
1479         do {
1480                 previous_token = info->interrupt_token;
1481
1482                 /* Already interrupted */
1483                 if (previous_token == INTERRUPT_STATE) {
1484                         token = NULL;
1485                         break;
1486                 }
1487
1488                 token = previous_token;
1489         } while (InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, INTERRUPT_STATE, previous_token) != previous_token);
1490
1491         return token;
1492 }
1493
1494 /*
1495  * mono_thread_info_prepare_interrupt:
1496  *
1497  * The state of the thread info interrupt token is set to 'interrupted' which means that :
1498  *  - if the thread calls one of the WaitFor functions, the function will return with
1499  *     WAIT_IO_COMPLETION instead of waiting
1500  *  - if the thread was waiting when this function was called, the wait will be broken
1501  *
1502  * It is possible that the wait functions return WAIT_IO_COMPLETION, but the target thread
1503  * didn't receive the interrupt signal yet, in this case it should call the wait function
1504  * again. This essentially means that the target thread will busy wait until it is ready to
1505  * process the interruption.
1506  */
1507 MonoThreadInfoInterruptToken*
1508 mono_thread_info_prepare_interrupt (MonoThreadInfo *info)
1509 {
1510         MonoThreadInfoInterruptToken *token;
1511
1512         token = set_interrupt_state (info);
1513
1514         THREADS_INTERRUPT_DEBUG ("interrupt prepare    tid %p token %p\n",
1515                 mono_thread_info_get_tid (info), token);
1516
1517         return token;
1518 }
1519
1520 void
1521 mono_thread_info_finish_interrupt (MonoThreadInfoInterruptToken *token)
1522 {
1523         THREADS_INTERRUPT_DEBUG ("interrupt finish     token %p\n", token);
1524
1525         if (token == NULL)
1526                 return;
1527
1528         g_assert (token->callback);
1529
1530         token->callback (token->data);
1531
1532         g_free (token);
1533 }
1534
1535 void
1536 mono_thread_info_self_interrupt (void)
1537 {
1538         MonoThreadInfo *info;
1539         MonoThreadInfoInterruptToken *token;
1540
1541         info = mono_thread_info_current ();
1542         g_assert (info);
1543
1544         token = set_interrupt_state (info);
1545         g_assert (!token);
1546
1547         THREADS_INTERRUPT_DEBUG ("interrupt self       tid %p\n",
1548                 mono_thread_info_get_tid (info));
1549 }
1550
1551 /* Clear the interrupted flag of the current thread, set with
1552  * mono_thread_info_self_interrupt, so it can wait again */
1553 void
1554 mono_thread_info_clear_self_interrupt ()
1555 {
1556         MonoThreadInfo *info;
1557         MonoThreadInfoInterruptToken *previous_token;
1558
1559         info = mono_thread_info_current ();
1560         g_assert (info);
1561
1562         previous_token = (MonoThreadInfoInterruptToken *)InterlockedCompareExchangePointer ((gpointer*) &info->interrupt_token, NULL, INTERRUPT_STATE);
1563         g_assert (previous_token == NULL || previous_token == INTERRUPT_STATE);
1564
1565         THREADS_INTERRUPT_DEBUG ("interrupt clear self tid %p previous_token %p\n", mono_thread_info_get_tid (info), previous_token);
1566 }
1567
1568 gboolean
1569 mono_thread_info_is_interrupt_state (MonoThreadInfo *info)
1570 {
1571         g_assert (info);
1572         return InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE;
1573 }
1574
1575 void
1576 mono_thread_info_describe_interrupt_token (MonoThreadInfo *info, GString *text)
1577 {
1578         g_assert (info);
1579
1580         if (!InterlockedReadPointer ((gpointer*) &info->interrupt_token))
1581                 g_string_append_printf (text, "not waiting");
1582         else if (InterlockedReadPointer ((gpointer*) &info->interrupt_token) == INTERRUPT_STATE)
1583                 g_string_append_printf (text, "interrupted state");
1584         else
1585                 g_string_append_printf (text, "waiting");
1586 }
1587
1588 gboolean
1589 mono_thread_info_is_current (MonoThreadInfo *info)
1590 {
1591         return mono_thread_info_get_tid (info) == mono_native_thread_id_get ();
1592 }
1593
1594 MonoThreadInfoWaitRet
1595 mono_thread_info_wait_one_handle (MonoThreadHandle *thread_handle, guint32 timeout, gboolean alertable)
1596 {
1597         MonoOSEventWaitRet res;
1598
1599         res = mono_os_event_wait_one (&thread_handle->event, timeout, alertable);
1600         if (res == MONO_OS_EVENT_WAIT_RET_SUCCESS_0)
1601                 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0;
1602         else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1603                 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1604         else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1605                 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1606         else
1607                 g_error ("%s: unknown res value %d", __func__, res);
1608 }
1609
1610 MonoThreadInfoWaitRet
1611 mono_thread_info_wait_multiple_handle (MonoThreadHandle **thread_handles, gsize nhandles, MonoOSEvent *background_change_event, gboolean waitall, guint32 timeout, gboolean alertable)
1612 {
1613         MonoOSEventWaitRet res;
1614         MonoOSEvent *thread_events [MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS];
1615         gint i;
1616
1617         g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS);
1618         if (background_change_event)
1619                 g_assert (nhandles <= MONO_OS_EVENT_WAIT_MAXIMUM_OBJECTS - 1);
1620
1621         for (i = 0; i < nhandles; ++i)
1622                 thread_events [i] = &thread_handles [i]->event;
1623
1624         if (background_change_event)
1625                 thread_events [nhandles ++] = background_change_event;
1626
1627         res = mono_os_event_wait_multiple (thread_events, nhandles, waitall, timeout, alertable);
1628         if (res >= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 && res <= MONO_OS_EVENT_WAIT_RET_SUCCESS_0 + nhandles - 1)
1629                 return MONO_THREAD_INFO_WAIT_RET_SUCCESS_0 + (res - MONO_OS_EVENT_WAIT_RET_SUCCESS_0);
1630         else if (res == MONO_OS_EVENT_WAIT_RET_ALERTED)
1631                 return MONO_THREAD_INFO_WAIT_RET_ALERTED;
1632         else if (res == MONO_OS_EVENT_WAIT_RET_TIMEOUT)
1633                 return MONO_THREAD_INFO_WAIT_RET_TIMEOUT;
1634         else
1635                 g_error ("%s: unknown res value %d", __func__, res);
1636 }
1637
1638 /*
1639  * mono_threads_join_mutex:
1640  *
1641  *   This mutex is used to avoid races between pthread_create () and pthread_join () on osx, see
1642  * https://bugzilla.xamarin.com/show_bug.cgi?id=50529
1643  * The code inside the lock should not block.
1644  */
1645 void
1646 mono_threads_join_lock (void)
1647 {
1648 #ifdef TARGET_OSX
1649         mono_os_mutex_lock (&join_mutex);
1650 #endif
1651 }
1652
1653 void
1654 mono_threads_join_unlock (void)
1655 {
1656 #ifdef TARGET_OSX
1657         mono_os_mutex_unlock (&join_mutex);
1658 #endif
1659 }