Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/libgc
diff options
context:
space:
mode:
authorAlex Rønne Petersen <alex@alexrp.com>2018-02-27 20:36:26 +0300
committerLudovic Henry <luhenry@microsoft.com>2018-02-27 20:36:26 +0300
commita5da7b21f4b6dbc5eaa09c2addee91b84dc1dbd5 (patch)
tree7e1b9c1d25df8b40c28e7e0d27e0de94b3807ba8 /libgc
parente8141fc31da958214e42dc28a523e9efc0c1ae10 (diff)
[runtime] Implement thread info flags and get rid of tools threads. (#7226)
* [acceptance-tests] Revert part of 1216780e830e521b8a4f61a8ca3c505ab63b9e2f. This didn't work as well as I'd hoped. * [libgc] Export GC_start_blocking () and GC_end_blocking (). * [libgc] Allow GC_start_blocking ()/GC_end_blocking () to be called unbalanced. This makes things a bit easier in Mono. * [libgc] Implement GC_start_blocking ()/GC_end_blocking () for Windows. * [utils/event] Export the MonoOSEvent APIs for use in the profiler. * [runtime] Implement thread info flags and get rid of tools threads. The concept of a tools thread was originally a good solution to the problem of some internal threads needing to be exempt from stop-the-world. Since they were introduced, however, we've added more code in the profiler's internal threads which require certain properties that don't hold true for tools threads. In particular, we call into some APIs in the metadata layer that, while not needing the managed heap, do need a domain to be set under certain conditions (see #6188). To avoid further complicating the semantics of tools thread by trying to set a domain for them in a way that doesn't break things horribly, we'll instead do away with the concept entirely. We already had mono_gc_set_skip_thread () to flag a thread as being exempt from stop-the-world, which we used in thread pool threads. This means we already have the infrastructure that is necessary for a thread to be fully attached to the runtime as a managed thread while still being exempt from stop-the-world. So, we do the following: 1. Introduce a set of flags that can be set on a MonoThreadInfo at arbitrary points (although not in async context). These can indicate that the thread doesn't want to participate in stop-the-world, doesn't want to receive sampling signals, etc. 2. Change the thread iteration macros to allow skipping threads with specific flags set and use this where appropriate (e.g. SGen stop-the-world code). 3. Get rid of mono_gc_set_skip_thread () and make runtime code use the new mono_thread_info_set_flags () instead. 4. Switch all internal profiler threads to using mono_thread_attach () and mono_thread_detach (). Use mono_thread_info_set_flags () to disable stop-the-world and sampling signals for these threads immediately after they start. With these changes, internal profiler threads are now fully attached threads and can call any APIs in the runtime as long as they don't touch the managed heap. * [libgc] Add a note about how GC_start_blocking ()/GC_end_blocking () differ from upstream.
Diffstat (limited to 'libgc')
-rw-r--r--libgc/include/gc.h12
-rw-r--r--libgc/pthread_support.c2
-rw-r--r--libgc/win32_threads.c48
3 files changed, 59 insertions, 3 deletions
diff --git a/libgc/include/gc.h b/libgc/include/gc.h
index e7929918ad0..c9c20e65a6c 100644
--- a/libgc/include/gc.h
+++ b/libgc/include/gc.h
@@ -835,6 +835,18 @@ typedef GC_PTR (*GC_fn_type) GC_PROTO((GC_PTR client_data));
GC_API GC_PTR GC_call_with_alloc_lock
GC_PROTO((GC_fn_type fn, GC_PTR client_data));
+/*
+ * These are similar to GC_do_blocking () in upstream bdwgc. The design is
+ * simpler in that there is no distinction between active and inactive stack
+ * frames; instead, while a thread is in blocking state, it promises to not
+ * interact with the GC at all, and to not keep any pointers to GC memory
+ * around from before entering blocking state. Additionally, these can be
+ * called unbalanced (they simply set a flag internally).
+ */
+GC_API void GC_start_blocking GC_PROTO((void));
+
+GC_API void GC_end_blocking GC_PROTO((void));
+
/* The following routines are primarily intended for use with a */
/* preprocessor which inserts calls to check C pointer arithmetic. */
/* They indicate failure by invoking the corresponding _print_proc. */
diff --git a/libgc/pthread_support.c b/libgc/pthread_support.c
index 6d240f65f0e..3ff89b29222 100644
--- a/libgc/pthread_support.c
+++ b/libgc/pthread_support.c
@@ -1245,7 +1245,6 @@ void GC_start_blocking(void) {
GC_thread me;
LOCK();
me = GC_lookup_thread(pthread_self());
- GC_ASSERT(!(me -> thread_blocked));
# ifdef SPARC
me -> stop_info.stack_ptr = (ptr_t)GC_save_regs_in_stack();
# else
@@ -1273,7 +1272,6 @@ void GC_end_blocking(void) {
GC_thread me;
LOCK(); /* This will block if the world is stopped. */
me = GC_lookup_thread(pthread_self());
- GC_ASSERT(me -> thread_blocked);
me -> thread_blocked = FALSE;
UNLOCK();
}
diff --git a/libgc/win32_threads.c b/libgc/win32_threads.c
index 5533b8f2e25..425d41a9892 100644
--- a/libgc/win32_threads.c
+++ b/libgc/win32_threads.c
@@ -48,6 +48,7 @@ struct GC_thread_Rep {
/* 0 ==> entry not valid. */
/* !in_use ==> stack_base == 0 */
GC_bool suspended;
+ GC_bool thread_blocked;
# ifdef CYGWIN32
void *status; /* hold exit value until join in case it's a pointer */
@@ -174,6 +175,7 @@ static GC_thread GC_new_thread(void) {
/* the world, wait here. Hopefully this can't happen on any */
/* systems that don't allow us to block here. */
while (GC_please_stop) Sleep(20);
+ GC_ASSERT(!thread_table[i]->thread_blocked);
return thread_table + i;
}
@@ -223,7 +225,6 @@ static void GC_delete_thread(DWORD thread_id) {
}
}
-
#ifdef CYGWIN32
/* Return a GC_thread corresponding to a given pthread_t. */
@@ -245,6 +246,20 @@ static GC_thread GC_lookup_thread(pthread_t id)
return thread_table + i;
}
+#else
+
+static GC_thread GC_lookup_thread(DWORD id)
+{
+ int i;
+ LONG max = GC_get_max_thread_index();
+
+ for (i = 0; i <= max; i++)
+ if (thread_table[i].in_use && thread_table[i].id == id)
+ return &thread_table[i];
+
+ return NULL;
+}
+
#endif /* CYGWIN32 */
void GC_push_thread_structures GC_PROTO((void))
@@ -264,6 +279,35 @@ void GC_push_thread_structures GC_PROTO((void))
# endif
}
+/* Wrappers for functions that are likely to block for an appreciable */
+/* length of time. Must be called in pairs, if at all. */
+/* Nothing much beyond the system call itself should be executed */
+/* between these. */
+
+void GC_start_blocking(void) {
+ GC_thread me;
+ LOCK();
+#ifdef CYGWIN32
+ me = GC_lookup_thread(pthread_self());
+#else
+ me = GC_lookup_thread(GetCurrentThreadId());
+#endif
+ me->thread_blocked = TRUE;
+ UNLOCK();
+}
+
+void GC_end_blocking(void) {
+ GC_thread me;
+ LOCK(); /* This will block if the world is stopped. */
+#ifdef CYGWIN32
+ me = GC_lookup_thread(pthread_self());
+#else
+ me = GC_lookup_thread(GetCurrentThreadId());
+#endif
+ me->thread_blocked = FALSE;
+ UNLOCK();
+}
+
/* Defined in misc.c */
extern CRITICAL_SECTION GC_write_cs;
@@ -281,6 +325,8 @@ void GC_stop_world()
for (i = 0; i <= GC_get_max_thread_index(); i++)
if (thread_table[i].stack_base != 0
&& thread_table[i].id != thread_id) {
+ if (thread_table [i].thread_blocked)
+ continue;
# ifdef MSWINCE
/* SuspendThread will fail if thread is running kernel code */
while (SuspendThread(thread_table[i].handle) == (DWORD)-1)