diff --git a/runtime/runtime.m b/runtime/runtime.m index f4d8faaf2ea8..008dd1796856 100644 --- a/runtime/runtime.m +++ b/runtime/runtime.m @@ -1133,18 +1133,24 @@ -(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags; abort (); } +extern "C" { + static thread_local int xamarin_handling_unhandled_exceptions = 0; +} + static void exception_handler (NSException *exc) { // COOP: We won't get here in coop-mode, because we don't set the uncaught objc exception handler in that case. LOG (PRODUCT ": Received unhandled ObjectiveC exception: %@ %@", [exc name], [exc reason]); - if (xamarin_is_gc_coop) { - PRINT ("Uncaught Objective-C exception: %@", exc); - assert (false); // Re-throwing the Objective-C exception will probably just end up with infinite recursion + if (xamarin_handling_unhandled_exceptions == 1) { + PRINT ("Detected recursion when handling uncaught Objective-C exception: %@", exc); + abort (); } + xamarin_handling_unhandled_exceptions = 1; xamarin_throw_ns_exception (exc); + xamarin_handling_unhandled_exceptions = 0; } #if defined (DEBUG) @@ -2369,6 +2375,14 @@ -(void) xamarinSetFlags: (enum XamarinGCHandleFlags) flags; xamarin_gchandle_free (exception_gchandle); } + // If we end up here as part of an unhandled Objective-C exception, we're also trying to detect an infinite loop by + // setting the xamarin_handling_unhandled_exceptions variable to 1 before processing the unhandled Objective-C exception, + // and clearing it afterwards. However, the clearing of the variable will never happen if Mono unwinds native + // stack frames, and then on the next unhandled Objective-C exception we'll think we're recursing when we're really not. + // (FWIW this is yet another reason why letting Mono unhandled native frames is a really bad idea). + // So here we work around that by clearing the variable before letting Mono unwind native frames. + xamarin_handling_unhandled_exceptions = 0; + mono_raise_exception ((MonoException *) exception); #endif break;