Skip to content

Commit

Permalink
[runtime] Reworked coop GC stack handling in platforms with restricte…
Browse files Browse the repository at this point in the history
…d access to register contexts.
  • Loading branch information
tritao committed Oct 14, 2015
1 parent 97ad88d commit 610ac81
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 31 deletions.
7 changes: 7 additions & 0 deletions eglib/src/gbytearray.c
Expand Up @@ -49,3 +49,10 @@ g_byte_array_append (GByteArray *array,
{
return (GByteArray *)g_array_append_vals ((GArray *)array, data, len);
}

void
g_byte_array_set_size (GByteArray *array, gint length)
{
g_array_set_size ((GArray *)array, length);
}

1 change: 1 addition & 0 deletions eglib/src/glib.h
Expand Up @@ -486,6 +486,7 @@ struct _GByteArray {
GByteArray *g_byte_array_new (void);
GByteArray* g_byte_array_append (GByteArray *array, const guint8 *data, guint len);
guint8* g_byte_array_free (GByteArray *array, gboolean free_segment);
void g_byte_array_set_size (GByteArray *array, gint length);

/*
* Array
Expand Down
9 changes: 9 additions & 0 deletions mono/metadata/sgen-mono.c
Expand Up @@ -2376,6 +2376,15 @@ sgen_client_scan_thread_data (void *start_nursery, void *end_nursery, gboolean p
sgen_conservatively_pin_objects_from ((void**)&info->client_info.regs, (void**)&info->client_info.regs + ARCH_NUM_REGS,
start_nursery, end_nursery, PIN_TYPE_STACK);
#endif
{
// This is used on Coop GC for platforms where we cannot get the data for individual registers.
// We force a spill of all registers into the stack and pass a chunk of data into sgen.
MonoThreadUnwindState *state = &info->client_info.info.thread_saved_state [SELF_SUSPEND_STATE_INDEX];
if (state && state->gc_stackdata) {
sgen_conservatively_pin_objects_from (state->gc_stackdata, (void**)((char*)state->gc_stackdata + state->gc_stackdata_size),
start_nursery, end_nursery, PIN_TYPE_STACK);
}
}
}
} END_FOREACH_THREAD
}
Expand Down
2 changes: 2 additions & 0 deletions mono/utils/mono-stack-unwinding.h
Expand Up @@ -94,6 +94,8 @@ typedef struct {
MonoContext ctx;
gpointer unwind_data [3]; /*right now: domain, lmf and jit_tls*/
gboolean valid;
void *gc_stackdata;
int gc_stackdata_size;
} MonoThreadUnwindState;


Expand Down
8 changes: 4 additions & 4 deletions mono/utils/mono-threads-api.h
Expand Up @@ -20,8 +20,8 @@ This API is experimental. It will eventually be required to properly use the res
*/

/* Don't use those directly, use the MONO_(BEGIN|END)_EFRAME */
MONO_API void* mono_threads_enter_gc_unsafe_region (void);
MONO_API void mono_threads_exit_gc_unsafe_region (void *region_cookie);
MONO_API void* mono_threads_enter_gc_unsafe_region (void* stackdata);
MONO_API void mono_threads_exit_gc_unsafe_region (void *region_cookie, void* stackdata);

/*
Use those macros to limit regions of code that interact with managed memory or use the embedding API.
Expand All @@ -31,8 +31,8 @@ For further explanation of what can and can't be done in GC unsafe mode:
http://www.mono-project.com/docs/advanced/runtime/docs/coop-suspend/#gc-unsafe-mode
*/
#define MONO_BEGIN_EFRAME { void *__region_cookie = mono_threads_enter_gc_unsafe_region ();
#define MONO_END_EFRAME mono_threads_exit_gc_unsafe_region (__region_cookie); }
#define MONO_BEGIN_EFRAME { void *__dummy; void *__region_cookie = mono_threads_enter_gc_unsafe_region (__dummy);
#define MONO_END_EFRAME mono_threads_exit_gc_unsafe_region (__region_cookie, __dummy); }


MONO_END_DECLS
Expand Down
71 changes: 56 additions & 15 deletions mono/utils/mono-threads-coop.c
Expand Up @@ -29,6 +29,13 @@
#include <mono/utils/mach-support.h>
#endif

#ifdef _MSC_VER
// TODO: Find MSVC replacement for __builtin_unwind_init
#define SAVE_REGS_ON_STACK g_assert_not_reached ();
#else
#define SAVE_REGS_ON_STACK __builtin_unwind_init ();
#endif

#ifdef USE_COOP_BACKEND

volatile size_t mono_polling_required;
Expand Down Expand Up @@ -67,8 +74,36 @@ mono_threads_state_poll (void)
}
}

