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