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