static void *
return_stack_ptr ()
{
int i;
return &i;

This comment has been minimized.

Copy link
@luhenry

luhenry Oct 26, 2015

Contributor

why not use __builtin_frame_address on compilers that support it (gcc and llvm), as we have a warning every time this function is compiled?

This comment has been minimized.

Copy link
@tritao

tritao Oct 26, 2015

Author Contributor

Sounds like a great idea. Alternatively we can also get rid of the warning using some #pragmas.

}

static void
copy_stack_data (MonoThreadInfo *info, void* stackdata_begin)
{
MonoThreadUnwindState *state;
int stackdata_size;
void* stackdata_end = return_stack_ptr ();

SAVE_REGS_ON_STACK;

state = &info->thread_saved_state [SELF_SUSPEND_STATE_INDEX];

stackdata_size = (char*)stackdata_begin - (char*)stackdata_end;
g_assert (stackdata_size > 0);

g_byte_array_set_size (info->stackdata, stackdata_size);
state->gc_stackdata = info->stackdata->data;
memcpy (state->gc_stackdata, stackdata_end, stackdata_size);

state->gc_stackdata_size = stackdata_size;
}

void*
mono_threads_prepare_blocking (void)
mono_threads_prepare_blocking (void* stackdata)
{
MonoThreadInfo *info;
++coop_do_blocking_count;
Expand All @@ -80,6 +115,8 @@ mono_threads_prepare_blocking (void)
return NULL;
}

copy_stack_data (info, stackdata);

retry:
++coop_save_count;
mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
Expand All @@ -96,7 +133,7 @@ mono_threads_prepare_blocking (void)
}

