From 8577b00cb7d541440b692f97b5ddf237b9864c05 Mon Sep 17 00:00:00 2001 From: Berthold Stoeger Date: Sat, 12 Mar 2022 18:30:23 +0100 Subject: [PATCH] core: add class that collects global objects to be deleted on exit We have a prevailing problem with global QObjects defined as static global variables. These get destructed after main() exits, which means that the QApplication object does not exist anymore. This more often than not leads to crashes. In a quick search I didn't find a mechanism to register objects for deletion with QApplication. Therefore, let's do our own list of global objects that get destructed before destroying the QApplication. Signed-off-by: Berthold Stoeger --- Subsurface-mobile.pro | 2 ++ core/CMakeLists.txt | 2 ++ core/globals.cpp | 21 ++++++++++++++++++ core/globals.h | 50 +++++++++++++++++++++++++++++++++++++++++++ subsurface-helper.cpp | 2 ++ 5 files changed, 77 insertions(+) create mode 100644 core/globals.cpp create mode 100644 core/globals.h diff --git a/Subsurface-mobile.pro b/Subsurface-mobile.pro index e5c5d6767..b910be2db 100644 --- a/Subsurface-mobile.pro +++ b/Subsurface-mobile.pro @@ -57,6 +57,7 @@ SOURCES += subsurface-mobile-main.cpp \ core/gas-model.c \ core/gaspressures.c \ core/git-access.c \ + core/globals.cpp \ core/liquivision.c \ core/load-git.c \ core/parse-xml.c \ @@ -201,6 +202,7 @@ HEADERS += \ core/event.h \ core/extradata.h \ core/git-access.h \ + core/globals.h \ core/pref.h \ core/profile.h \ core/qthelper.h \ diff --git a/core/CMakeLists.txt b/core/CMakeLists.txt index 9dc2ddb8b..2fe7a7707 100644 --- a/core/CMakeLists.txt +++ b/core/CMakeLists.txt @@ -105,6 +105,8 @@ set(SUBSURFACE_CORE_LIB_SRCS gettextfromc.h git-access.c git-access.h + globals.cpp + globals.h imagedownloader.cpp imagedownloader.h import-cobalt.c diff --git a/core/globals.cpp b/core/globals.cpp new file mode 100644 index 000000000..1516c5a5a --- /dev/null +++ b/core/globals.cpp @@ -0,0 +1,21 @@ +// SPDX-License-Identifier: GPL-2.0 +#include "globals.h" + +#include + +static std::vector global_objects; + +void register_global_internal(GlobalObjectBase *o) +{ + global_objects.push_back(o); +} + +void free_globals() +{ + // We free the objects by hand, so that we can free them in reverse + // order of creation. AFAIK, order-of-destruction is implementantion-defined + // for std::vector<>. + for (auto it = global_objects.rbegin(); it != global_objects.rend(); ++it) + delete *it; + global_objects.clear(); +} diff --git a/core/globals.h b/core/globals.h new file mode 100644 index 000000000..524c0c8fb --- /dev/null +++ b/core/globals.h @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0 +// Collection of objects that will be deleted on application exit. +// This feature is needed because many Qt-objects crash if freed +// after the application has exited. +#ifndef GLOBALS_H +#define GLOBALS_H + +#include +#include + +template +T *make_global(Args &&...args); // construct a global object of type T. + +template +T *register_global(T *); // register an already constructed object. returns input. + +void free_globals(); // call on application exit. frees all global objects. + +// Implementation + +// A class with a virtual destructor that will be used to destruct the objects. +struct GlobalObjectBase +{ + virtual ~GlobalObjectBase() { } +}; + +template +struct GlobalObject : T, GlobalObjectBase +{ + using T::T; // Inherit constructor from actual object. +}; + +void register_global_internal(GlobalObjectBase *); + +template +T *make_global(Args &&...args) +{ + GlobalObject *res = new GlobalObject(std::forward(args)...); + register_global_internal(res); + return res; +} + +template +T *register_global(T *o) +{ + make_global>(o); + return o; +} + +#endif diff --git a/subsurface-helper.cpp b/subsurface-helper.cpp index 193a472b5..e0d8f18d6 100644 --- a/subsurface-helper.cpp +++ b/subsurface-helper.cpp @@ -8,6 +8,7 @@ #endif #include "stats/statsview.h" +#include "core/globals.h" #include "core/qt-gui.h" #include "core/settings/qPref.h" #include "core/ssrf.h" @@ -67,6 +68,7 @@ void exit_ui() #ifndef SUBSURFACE_MOBILE delete MainWindow::instance(); #endif // SUBSURFACE_MOBILE + free_globals(); free((void *)existing_filename); }