[tools] Add nuget-hash-extractor tool to help produce the runtime ignored assemblies...
[mono-project.git] / mono / utils / mono-threads-coop.c
blob7f2748536830157d0179423a510f6c7e21e9b6da
1 /*
2 * mono-threads.c: Coop threading
4 * Author:
5 * Rodrigo Kumpera (kumpera@gmail.com)
7 * Copyright 2015 Xamarin, Inc (http://www.xamarin.com)
8 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
9 */
11 #include <config.h>
13 /* enable pthread extensions */
14 #ifdef TARGET_MACH
15 #define _DARWIN_C_SOURCE
16 #endif
18 #include <mono/utils/mono-compiler.h>
19 #include <mono/utils/mono-threads.h>
20 #include <mono/utils/mono-tls.h>
21 #include <mono/utils/hazard-pointer.h>
22 #include <mono/utils/mono-memory-model.h>
23 #include <mono/utils/mono-mmap.h>
24 #include <mono/utils/atomic.h>
25 #include <mono/utils/mono-time.h>
26 #include <mono/utils/mono-counters.h>
27 #include <mono/utils/mono-threads-coop.h>
28 #include <mono/utils/mono-threads-api.h>
29 #include <mono/utils/checked-build.h>
30 #include <mono/utils/mono-threads-debug.h>
32 #ifdef TARGET_OSX
33 #include <mono/utils/mach-support.h>
34 #endif
36 #ifdef _MSC_VER
37 // TODO: Find MSVC replacement for __builtin_unwind_init
38 #define SAVE_REGS_ON_STACK g_assert_not_reached ();
39 #else
40 #define SAVE_REGS_ON_STACK __builtin_unwind_init ();
41 #endif
43 volatile size_t mono_polling_required;
45 // FIXME: This would be more efficient if instead of instantiating the stack it just pushed a simple depth counter up and down,
46 // perhaps with a per-thread cookie in the high bits.
47 #ifdef ENABLE_CHECKED_BUILD_GC
49 // Maintains a single per-thread stack of ints, used to ensure nesting is not violated
50 static MonoNativeTlsKey coop_reset_count_stack_key;
52 static void
53 coop_tls_push (gpointer cookie)
55 GArray *stack;
57 stack = mono_native_tls_get_value (coop_reset_count_stack_key);
58 if (!stack) {
59 stack = g_array_new (FALSE, FALSE, sizeof(gpointer));
60 mono_native_tls_set_value (coop_reset_count_stack_key, stack);
63 g_array_append_val (stack, cookie);
66 static void
67 coop_tls_pop (gpointer received_cookie)
69 GArray *stack;
70 gpointer expected_cookie;
72 stack = mono_native_tls_get_value (coop_reset_count_stack_key);
73 if (!stack || 0 == stack->len)
74 mono_fatal_with_history ("Received cookie %p but found no stack at all\n", received_cookie);
76 expected_cookie = g_array_index (stack, gpointer, stack->len - 1);
77 stack->len --;
79 if (0 == stack->len) {
80 g_array_free (stack,TRUE);
81 mono_native_tls_set_value (coop_reset_count_stack_key, NULL);
84 if (expected_cookie != received_cookie)
85 mono_fatal_with_history ("Received cookie %p but expected %p\n", received_cookie, expected_cookie);
88 #endif
90 static void
91 check_info (MonoThreadInfo *info, const gchar *action, const gchar *state)
93 if (!info)
94 g_error ("Cannot %s GC %s region if the thread is not attached", action, state);
95 if (!mono_thread_info_is_current (info))
96 g_error ("[%p] Cannot %s GC %s region on a different thread", mono_thread_info_get_tid (info), action, state);
97 if (!mono_thread_info_is_live (info))
98 g_error ("[%p] Cannot %s GC %s region if the thread is not live", mono_thread_info_get_tid (info), action, state);
101 static int coop_reset_blocking_count;
102 static int coop_try_blocking_count;
103 static int coop_do_blocking_count;
104 static int coop_do_polling_count;
105 static int coop_save_count;
107 static void
108 mono_threads_state_poll_with_info (MonoThreadInfo *info);
110 void
111 mono_threads_state_poll (void)
113 mono_threads_state_poll_with_info (mono_thread_info_current_unchecked ());
116 static void
117 mono_threads_state_poll_with_info (MonoThreadInfo *info)
119 g_assert (mono_threads_is_coop_enabled ());
121 ++coop_do_polling_count;
123 if (!info)
124 return;
126 THREADS_SUSPEND_DEBUG ("FINISH SELF SUSPEND OF %p\n", mono_thread_info_get_tid (info));
128 /* Fast check for pending suspend requests */
129 if (!(info->thread_state & (STATE_ASYNC_SUSPEND_REQUESTED | STATE_SELF_SUSPEND_REQUESTED)))
130 return;
132 ++coop_save_count;
133 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
135 /* commit the saved state and notify others if needed */
136 switch (mono_threads_transition_state_poll (info)) {
137 case SelfSuspendResumed:
138 break;
139 case SelfSuspendWait:
140 mono_thread_info_wait_for_resume (info);
141 break;
142 case SelfSuspendNotifyAndWait:
143 mono_threads_notify_initiator_of_suspend (info);
144 mono_thread_info_wait_for_resume (info);
145 break;
148 if (info->async_target) {
149 info->async_target (info->user_data);
150 info->async_target = NULL;
151 info->user_data = NULL;
155 static volatile gpointer* dummy_global;
157 static MONO_NEVER_INLINE
158 void*
159 return_stack_ptr (gpointer *i)
161 dummy_global = i;
162 return i;
165 static void
166 copy_stack_data (MonoThreadInfo *info, gpointer *stackdata_begin)
168 MonoThreadUnwindState *state;
169 int stackdata_size;
170 gpointer dummy;
171 void* stackdata_end = return_stack_ptr (&dummy);
173 SAVE_REGS_ON_STACK;
175 state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];
177 stackdata_size = (char*)stackdata_begin - (char*)stackdata_end;
179 if (((gsize) stackdata_begin & (SIZEOF_VOID_P - 1)) != 0)
180 g_error ("stackdata_begin (%p) must be %d-byte aligned", stackdata_begin, SIZEOF_VOID_P);
181 if (((gsize) stackdata_end & (SIZEOF_VOID_P - 1)) != 0)
182 g_error ("stackdata_end (%p) must be %d-byte aligned", stackdata_end, SIZEOF_VOID_P);
184 if (stackdata_size <= 0)
185 g_error ("stackdata_size = %d, but must be > 0, stackdata_begin = %p, stackdata_end = %p", stackdata_size, stackdata_begin, stackdata_end);
187 g_byte_array_set_size (info->stackdata, stackdata_size);
188 state->gc_stackdata = info->stackdata->data;
189 memcpy (state->gc_stackdata, stackdata_end, stackdata_size);
191 state->gc_stackdata_size = stackdata_size;
194 static gpointer
195 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata);
197 gpointer
198 mono_threads_enter_gc_safe_region (gpointer *stackdata)
200 return mono_threads_enter_gc_safe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
203 gpointer
204 mono_threads_enter_gc_safe_region_with_info (MonoThreadInfo *info, gpointer *stackdata)
206 gpointer cookie;
208 if (!mono_threads_is_coop_enabled ())
209 return NULL;
211 cookie = mono_threads_enter_gc_safe_region_unbalanced_with_info (info, stackdata);
213 #ifdef ENABLE_CHECKED_BUILD_GC
214 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
215 coop_tls_push (cookie);
216 #endif
218 return cookie;
221 gpointer
222 mono_threads_enter_gc_safe_region_unbalanced (gpointer *stackdata)
224 return mono_threads_enter_gc_safe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
227 static gpointer
228 mono_threads_enter_gc_safe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata)
230 if (!mono_threads_is_coop_enabled ())
231 return NULL;
233 ++coop_do_blocking_count;
235 check_info (info, "enter", "safe");
237 copy_stack_data (info, stackdata);
239 retry:
240 ++coop_save_count;
241 mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
243 switch (mono_threads_transition_do_blocking (info)) {
244 case DoBlockingContinue:
245 break;
246 case DoBlockingPollAndRetry:
247 mono_threads_state_poll_with_info (info);
248 goto retry;
251 return info;
254 void
255 mono_threads_exit_gc_safe_region (gpointer cookie, gpointer *stackdata)
257 if (!mono_threads_is_coop_enabled ())
258 return;
260 #ifdef ENABLE_CHECKED_BUILD_GC
261 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
262 coop_tls_pop (cookie);
263 #endif
265 mono_threads_exit_gc_safe_region_unbalanced (cookie, stackdata);
268 void
269 mono_threads_exit_gc_safe_region_unbalanced (gpointer cookie, gpointer *stackdata)
271 MonoThreadInfo *info;
273 if (!mono_threads_is_coop_enabled ())
274 return;
276 info = (MonoThreadInfo *)cookie;
278 check_info (info, "exit", "safe");
280 switch (mono_threads_transition_done_blocking (info)) {
281 case DoneBlockingOk:
282 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
283 break;
284 case DoneBlockingWait:
285 THREADS_SUSPEND_DEBUG ("state polling done, notifying of resume\n");
286 mono_thread_info_wait_for_resume (info);
287 break;
288 default:
289 g_error ("Unknown thread state");
293 void
294 mono_threads_assert_gc_safe_region (void)
296 MONO_REQ_GC_SAFE_MODE;
299 gpointer
300 mono_threads_enter_gc_unsafe_region (gpointer *stackdata)
302 return mono_threads_enter_gc_unsafe_region_with_info (mono_thread_info_current_unchecked (), stackdata);
305 gpointer
306 mono_threads_enter_gc_unsafe_region_with_info (THREAD_INFO_TYPE *info, gpointer *stackdata)
308 gpointer cookie;
310 if (!mono_threads_is_coop_enabled ())
311 return NULL;
313 cookie = mono_threads_enter_gc_unsafe_region_unbalanced_with_info (info, stackdata);
315 #ifdef ENABLE_CHECKED_BUILD_GC
316 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
317 coop_tls_push (cookie);
318 #endif
320 return cookie;
323 gpointer
324 mono_threads_enter_gc_unsafe_region_unbalanced (gpointer *stackdata)
326 return mono_threads_enter_gc_unsafe_region_unbalanced_with_info (mono_thread_info_current_unchecked (), stackdata);
329 gpointer
330 mono_threads_enter_gc_unsafe_region_unbalanced_with_info (MonoThreadInfo *info, gpointer *stackdata)
332 if (!mono_threads_is_coop_enabled ())
333 return NULL;
335 ++coop_reset_blocking_count;
337 check_info (info, "enter", "unsafe");
339 copy_stack_data (info, stackdata);
341 switch (mono_threads_transition_abort_blocking (info)) {
342 case AbortBlockingIgnore:
343 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
344 return NULL;
345 case AbortBlockingIgnoreAndPoll:
346 mono_threads_state_poll_with_info (info);
347 return NULL;
348 case AbortBlockingOk:
349 info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
350 break;
351 case AbortBlockingWait:
352 mono_thread_info_wait_for_resume (info);
353 break;
354 default:
355 g_error ("Unknown thread state");
358 return info;
361 gpointer
362 mono_threads_enter_gc_unsafe_region_cookie (void)
364 MonoThreadInfo *info;
366 g_assert (mono_threads_is_coop_enabled ());
368 info = mono_thread_info_current_unchecked ();
370 check_info (info, "enter (cookie)", "unsafe");
372 #ifdef ENABLE_CHECKED_BUILD_GC
373 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
374 coop_tls_push (info);
375 #endif
377 return info;
380 void
381 mono_threads_exit_gc_unsafe_region (gpointer cookie, gpointer *stackdata)
383 if (!mono_threads_is_coop_enabled ())
384 return;
386 #ifdef ENABLE_CHECKED_BUILD_GC
387 if (mono_check_mode_enabled (MONO_CHECK_MODE_GC))
388 coop_tls_pop (cookie);
389 #endif
391 mono_threads_exit_gc_unsafe_region_unbalanced (cookie, stackdata);
394 void
395 mono_threads_exit_gc_unsafe_region_unbalanced (gpointer cookie, gpointer *stackdata)
397 if (!mono_threads_is_coop_enabled ())
398 return;
400 if (!cookie)
401 return;
403 mono_threads_enter_gc_safe_region_unbalanced (stackdata);
406 void
407 mono_threads_assert_gc_unsafe_region (void)
409 MONO_REQ_GC_UNSAFE_MODE;
412 gboolean
413 mono_threads_is_coop_enabled (void)
415 #if defined(USE_COOP_GC)
416 return TRUE;
417 #else
418 static int is_coop_enabled = -1;
419 if (G_UNLIKELY (is_coop_enabled == -1))
420 is_coop_enabled = g_getenv ("MONO_ENABLE_COOP") != NULL ? 1 : 0;
421 return is_coop_enabled == 1;
422 #endif
426 void
427 mono_threads_coop_init (void)
429 if (!mono_threads_is_coop_enabled ())
430 return;
432 mono_counters_register ("Coop Reset Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_reset_blocking_count);
433 mono_counters_register ("Coop Try Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_try_blocking_count);
434 mono_counters_register ("Coop Do Blocking", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_blocking_count);
435 mono_counters_register ("Coop Do Polling", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_do_polling_count);
436 mono_counters_register ("Coop Save Count", MONO_COUNTER_GC | MONO_COUNTER_INT, &coop_save_count);
437 //See the above for what's wrong here.
439 #ifdef ENABLE_CHECKED_BUILD_GC
440 mono_native_tls_alloc (&coop_reset_count_stack_key, NULL);
441 #endif
444 void
445 mono_threads_coop_begin_global_suspend (void)
447 if (mono_threads_is_coop_enabled ())
448 mono_polling_required = 1;
451 void
452 mono_threads_coop_end_global_suspend (void)
454 if (mono_threads_is_coop_enabled ())
455 mono_polling_required = 0;