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 <new>
#include <stack>
#include <tuple>
#include <base/hash.h>
@ -4547,7 +4548,6 @@ int main(int argc, const char **argv)
CCmdlineFix CmdlineFix(&argc, &argv);
bool Silent = false;
bool RandInitFailed = false;
for(int i = 1; i < argc; i++)
{
@ -4582,17 +4582,42 @@ int main(int argc, const char **argv)
vpLoggers.push_back(pFutureAssertionLogger);
log_set_global_logger(log_logger_collection(std::move(vpLoggers)).release());
if(secure_random_init() != 0)
{
RandInitFailed = true;
}
std::stack<std::function<void()>> CleanerFunctions;
std::function<void()> PerformCleanup = [&CleanerFunctions]() mutable {
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();
CleanerFunctions.push([]() { NotificationsUninit(); });
CClient *pClient = CreateClient();
IKernel *pKernel = IKernel::Create();
pKernel->RegisterInterface(pClient, false);
pClient->RegisterInterfaces();
CleanerFunctions.push([pKernel, pClient]() {
pKernel->Shutdown();
delete pKernel;
pClient->~CClient();
free(pClient);
});
const std::thread::id MainThreadId = std::this_thread::get_id();
dbg_assert_set_handler([MainThreadId, pClient](const char *pMsg) {
@ -4604,6 +4629,7 @@ int main(int argc, const char **argv)
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);
pClient->ShowMessageBox("Assertion Error", aMessage);
// Client will crash due to assertion, don't call PerformAllCleanup in this inconsistent state
});
// create the components
@ -4634,6 +4660,7 @@ int main(int argc, const char **argv)
const char *pError = "Failed to initialize the secure RNG.";
dbg_msg("secure", "%s", pError);
pClient->ShowMessageBox("Secure RNG Error", pError);
PerformAllCleanup();
return -1;
}
@ -4668,9 +4695,7 @@ int main(int argc, const char **argv)
const char *pError = "Failed to register an interface.";
dbg_msg("client", "%s", pError);
pClient->ShowMessageBox("Kernel Error", pError);
delete pKernel;
pClient->~CClient();
free(pClient);
PerformAllCleanup();
return -1;
}
}
@ -4695,6 +4720,7 @@ int main(int argc, const char **argv)
const char *pError = "Failed to load config from '" CONFIG_FILE "'.";
dbg_msg("client", "%s", pError);
pClient->ShowMessageBox("Config File Error", pError);
PerformAllCleanup();
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());
dbg_msg("client", "%s", aError);
pClient->ShowMessageBox("SDL Error", aError);
PerformAllCleanup();
return -1;
}
#ifndef CONF_PLATFORM_ANDROID
atexit(SDL_Quit);
#endif
CleanerFunctions.push([]() { SDL_Quit(); });
// run the client
dbg_msg("client", "starting...");
@ -4781,23 +4805,14 @@ int main(int argc, const char **argv)
pStorage->GetBinaryPath(PLAT_CLIENT_EXEC, aRestartBinaryPath, sizeof(aRestartBinaryPath));
}
pClient->~CClient();
free(pClient);
pKernel->Shutdown();
delete pKernel;
SDL_Quit();
NotificationsUninit();
secure_random_uninit();
PerformCleanup();
if(Restarting)
{
shell_execute(aRestartBinaryPath);
}
#ifdef CONF_PLATFORM_ANDROID
// properly close this native thread, so globals are destructed
std::exit(0);
#endif
PerformFinalCleanup();
return 0;
}