void
mono_threads_finish_blocking (void *cookie)
mono_threads_finish_blocking (void *cookie, void* stackdata)
{
static gboolean warned_about_bad_transition;
MonoThreadInfo *info = cookie;
Expand Down Expand Up @@ -128,7 +165,7 @@ mono_threads_finish_blocking (void *cookie)


void*
mono_threads_reset_blocking_start (void)
mono_threads_reset_blocking_start (void* stackdata)
{
MonoThreadInfo *info = mono_thread_info_current_unchecked ();
++coop_reset_blocking_count;
Expand All @@ -137,6 +174,8 @@ mono_threads_reset_blocking_start (void)
if (!info || !mono_thread_info_is_live (info))
return NULL;

copy_stack_data (info, stackdata);

switch (mono_threads_transition_abort_blocking (info)) {
case AbortBlockingIgnore:
info->thread_saved_state [SELF_SUSPEND_STATE_INDEX].valid = FALSE;
Expand All @@ -156,19 +195,19 @@ mono_threads_reset_blocking_start (void)
}

void
mono_threads_reset_blocking_end (void *cookie)
mono_threads_reset_blocking_end (void *cookie, void* stackdata)
{
MonoThreadInfo *info = cookie;

if (!info)
return;

g_assert (info == mono_thread_info_current_unchecked ());
mono_threads_prepare_blocking ();
mono_threads_prepare_blocking (stackdata);
}

void*
mono_threads_try_prepare_blocking (void)
mono_threads_try_prepare_blocking (void* stackdata)
{
MonoThreadInfo *info;
++coop_try_blocking_count;
Expand All @@ -180,6 +219,8 @@ mono_threads_try_prepare_blocking (void)
return NULL;
}

copy_stack_data (info, stackdata);

retry:
++coop_save_count;
mono_threads_get_runtime_callbacks ()->thread_state_init (&info->thread_saved_state [SELF_SUSPEND_STATE_INDEX]);
Expand All @@ -196,9 +237,9 @@ mono_threads_try_prepare_blocking (void)
}

void
mono_threads_finish_try_blocking (void* cookie)
mono_threads_finish_try_blocking (void* cookie, void* stackdata)
{
mono_threads_finish_blocking (cookie);
mono_threads_finish_blocking (cookie, stackdata);
}

gboolean
Expand All @@ -210,7 +251,7 @@ mono_threads_core_begin_async_resume (MonoThreadInfo *info)

gboolean
mono_threads_core_begin_async_suspend (MonoThreadInfo *info, gboolean interrupt_kernel)
{
{

This comment has been minimized.

Copy link
@Therzok

Therzok Oct 26, 2015

Contributor

Whitespace :trollface:

mono_threads_add_to_pending_operation_set (info);
/* There's nothing else to do after we async request the thread to suspend */
return TRUE;
Expand Down Expand Up @@ -271,27 +312,27 @@ mono_threads_core_end_global_suspend (void)
}

void*
mono_threads_enter_gc_unsafe_region (void)
mono_threads_enter_gc_unsafe_region (void* stackdata)
{
return mono_threads_reset_blocking_start ();
return mono_threads_reset_blocking_start (stackdata);
}

void
mono_threads_exit_gc_unsafe_region (void *regions_cookie)
mono_threads_exit_gc_unsafe_region (void *regions_cookie, void* stackdata)
{
mono_threads_reset_blocking_end (regions_cookie);
mono_threads_reset_blocking_end (regions_cookie, stackdata);
}

#else

void*
mono_threads_enter_gc_unsafe_region (void)
mono_threads_enter_gc_unsafe_region (void* stackdata)
{
return NULL;
}

void
mono_threads_exit_gc_unsafe_region (void *regions_cookie)
mono_threads_exit_gc_unsafe_region (void *regions_cookie, void* stackdata)
{
}

Expand Down
29 changes: 17 additions & 12 deletions mono/utils/mono-threads-coop.h
Expand Up @@ -22,39 +22,44 @@

#define MONO_PREPARE_BLOCKING \
{ \
void *__blocking_cookie = mono_threads_prepare_blocking ();
void *__dummy; \
void *__blocking_cookie = mono_threads_prepare_blocking (&__dummy);

#define MONO_FINISH_BLOCKING \
mono_threads_finish_blocking (__blocking_cookie); \
mono_threads_finish_blocking (__blocking_cookie, &__dummy); \
}

#define MONO_PREPARE_RESET_BLOCKING \
{ \
void *__reset_cookie = mono_threads_reset_blocking_start ();
void *__dummy; \
void *__reset_cookie = mono_threads_reset_blocking_start (&__dummy);

#define MONO_FINISH_RESET_BLOCKING \
mono_threads_reset_blocking_end (__reset_cookie); \
mono_threads_reset_blocking_end (__reset_cookie, &__dummy); \
}

#define MONO_TRY_BLOCKING \
{ \
void *__try_block_cookie = mono_threads_try_prepare_blocking ();
void *__dummy; \
void *__try_block_cookie = mono_threads_try_prepare_blocking (&__dummy);

#define MONO_FINISH_TRY_BLOCKING \
mono_threads_finish_try_blocking (__try_block_cookie); \
mono_threads_finish_try_blocking (__try_block_cookie, &__dummy); \
}

/* Internal API */

void mono_threads_state_poll (void);
void* mono_threads_prepare_blocking (void);
void mono_threads_finish_blocking (void* cookie);
void mono_threads_state_poll_stack_data (void* stackdata);

void* mono_threads_reset_blocking_start (void);
void mono_threads_reset_blocking_end (void* cookie);
void* mono_threads_prepare_blocking (void* stackdata);
void mono_threads_finish_blocking (void* cookie, void* stackdata);

void* mono_threads_try_prepare_blocking (void);
void mono_threads_finish_try_blocking (void* cookie);
void* mono_threads_reset_blocking_start (void* stackdata);
void mono_threads_reset_blocking_end (void* cookie, void* stackdata);

void* mono_threads_try_prepare_blocking (void* stackdata);
void mono_threads_finish_try_blocking (void* cookie, void* stackdata);

/* JIT specific interface */
extern volatile size_t mono_polling_required;
Expand Down
4 changes: 4 additions & 0 deletions mono/utils/mono-threads.c
Expand Up @@ -326,6 +326,8 @@ register_thread (MonoThreadInfo *info, gpointer baseptr)
info->stack_start_limit = staddr;
info->stack_end = staddr + stsize;

info->stackdata = g_byte_array_new ();

mono_threads_platform_register (info);

/*
Expand Down Expand Up @@ -387,6 +389,8 @@ unregister_thread (void *arg)

mono_thread_info_suspend_unlock ();

g_byte_array_free (info->stackdata, /*free_segment=*/TRUE);

/*now it's safe to free the thread info.*/
mono_thread_hazardous_free_or_queue (info, free_thread_info, TRUE, FALSE);
mono_thread_small_id_free (small_id);
Expand Down
3 changes: 3 additions & 0 deletions mono/utils/mono-threads.h
Expand Up @@ -224,6 +224,9 @@ typedef struct {
int signal;
#endif

/* This memory pool is used by coop GC to save stack data roots between GC unsafe regions */
GByteArray *stackdata;

/*In theory, only the posix backend needs this, but having it on mach/win32 simplifies things a lot.*/
MonoThreadUnwindState thread_saved_state [2]; //0 is self suspend, 1 is async suspend.

Expand Down

0 comments on commit 610ac81

Please sign in to comment.