[coop] Drop support for MONO_ENABLE_BLOCKING_TRANSITION env var
[mono-project.git] / mono / utils / mono-threads-coop.c
blob0418261389ba9ae5d7ed9dfc485a22e485c5f6ee
1 /**
2 * \file
3 * Coop threading
5 * Author:
6 * Rodrigo Kumpera (kumpera@gmail.com)
8 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
9 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <config.h>
14 /* enable pthread extensions */
15 #ifdef TARGET_MACH
16 #define _DARWIN_C_SOURCE
17 #endif
19 #include <mono/utils/mono-compiler.h>
20 #include <mono/utils/mono-threads.h>
21 #include <mono/utils/mono-tls.h>
22 #include <mono/utils/hazard-pointer.h>
23 #include <mono/utils/mono-memory-model.h>
24 #include <mono/utils/mono-mmap.h>
25 #include <mono/utils/atomic.h>
26 #include <mono/utils/mono-time.h>
27 #include <mono/utils/mono-counters.h>
28 #include <mono/utils/mono-threads-coop.h>
29 #include <mono/utils/mono-threads-api.h>
30 #include <mono/utils/checked-build.h>
31 #include <mono/utils/mono-threads-debug.h>
33 #ifdef TARGET_OSX
34 #include <mono/utils/mach-support.h>
35 #endif
37 #ifdef _MSC_VER
38 // TODO: Find MSVC replacement for __builtin_unwind_init
39 #define SAVE_REGS_ON_STACK g_assert_not_reached ();
40 #elif defined (HOST_WASM)
41 //TODO: figure out wasm stack scanning
42 #define SAVE_REGS_ON_STACK do {} while (0)
43 #else
44 #define SAVE_REGS_ON_STACK __builtin_unwind_init ();
45 #endif
47 volatile size_t mono_polling_required;
49 // FIXME: This would be more efficient if instead of instantiating the stack it just pushed a simple depth counter up and down,
50 // perhaps with a per-thread cookie in the high bits.
51 #ifdef ENABLE_CHECKED_BUILD_GC
53 // Maintains a single per-thread stack of ints, used to ensure nesting is not violated
54 static MonoNativeTlsKey coop_reset_count_stack_key;
56 static void
57 coop_tls_push (gpointer cookie)
59 GArray *stack;
61 stack = (GArray*)mono_native_tls_get_value (coop_reset_count_stack_key);
62 if (!stack) {
63 stack = g_array_new (FALSE, FALSE, sizeof(gpointer));
64 mono_native_tls_set_value (coop_reset_count_stack_key, stack);
67 g_array_append_val (stack, cookie);
70 static void
71 coop_tls_pop (gpointer received_cookie)
73 GArray *stack;
74 gpointer expected_cookie;
76 stack = (GArray*)mono_native_tls_get_value (coop_reset_count_stack_key);
77 if (!stack || 0 == stack->len)
78 mono_fatal_with_history ("Received cookie %p but found no stack at all\n", received_cookie);
80 expected_cookie = g_array_index (stack, gpointer, stack->len - 1);
81 stack->len --;
83 if (0 == stack->len) {
84 g_array_free (stack,TRUE);
85 mono_native_tls_set_value (coop_reset_count_stack_key, NULL);
88 if (expected_cookie != received_cookie)
89 mono_fatal_with_history ("Received cookie %p but expected %p\n", received_cookie, expected_cookie);
92 #endif
94 static void
95 check_info (MonoThreadInfo *info, const gchar *action, const gchar *state, const char *func)
97 if (!info)
98 g_error ("%s Cannot %s GC %s region if the thread is not attached", func, action, state);
99 if (!mono_thread_info_is_current (info))
100 g_error ("%s [%p] Cannot %s GC %s region on a different thread", func, mono_thread_info_get_tid (info), action, state);
101 if (!mono_thread_info_is_live (info))
102 g_error ("%s [%p] Cannot %s GC %s region if the thread is not live", func, mono_thread_info_get_tid (info), action, state);
105 static int coop_reset_blocking_count;
106 static int coop_try_blocking_count;
107 static int coop_do_blocking_count;
108 static int coop_do_polling_count;
109 static int coop_save_count;
111 static void
112 mono_threads_state_poll_with_info (MonoThreadInfo *info);
114 void
115 mono_threads_state_poll (void)
117 mono_threads_state_poll_with_info (mono_thread_info_current_unchecked ());
120 static void
121 mono_threads_state_poll_with_info (MonoThreadInfo *info)
123 g_assert (mono_threads_is_blocking_transition_enabled ());
125 ++coop_do_polling_count;
127 if (!info)
128 return;
130 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
132 /* Fast fail if no_safepoints is set */
133 g_assert (!(info->thread_state & THREAD_SUSPEND_NO_SAFEPOINTS_MASK));
135 /* Fast check for pending suspend requests */
136 if (!(info->thread_state & STATE_ASYNC_SUSPEND_REQUESTED))
137 return;
139 ++coop_save_count;
140 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
142 /* commit the saved state and notify others if needed */
143 switch (mono_threads_transition_state_poll (info)) {
144 case SelfSuspendResumed:
145 break;
146 case SelfSuspendNotifyAndWait:
147 mono_threads_notify_initiator_of_suspend (info);
148 mono_thread_info_wait_for_resume (info);
149 break;
152 if (info->async_target) {
153 info->async_target (info->user_data);
154 info->async_target = NULL;
155 info->user_data = NULL;
159 static volatile gpointer* dummy_global;
161 static MONO_NEVER_INLINE
162 void*
163 return_stack_ptr (gpointer *i)
165 dummy_global = i;
166 return i;
169 static void
170 copy_stack_data (MonoThreadInfo *info, MonoStackData *stackdata_begin)
172 MonoThreadUnwindState *state;
173 int stackdata_size;
174 gpointer dummy;
175 void* stackdata_end = return_stack_ptr (&dummy);
177 SAVE_REGS_ON_STACK;
179 state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
181 stackdata_size = (char*)mono_stackdata_get_stackpointer (stackdata_begin) - (char*)stackdata_end;
183 const char *function_name = mono_stackdata_get_function_name (stackdata_begin);
185 if (((gsize) stackdata_begin & (SIZEOF_VOID_P - 1)) != 0)
186 g_error ("%s stackdata_begin (%p) must be %d-byte aligned", function_name, stackdata_begin, SIZEOF_VOID_P);
187 if (((gsize) stackdata_end & (SIZEOF_VOID_P - 1)) != 0)
188 g_error ("%s stackdata_end (%p) must be %d-byte aligned", function_name, stackdata_end, SIZEOF_VOID_P);
190 if (stackdata_size <= 0)
191 g_error ("%s stackdata_size = %d, but must be > 0, stackdata_begin = %p, stackdata_end = %p", function_name, stackdata_size, stackdata_begin, stackdata_end);
193 g_byte_array_set_size (info->stackdata, stackdata_size);
194 state->gc_stackdata = info->stackdata->data;
195 memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
197 state->gc_stackdata_size = stackdata_size;
200 static gpointer
201 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata);
203 gpointer
204 mono_threads_enter_gc_safe_region_internal (MonoStackData *stackdata)
206 return mono_threads_enter_gc_safe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
209 gpointer
210 mono_threads_enter_gc_safe_region (gpointer *stackpointer)
212 MONO_STACKDATA (stackdata);
213 stackdata.stackpointer = stackpointer;
214 return mono_threads_enter_gc_safe_region_internal (&stackdata);
217 gpointer
218 mono_threads_enter_gc_safe_region_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
220 gpointer cookie;
222 if (!mono_threads_is_blocking_transition_enabled ())
223 return NULL;
225 cookie = mono_threads_enter_gc_safe_region_unbalanced_with_info (info, stackdata);
227 #ifdef ENABLE_CHECKED_BUILD_GC
228 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
229 coop_tls_push (cookie);
230 #endif
232 return cookie;
235 gpointer
236 mono_threads_enter_gc_safe_region_unbalanced_internal (MonoStackData *stackdata)
238 return mono_threads_enter_gc_safe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
241 gpointer
242 mono_threads_enter_gc_safe_region_unbalanced (gpointer *stackpointer)
244 MONO_STACKDATA (stackdata);
245 stackdata.stackpointer = stackpointer;
246 return mono_threads_enter_gc_safe_region_unbalanced_internal (&stackdata);
249 static gpointer
250 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
252 if (!mono_threads_is_blocking_transition_enabled ())
253 return NULL;
255 ++coop_do_blocking_count;
257 const char *function_name = mono_stackdata_get_function_name (stackdata);
259 check_info (info, "enter", "safe", function_name);
261 copy_stack_data (info, stackdata);
263 retry:
264 ++coop_save_count;
265 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
267 switch (mono_threads_transition_do_blocking (info, function_name)) {
268 case DoBlockingContinue:
269 break;
270 case DoBlockingPollAndRetry:
271 mono_threads_state_poll_with_info (info);
272 goto retry;
275 return info;
278 void
279 mono_threads_exit_gc_safe_region_internal (gpointer cookie, MonoStackData *stackdata)
281 if (!mono_threads_is_blocking_transition_enabled ())
282 return;
284 #ifdef ENABLE_CHECKED_BUILD_GC
285 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
286 coop_tls_pop (cookie);
287 #endif
289 mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, stackdata);
292 void
293 mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackpointer)
295 MONO_STACKDATA (stackdata);
296 stackdata.stackpointer = stackpointer;
297 mono_threads_exit_gc_safe_region_internal (cookie, &stackdata);
300 void
301 mono_threads_exit_gc_safe_region_unbalanced_internal (gpointer cookie, MonoStackData *stackdata)
303 MonoThreadInfo *info;
305 if (!mono_threads_is_blocking_transition_enabled ())
306 return;
308 info = (MonoThreadInfo *)cookie;
310 const char *function_name = mono_stackdata_get_function_name (stackdata);
312 check_info (info, "exit", "safe", function_name);
314 switch (mono_threads_transition_done_blocking (info, function_name)) {
315 case DoneBlockingOk:
316 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
317 break;
318 case DoneBlockingWait:
319 /* If full coop suspend, we're just waiting for the initiator
320 * to resume us. If hybrid suspend, we were either self
321 * suspended cooperatively from async_suspend_requested (same
322 * as full coop), or we were suspended preemptively while in
323 * blocking and we're waiting for two things: the suspend
324 * signal handler to run and notify the initiator and
325 * immediately return, and then for the resume. */
326 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
327 mono_thread_info_wait_for_resume (info);
328 break;
329 default:
330 g_error ("Unknown thread state");
333 if (info->async_target) {
334 info->async_target (info->user_data);
335 info->async_target = NULL;
336 info->user_data = NULL;
340 void
341 mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackpointer)
343 MONO_STACKDATA (stackdata);
344 stackdata.stackpointer = stackpointer;
345 mono_threads_exit_gc_safe_region_unbalanced_internal (cookie, &stackdata);
348 void
349 mono_threads_assert_gc_safe_region (void)
351 MONO_REQ_GC_SAFE_MODE;
354 gpointer
355 mono_threads_enter_gc_unsafe_region_internal (MonoStackData *stackdata)
357 return mono_threads_enter_gc_unsafe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
360 gpointer
361 mono_threads_enter_gc_unsafe_region (gpointer *stackpointer)
363 MONO_STACKDATA (stackdata);
364 stackdata.stackpointer = stackpointer;
365 return mono_threads_enter_gc_unsafe_region_internal (&stackdata);
368 gpointer
369 mono_threads_enter_gc_unsafe_region_with_info (THREAD_INFO_TYPE *info, MonoStackData *stackdata)
371 gpointer cookie;
373 if (!mono_threads_is_blocking_transition_enabled ())
374 return NULL;
376 cookie = mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, stackdata);
378 #ifdef ENABLE_CHECKED_BUILD_GC
379 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
380 coop_tls_push (cookie);
381 #endif
383 return cookie;
386 gpointer
387 mono_threads_enter_gc_unsafe_region_unbalanced_internal (MonoStackData *stackdata)
389 return mono_threads_enter_gc_unsafe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
392 gpointer
393 mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackpointer)
395 MONO_STACKDATA (stackdata);
396 stackdata.stackpointer = stackpointer;
397 return mono_threads_enter_gc_unsafe_region_unbalanced_internal (&stackdata);
400 gpointer
401 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (MonoThreadInfo *info, MonoStackData *stackdata)
403 if (!mono_threads_is_blocking_transition_enabled ())
404 return NULL;
406 ++coop_reset_blocking_count;
408 const char *function_name = mono_stackdata_get_function_name (stackdata);
410 check_info (info, "enter", "unsafe", function_name);
412 copy_stack_data (info, stackdata);
414 switch (mono_threads_transition_abort_blocking (info, function_name)) {
415 case AbortBlockingIgnore:
416 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
417 return NULL;
418 case AbortBlockingIgnoreAndPoll:
419 mono_threads_state_poll_with_info (info);
420 return NULL;
421 case AbortBlockingOk:
422 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
423 break;
424 case AbortBlockingWait:
425 /* If full coop suspend, we're just waiting for the initiator
426 * to resume us. If hybrid suspend, we were either self
427 * suspended cooperatively from async_suspend_requested (same
428 * as full coop), or we were suspended preemptively while in
429 * blocking and we're waiting for two things: the suspend
430 * signal handler to run and notify the initiator and
431 * immediately return, and then for the resume. */
432 mono_thread_info_wait_for_resume (info);
433 break;
434 default:
435 g_error ("Unknown thread state %s", function_name);
438 if (info->async_target) {
439 info->async_target (info->user_data);
440 info->async_target = NULL;
441 info->user_data = NULL;
444 return info;
447 gpointer
448 mono_threads_enter_gc_unsafe_region_cookie (void)
450 MonoThreadInfo *info;
452 g_assert (mono_threads_is_blocking_transition_enabled ());
454 info = mono_thread_info_current_unchecked ();
456 check_info (info, "enter (cookie)", "unsafe", "");
458 #ifdef ENABLE_CHECKED_BUILD_GC
459 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
460 coop_tls_push (info);
461 #endif
463 return info;
466 void
467 mono_threads_exit_gc_unsafe_region_internal (gpointer cookie, MonoStackData *stackdata)
469 if (!mono_threads_is_blocking_transition_enabled ())
470 return;
472 #ifdef ENABLE_CHECKED_BUILD_GC
473 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
474 coop_tls_pop (cookie);
475 #endif
477 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, stackdata);
480 void
481 mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackpointer)
483 MONO_STACKDATA (stackdata);
484 stackdata.stackpointer = stackpointer;
485 mono_threads_exit_gc_unsafe_region_internal (cookie, &stackdata);
488 void
489 mono_threads_exit_gc_unsafe_region_unbalanced_internal (gpointer cookie, MonoStackData *stackdata)
491 if (!mono_threads_is_blocking_transition_enabled ())
492 return;
494 if (!cookie)
495 return;
497 mono_threads_enter_gc_safe_region_unbalanced_internal (stackdata);
500 void
501 mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackpointer)
503 MONO_STACKDATA (stackdata);
504 stackdata.stackpointer = stackpointer;
505 mono_threads_exit_gc_unsafe_region_unbalanced_internal (cookie, &stackdata);
508 void
509 mono_threads_assert_gc_unsafe_region (void)
511 MONO_REQ_GC_UNSAFE_MODE;
514 static int
515 threads_suspend_policy_default (void)
517 #if defined (ENABLE_COOP_SUSPEND)
518 return MONO_THREADS_SUSPEND_FULL_COOP;
519 #elif defined (ENABLE_HYBRID_SUSPEND)
520 return MONO_THREADS_SUSPEND_HYBRID;
521 #else
522 return 0; /* unset */
523 #endif
526 /* Look up whether an env var is set, warn that it's obsolete and offer a new
527 * alternative
529 static gboolean
530 hasenv_obsolete (const char *name, const char* newval)
532 // If they already set MONO_THREADS_SUSPEND to something, maybe they're keeping
533 // the old var set for compatability with old Mono - in that case don't nag.
534 // FIXME: but maybe nag if MONO_THREADS_SUSPEND isn't set to "newval"?
535 static int quiet = -1;
536 if (g_hasenv (name)) {
537 if (G_UNLIKELY (quiet == -1))
538 quiet = g_hasenv ("MONO_THREADS_SUSPEND");
539 if (!quiet)
540 g_warning ("%s environment variable is obsolete. Use MONO_THREADS_SUSPEND=%s", name, newval);
541 return TRUE;
543 return FALSE;
546 static int
547 threads_suspend_policy_getenv_compat (void)
549 int policy = 0;
550 if (hasenv_obsolete ("MONO_ENABLE_COOP", "coop") || hasenv_obsolete ("MONO_ENABLE_COOP_SUSPEND", "coop")) {
551 g_assertf (!hasenv_obsolete ("MONO_ENABLE_HYBRID_SUSPEND", "hybrid"),
552 "Environment variables set to enable both hybrid and cooperative suspend simultaneously");
553 policy = MONO_THREADS_SUSPEND_FULL_COOP;
554 } else if (hasenv_obsolete ("MONO_ENABLE_HYBRID_SUSPEND", "hybrid"))
555 policy = MONO_THREADS_SUSPEND_HYBRID;
556 return policy;
559 static int
560 threads_suspend_policy_getenv (void)
562 int policy = 0;
563 if (g_hasenv ("MONO_THREADS_SUSPEND")) {
564 gchar *str = g_getenv ("MONO_THREADS_SUSPEND");
565 if (!strcmp (str, "coop"))
566 policy = MONO_THREADS_SUSPEND_FULL_COOP;
567 else if (!strcmp (str, "hybrid"))
568 policy = MONO_THREADS_SUSPEND_HYBRID;
569 else if (!strcmp (str, "preemptive"))
570 policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE;
571 else
572 g_error ("MONO_THREADS_SUSPEND environment variable set to '%s', must be one of coop, hybrid, preemptive.", str);
573 g_free (str);
575 return policy;
578 static char threads_suspend_policy;
580 MonoThreadsSuspendPolicy
581 mono_threads_suspend_policy (void)
583 int policy = threads_suspend_policy;
584 if (G_UNLIKELY (policy == 0)) {
585 // thread suspend policy:
586 // if the MONO_THREADS_SUSPEND env is set, use it.
587 // otherwise if there's a compiled-in default, use it.
588 // otherwise if one of the old environment variables is set, use that.
589 // otherwise use full preemptive suspend.
590 (policy = threads_suspend_policy_getenv ())
591 || (policy = threads_suspend_policy_default ())
592 || (policy = threads_suspend_policy_getenv_compat ())
593 || (policy = MONO_THREADS_SUSPEND_FULL_PREEMPTIVE);
594 g_assert (policy);
595 threads_suspend_policy = (char)policy;
597 return (MonoThreadsSuspendPolicy)policy;
600 static MonoThreadsSuspendPolicy
601 mono_threads_suspend_validate_policy (MonoThreadsSuspendPolicy policy)
603 switch (policy) {
604 case MONO_THREADS_SUSPEND_FULL_COOP:
605 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
606 case MONO_THREADS_SUSPEND_HYBRID:
607 return policy;
608 default:
609 g_error ("Invalid suspend policy %d.", (int)policy);
614 * mono_threads_suspend_override_policy:
616 * Don't use this. Provides a last resort escape hatch to override configure
617 * and environment settings and use the given thread suspend policy.
620 void
621 mono_threads_suspend_override_policy (MonoThreadsSuspendPolicy new_policy)
623 threads_suspend_policy = (char)mono_threads_suspend_validate_policy (new_policy);
624 g_warning ("Overriding suspend policy. Using %s suspend.", mono_threads_suspend_policy_name (mono_threads_suspend_policy ()));
627 const char*
628 mono_threads_suspend_policy_name (MonoThreadsSuspendPolicy policy)
630 switch (policy) {
631 case MONO_THREADS_SUSPEND_FULL_COOP:
632 return "cooperative";
633 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
634 return "preemptive";
635 case MONO_THREADS_SUSPEND_HYBRID:
636 return "hybrid";
637 default:
638 g_assert_not_reached ();
642 gboolean
643 mono_threads_suspend_policy_is_blocking_transition_enabled (MonoThreadsSuspendPolicy p)
645 switch (p) {
646 case MONO_THREADS_SUSPEND_FULL_COOP:
647 case MONO_THREADS_SUSPEND_HYBRID:
648 return TRUE;
649 case MONO_THREADS_SUSPEND_FULL_PREEMPTIVE:
650 return FALSE;
651 default:
652 g_assert_not_reached ();
656 gboolean
657 mono_threads_is_cooperative_suspension_enabled (void)
659 return (mono_threads_suspend_policy () == MONO_THREADS_SUSPEND_FULL_COOP);
662 gboolean
663 mono_threads_is_blocking_transition_enabled (void)
665 return mono_threads_suspend_policy_is_blocking_transition_enabled (mono_threads_suspend_policy ());
668 gboolean
669 mono_threads_is_hybrid_suspension_enabled (void)
671 return (mono_threads_suspend_policy () == MONO_THREADS_SUSPEND_HYBRID);
674 void
675 mono_threads_coop_init (void)
677 if (!mono_threads_are_safepoints_enabled () && !mono_threads_is_blocking_transition_enabled ())
678 return;
680 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
681 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
682 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
683 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
684 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
685 //See the above for what's wrong here.
687 #ifdef ENABLE_CHECKED_BUILD_GC
688 mono_native_tls_alloc (&coop_reset_count_stack_key, NULL);
689 #endif
692 void
693 mono_threads_coop_begin_global_suspend (void)
695 if (mono_threads_are_safepoints_enabled ())
696 mono_polling_required = 1;
699 void
700 mono_threads_coop_end_global_suspend (void)
702 if (mono_threads_are_safepoints_enabled ())
703 mono_polling_required = 0;
706 void
707 mono_threads_enter_no_safepoints_region (const char *func)
709 MONO_REQ_GC_UNSAFE_MODE;
710 mono_threads_transition_begin_no_safepoints (mono_thread_info_current (), func);
713 void
714 mono_threads_exit_no_safepoints_region (const char *func)
716 MONO_REQ_GC_UNSAFE_MODE;
717 mono_threads_transition_end_no_safepoints (mono_thread_info_current (), func);