Sleep-loop rather than abort during termination

We don't want to actually exit the process as it'll automatically be restarted gracefully due to a timeout after being unable to exit within a fixed duration so we just want to infinite sleep during termination. This should fix issues where exiting any game would cause the app to force close after some time as exception signal handling would fail in the background, the app should stay open now and automatically restart itself when another game is loaded in.
This commit is contained in:
PixelyIon 2022-04-10 13:26:02 +05:30
parent ea00f1bb82
commit 164d4852fa

View File

@ -14,7 +14,12 @@ namespace skyline::signal {
std::rethrow_exception(SignalExceptionPtr); std::rethrow_exception(SignalExceptionPtr);
} }
std::terminate_handler terminateHandler{}; void SleepTillExit() {
// We don't want to actually exit the process ourselves as it'll automatically be restarted gracefully due to a timeout after being unable to exit within a fixed duration
Logger::EmulationContext.TryFlush(); // We want to attempt to flush logs before exiting
while (true)
sleep(std::numeric_limits<int>::max()); // We just trigger the timeout wait by sleeping forever
}
inline StackFrame *SafeFrameRecurse(size_t depth, StackFrame *frame) { inline StackFrame *SafeFrameRecurse(size_t depth, StackFrame *frame) {
if (frame) { if (frame) {
@ -22,17 +27,17 @@ namespace skyline::signal {
if (frame->lr && frame->next) if (frame->lr && frame->next)
frame = frame->next; frame = frame->next;
else else
terminateHandler(); SleepTillExit();
} }
} else { } else {
terminateHandler(); SleepTillExit();
} }
return frame; return frame;
} }
void TerminateHandler() { void TerminateHandler() {
auto exception{std::current_exception()}; auto exception{std::current_exception()};
if (terminateHandler && exception && exception == SignalExceptionPtr) { if (exception && exception == SignalExceptionPtr) {
StackFrame *frame; StackFrame *frame;
asm("MOV %0, FP" : "=r"(frame)); asm("MOV %0, FP" : "=r"(frame));
frame = SafeFrameRecurse(2, frame); // We unroll past 'std::terminate' frame = SafeFrameRecurse(2, frame); // We unroll past 'std::terminate'
@ -56,17 +61,14 @@ namespace skyline::signal {
frame = SafeFrameRecurse(2, lookupFrame); frame = SafeFrameRecurse(2, lookupFrame);
hasAdvanced = true; hasAdvanced = true;
} else { } else {
Logger::EmulationContext.TryFlush(); SleepTillExit(); // We presumably have no exception handlers left on the stack to consume the exception, it's time to quit
terminateHandler(); // We presumably have no exception handlers left on the stack to consume the exception, it's time to quit
} }
} }
lookupFrame = lookupFrame->next; lookupFrame = lookupFrame->next;
} }
if (!frame->next) { if (!frame->next)
Logger::EmulationContext.TryFlush(); // We want to attempt to flush all logs before quitting SleepTillExit(); // We don't know the frame's stack boundaries, the only option is to quit
terminateHandler(); // We don't know the frame's stack boundaries, the only option is to quit
}
asm("MOV SP, %x0\n\t" // Stack frame is the first item on a function's stack, it's used to calculate calling function's stack pointer asm("MOV SP, %x0\n\t" // Stack frame is the first item on a function's stack, it's used to calculate calling function's stack pointer
"MOV LR, %x1\n\t" "MOV LR, %x1\n\t"
@ -76,7 +78,7 @@ namespace skyline::signal {
__builtin_unreachable(); __builtin_unreachable();
} else { } else {
terminateHandler(); SleepTillExit(); // We don't want to delegate to the older terminate handler as it might cause an exit
} }
} }
@ -97,11 +99,7 @@ namespace skyline::signal {
SignalExceptionPtr = std::make_exception_ptr(signalException); SignalExceptionPtr = std::make_exception_ptr(signalException);
context->uc_mcontext.pc = reinterpret_cast<u64>(&ExceptionThrow); context->uc_mcontext.pc = reinterpret_cast<u64>(&ExceptionThrow);
auto handler{std::get_terminate()}; std::set_terminate(TerminateHandler);
if (handler != TerminateHandler) {
terminateHandler = handler;
std::set_terminate(TerminateHandler);
}
Logger::EmulationContext.TryFlush(); // We want to attempt to flush all logs in case exception handling fails and infloops Logger::EmulationContext.TryFlush(); // We want to attempt to flush all logs in case exception handling fails and infloops
} }