mono_set_pending_exception (mono_get_exception_thread_state (msg));
return;
}
+
+ mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
thread->abort_exc = NULL;
if (thread->abort_state_handle) {
mono_gchandle_free (thread->abort_state_handle);
thread->state &= ~ThreadState_AbortRequested;
if (thread->abort_exc) {
+ mono_get_eh_callbacks ()->mono_clear_abort_threshold ();
thread->abort_exc = NULL;
if (thread->abort_state_handle) {
mono_gchandle_free (thread->abort_state_handle);
{
MonoInternalThread *thread = mono_thread_internal_current ();
- if (thread && thread->abort_exc && !is_running_protected_wrapper ()) {
- /*
- * FIXME: Clear the abort exception and return an AppDomainUnloaded
- * exception if the thread no longer references a dying appdomain.
- */
- thread->abort_exc->trace_ips = NULL;
- thread->abort_exc->stack_trace = NULL;
- return thread->abort_exc;
- }
+ if (!(thread && thread->abort_exc && !is_running_protected_wrapper ()))
+ return NULL;
- return NULL;
+ // We don't want to have our exception effect calls made by
+ // the catching block
+
+ if (!mono_get_eh_callbacks ()->mono_above_abort_threshold ())
+ return NULL;
+
+ /*
+ * FIXME: Clear the abort exception and return an AppDomainUnloaded
+ * exception if the thread no longer references a dying appdomain.
+ */
+ thread->abort_exc->trace_ips = NULL;
+ thread->abort_exc->stack_trace = NULL;
+ return thread->abort_exc;
}
#if MONO_SMALL_CONFIG
#define MONO_ARCH_CONTEXT_DEF
#endif
+#ifndef MONO_ARCH_STACK_GROWS_UP
+#define MONO_ARCH_STACK_GROWS_UP 0
+#endif
+
static gpointer restore_context_func, call_filter_func;
static gpointer throw_exception_func, rethrow_exception_func;
static gpointer throw_corlib_exception_func;
static void mono_runtime_walk_stack_with_ctx (MonoJitStackWalk func, MonoContext *start_ctx, MonoUnwindOptions unwind_options, void *user_data);
static gboolean mono_current_thread_has_handle_block_guard (void);
+static gboolean
+first_managed (MonoStackFrameInfo *frame, MonoContext *ctx, gpointer addr)
+{
+ gpointer **data = (gpointer **)addr;
+
+ if (!frame->managed)
+ return FALSE;
+
+ *data = MONO_CONTEXT_GET_SP(ctx);
+ g_assert (*data);
+ return TRUE;
+}
+
+static gpointer
+mono_thread_get_managed_sp (void)
+{
+ gpointer addr = NULL;
+ mono_walk_stack (first_managed, MONO_UNWIND_SIGNAL_SAFE, &addr);
+ return addr;
+}
+
+static inline int
+mini_abort_threshold_offset (gpointer threshold, gpointer sp)
+{
+ intptr_t stack_threshold = (intptr_t) threshold;
+ intptr_t stack_pointer = (intptr_t) sp;
+
+ const int direction = MONO_ARCH_STACK_GROWS_UP ? -1 : 1;
+ intptr_t magnitude = stack_pointer - stack_threshold;
+
+ return direction * magnitude;
+}
+
+static inline void
+mini_clear_abort_threshold (void)
+{
+ MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+ jit_tls->abort_exc_stack_threshold = NULL;
+}
+
+static inline void
+mini_set_abort_threshold (MonoContext *ctx)
+{
+ gpointer sp = MONO_CONTEXT_GET_SP (ctx);
+ MonoJitTlsData *jit_tls = mono_get_jit_tls ();
+ // Only move it up, to avoid thrown/caught
+ // exceptions lower in the stack from triggering
+ // a rethrow
+ gboolean above_threshold = mini_abort_threshold_offset (jit_tls->abort_exc_stack_threshold, sp) >= 0;
+ if (!jit_tls->abort_exc_stack_threshold || above_threshold) {
+ jit_tls->abort_exc_stack_threshold = sp;
+ }
+}
+
+// Note: In the case that the frame is above where the thread abort
+// was set we bump the threshold so that functions called from the new,
+// higher threshold don't trigger the thread abort exception
+static inline gboolean
+mini_above_abort_threshold (void)
+{
+ gpointer sp = mono_thread_get_managed_sp ();
+ MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
+
+ gboolean above_threshold = mini_abort_threshold_offset (jit_tls->abort_exc_stack_threshold, sp) >= 0;
+
+ if (above_threshold)
+ jit_tls->abort_exc_stack_threshold = sp;
+
+ return above_threshold;
+}
+
void
mono_exceptions_init (void)
{
cbs.mono_exception_walk_trace = mono_exception_walk_trace;
cbs.mono_install_handler_block_guard = mono_install_handler_block_guard;
cbs.mono_current_thread_has_handle_block_guard = mono_current_thread_has_handle_block_guard;
+ cbs.mono_clear_abort_threshold = mini_above_abort_threshold;
+ cbs.mono_above_abort_threshold = mini_clear_abort_threshold;
mono_install_eh_callbacks (&cbs);
}
return val;
}
-#ifndef MONO_ARCH_STACK_GROWS_UP
-#define DOES_STACK_GROWS_UP 1
-#else
-#define DOES_STACK_GROWS_UP 0
-#endif
-
#define MAX_UNMANAGED_BACKTRACE 128
static MonoArray*
build_native_trace (MonoError *error)
dynamic_methods = g_slist_prepend (dynamic_methods, method);
if (stack_overflow) {
- if (DOES_STACK_GROWS_UP)
- free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
- else
+ if (MONO_ARCH_STACK_GROWS_UP)
free_stack = (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (ctx));
+ else
+ free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
} else {
free_stack = 0xffffff;
}
setup_stack_trace (mono_ex, dynamic_methods, initial_trace_ips, &trace_ips);
g_slist_free (dynamic_methods);
/* mono_debugger_agent_handle_exception () needs this */
+ mini_set_abort_threshold (ctx);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
return TRUE;
}
// FIXME: This runs managed code so it might cause another stack overflow when
// we are handling a stack overflow
+ mini_set_abort_threshold (ctx);
mono_unhandled_exception (obj);
} else {
gboolean unhandled = FALSE;
//printf ("M: %s %d.\n", mono_method_full_name (method, TRUE), frame_count);
if (stack_overflow) {
- if (DOES_STACK_GROWS_UP)
- free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
- else
+ if (MONO_ARCH_STACK_GROWS_UP)
free_stack = (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (ctx));
+ else
+ free_stack = (guint8*)(MONO_CONTEXT_GET_SP (ctx)) - (guint8*)(MONO_CONTEXT_GET_SP (&initial_ctx));
} else {
free_stack = 0xffffff;
}
jit_tls->orig_ex_ctx_set = TRUE;
mono_profiler_exception_clause_handler (method, ei->flags, i);
jit_tls->orig_ex_ctx_set = FALSE;
+ mini_set_abort_threshold (ctx);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
mono_set_lmf (lmf);
#ifndef DISABLE_PERFCOUNTERS
jit_tls->orig_ex_ctx_set = TRUE;
mono_profiler_exception_clause_handler (method, ei->flags, i);
jit_tls->orig_ex_ctx_set = FALSE;
+ mini_set_abort_threshold (ctx);
call_filter (ctx, ei->handler_start);
}
if (ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
jit_tls->resume_state.lmf = lmf;
jit_tls->resume_state.first_filter_idx = first_filter_idx;
jit_tls->resume_state.filter_idx = filter_idx;
+ mini_set_abort_threshold (ctx);
MONO_CONTEXT_SET_IP (ctx, ei->handler_start);
return 0;
} else {
+ mini_set_abort_threshold (ctx);
call_filter (ctx, ei->handler_start);
}
}
return 0;
}
+ public static void HasTry ()
+ {
+ try {
+ throw new Exception ("boop");
+ } catch (Exception e) {
+ // See if we re-throw the thread abort exception here
+ }
+ }
+
+ public static int test_0_thread_abort_water_mark ()
+ {
+ Boolean failed = true;
+
+ try {
+ Thread.CurrentThread.Abort ("test_0_thread_abort_water_mark");
+ } catch (ThreadAbortException e) {
+ HasTry ();
+ Thread.ResetAbort ();
+ failed = false;
+ } finally {
+ if (failed) {
+ Thread.ResetAbort ();
+ throw new Exception ("Threw pending ThreadAbort exception under stack threshold");
+ }
+ Console.WriteLine ("Working thread abort");
+ }
+
+ return 0;
+ }
+
+ public static int test_0_thread_abort_water_mark_other_exc ()
+ {
+ Boolean failed = true;
+
+ try {
+ try {
+ try {
+ Thread.CurrentThread.Abort ("TestKeepAbort");
+ } catch (ThreadAbortException ta_ex) {
+ throw new ArgumentNullException("SpecificDummyException");
+ }
+ } catch (ArgumentNullException ex){
+ // Throw ThreadAbortException here
+ }
+ } catch (ThreadAbortException ex) {
+ Console.WriteLine ("Retained thread abort exception");
+ failed = false;
+ Thread.ResetAbort ();
+ } catch (Exception e) {
+ failed = true;
+ } finally {
+ if (failed)
+ throw new Exception ("Lost the thread abort due to another exception running.");
+ }
+
+ return 0;
+ }
+
public class CBO : ContextBoundObject {
public void Run () {
Thread.CurrentThread.Abort ("FOO");