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