Properly uninitialize all client components

Track stack of cleaner functions that destruct client components so exactly the correct cleanup is performed in the reverse order of initialization.

This allows performing the cleanup also in the early-return cases without introducing duplicate code.
This commit is contained in:
Robert Müller 2023-05-10 20:51:25 +02:00
parent 95b0f8c1e4
commit c8b69dd815

View file

@ -5,6 +5,7 @@
#include <climits> #include <climits>
#include <new> #include <new>
#include <stack>
#include <tuple> #include <tuple>
#include <base/hash.h> #include <base/hash.h>
@ -4547,7 +4548,6 @@ int main(int argc, const char **argv)
CCmdlineFix CmdlineFix(&argc, &argv); CCmdlineFix CmdlineFix(&argc, &argv);
bool Silent = false; bool Silent = false;
bool RandInitFailed = false;
for(int i = 1; i < argc; i++) for(int i = 1; i < argc; i++)
{ {
@ -4582,17 +4582,42 @@ int main(int argc, const char **argv)
vpLoggers.push_back(pFutureAssertionLogger); vpLoggers.push_back(pFutureAssertionLogger);
log_set_global_logger(log_logger_collection(std::move(vpLoggers)).release()); log_set_global_logger(log_logger_collection(std::move(vpLoggers)).release());
if(secure_random_init() != 0) std::stack<std::function<void()>> CleanerFunctions;
{ std::function<void()> PerformCleanup = [&CleanerFunctions]() mutable {
RandInitFailed = true; while(!CleanerFunctions.empty())
} {
CleanerFunctions.top()();
CleanerFunctions.pop();
}
};
std::function<void()> PerformFinalCleanup = []() {
#ifdef CONF_PLATFORM_ANDROID
// properly close this native thread, so globals are destructed
std::exit(0);
#endif
};
std::function<void()> PerformAllCleanup = [PerformCleanup, PerformFinalCleanup]() mutable {
PerformCleanup();
PerformFinalCleanup();
};
const bool RandInitFailed = secure_random_init() != 0;
if(!RandInitFailed)
CleanerFunctions.push([]() { secure_random_uninit(); });
NotificationsInit(); NotificationsInit();
CleanerFunctions.push([]() { NotificationsUninit(); });
CClient *pClient = CreateClient(); CClient *pClient = CreateClient();
IKernel *pKernel = IKernel::Create(); IKernel *pKernel = IKernel::Create();
pKernel->RegisterInterface(pClient, false); pKernel->RegisterInterface(pClient, false);
pClient->RegisterInterfaces(); pClient->RegisterInterfaces();
CleanerFunctions.push([pKernel, pClient]() {
pKernel->Shutdown();
delete pKernel;
pClient->~CClient();
free(pClient);
});
const std::thread::id MainThreadId = std::this_thread::get_id(); const std::thread::id MainThreadId = std::this_thread::get_id();
dbg_assert_set_handler([MainThreadId, pClient](const char *pMsg) { dbg_assert_set_handler([MainThreadId, pClient](const char *pMsg) {
@ -4604,6 +4629,7 @@ int main(int argc, const char **argv)
char aMessage[512]; char aMessage[512];
str_format(aMessage, sizeof(aMessage), "An assertion error occured. Please write down or take a screenshot of the following information and report this error.\nPlease also share the assert log which you should find in the 'dumps' folder in your config directory.\n\n%s\n\nPlatform: %s\nGame version: %s %s\nOS version: %s", pMsg, CONF_PLATFORM_STRING, GAME_RELEASE_VERSION, GIT_SHORTREV_HASH != nullptr ? GIT_SHORTREV_HASH : "", aVersionStr); str_format(aMessage, sizeof(aMessage), "An assertion error occured. Please write down or take a screenshot of the following information and report this error.\nPlease also share the assert log which you should find in the 'dumps' folder in your config directory.\n\n%s\n\nPlatform: %s\nGame version: %s %s\nOS version: %s", pMsg, CONF_PLATFORM_STRING, GAME_RELEASE_VERSION, GIT_SHORTREV_HASH != nullptr ? GIT_SHORTREV_HASH : "", aVersionStr);
pClient->ShowMessageBox("Assertion Error", aMessage); pClient->ShowMessageBox("Assertion Error", aMessage);
// Client will crash due to assertion, don't call PerformAllCleanup in this inconsistent state
}); });
// create the components // create the components
@ -4634,6 +4660,7 @@ int main(int argc, const char **argv)
const char *pError = "Failed to initialize the secure RNG."; const char *pError = "Failed to initialize the secure RNG.";
dbg_msg("secure", "%s", pError); dbg_msg("secure", "%s", pError);
pClient->ShowMessageBox("Secure RNG Error", pError); pClient->ShowMessageBox("Secure RNG Error", pError);
PerformAllCleanup();
return -1; return -1;
} }
@ -4668,9 +4695,7 @@ int main(int argc, const char **argv)
const char *pError = "Failed to register an interface."; const char *pError = "Failed to register an interface.";
dbg_msg("client", "%s", pError); dbg_msg("client", "%s", pError);
pClient->ShowMessageBox("Kernel Error", pError); pClient->ShowMessageBox("Kernel Error", pError);
delete pKernel; PerformAllCleanup();
pClient->~CClient();
free(pClient);
return -1; return -1;
} }
} }
@ -4695,6 +4720,7 @@ int main(int argc, const char **argv)
const char *pError = "Failed to load config from '" CONFIG_FILE "'."; const char *pError = "Failed to load config from '" CONFIG_FILE "'.";
dbg_msg("client", "%s", pError); dbg_msg("client", "%s", pError);
pClient->ShowMessageBox("Config File Error", pError); pClient->ShowMessageBox("Config File Error", pError);
PerformAllCleanup();
return -1; return -1;
} }
} }
@ -4763,12 +4789,10 @@ int main(int argc, const char **argv)
str_format(aError, sizeof(aError), "Unable to initialize SDL base: %s", SDL_GetError()); str_format(aError, sizeof(aError), "Unable to initialize SDL base: %s", SDL_GetError());
dbg_msg("client", "%s", aError); dbg_msg("client", "%s", aError);
pClient->ShowMessageBox("SDL Error", aError); pClient->ShowMessageBox("SDL Error", aError);
PerformAllCleanup();
return -1; return -1;
} }
CleanerFunctions.push([]() { SDL_Quit(); });
#ifndef CONF_PLATFORM_ANDROID
atexit(SDL_Quit);
#endif
// run the client // run the client
dbg_msg("client", "starting..."); dbg_msg("client", "starting...");
@ -4781,23 +4805,14 @@ int main(int argc, const char **argv)
pStorage->GetBinaryPath(PLAT_CLIENT_EXEC, aRestartBinaryPath, sizeof(aRestartBinaryPath)); pStorage->GetBinaryPath(PLAT_CLIENT_EXEC, aRestartBinaryPath, sizeof(aRestartBinaryPath));
} }
pClient->~CClient(); PerformCleanup();
free(pClient);
pKernel->Shutdown();
delete pKernel;
SDL_Quit();
NotificationsUninit();
secure_random_uninit();
if(Restarting) if(Restarting)
{ {
shell_execute(aRestartBinaryPath); shell_execute(aRestartBinaryPath);
} }
#ifdef CONF_PLATFORM_ANDROID PerformFinalCleanup();
// properly close this native thread, so globals are destructed
std::exit(0);
#endif
return 0; return 0;
} }