Fix crash when passing --checkMinSpec flag

That flag caused a DLL to be loaded before Application was
	instanced.
	This triggers a Qt bug inside Q_COREAPP_STARTUP_FUNC that causes
	the previous registration pointing the startup function in
	the main executable to be overridden with the address of the
	function in the DLL (Since they both link the same static
	library)
	This leads to the correct function running in the wrong address
	space (the DLLs), hence not initializing some global variables
	correctly.
This commit is contained in:
Atlante45 2018-02-02 11:28:25 -08:00
parent 9ba1532c26
commit 80c0f2a21e
10 changed files with 68 additions and 73 deletions

View file

@ -23,9 +23,12 @@ int main(int argc, char* argv[]) {
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupGlobalInstances();
qInstallMessageHandler(LogHandler::verboseMessageHandler);
qInfo() << "Starting.";
AssignmentClientApp app(argc, argv);
int acReturn = app.exec();

View file

@ -29,6 +29,8 @@ int main(int argc, char* argv[]) {
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupGlobalInstances();
Setting::init();
#ifndef WIN32

View file

@ -62,6 +62,8 @@ int main(int argc, const char* argv[]) {
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupGlobalInstances();
Setting::init();
// Instance UserActivityLogger now that the settings are loaded

View file

@ -23,43 +23,53 @@
#include "SharedUtil.h"
namespace Setting {
static QSharedPointer<Manager> globalManager;
// cleans up the settings private instance. Should only be run once at closing down.
void cleanupPrivateInstance() {
auto globalManager = DependencyManager::get<Manager>();
Q_ASSERT(qApp && globalManager);
// grab the thread before we nuke the instance
QThread* settingsManagerThread = DependencyManager::get<Manager>()->thread();
QThread* settingsManagerThread = globalManager->thread();
// tell the private instance to clean itself up on its thread
DependencyManager::destroy<Manager>();
globalManager.reset();
// quit the settings manager thread and wait on it to make sure it's gone
// quit the settings manager thread
settingsManagerThread->quit();
settingsManagerThread->wait();
// Save all settings
globalManager->saveAll();
qCDebug(shared) << "Settings thread stopped.";
}
void setupPrivateInstance() {
// Ensure Setting::init has already ran and qApp exists
if (qApp && globalManager) {
// Let's set up the settings Private instance on its own thread
QThread* thread = new QThread();
Q_CHECK_PTR(thread);
thread->setObjectName("Settings Thread");
auto globalManager = DependencyManager::get<Manager>();
Q_ASSERT(qApp && globalManager);
QObject::connect(thread, SIGNAL(started()), globalManager.data(), SLOT(startTimer()));
QObject::connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
QObject::connect(thread, SIGNAL(finished()), globalManager.data(), SLOT(deleteLater()));
globalManager->moveToThread(thread);
thread->start();
qCDebug(shared) << "Settings thread started.";
// Let's set up the settings private instance on its own thread
QThread* thread = new QThread(qApp);
Q_CHECK_PTR(thread);
thread->setObjectName("Settings Thread");
// Register cleanupPrivateInstance to run inside QCoreApplication's destructor.
qAddPostRoutine(cleanupPrivateInstance);
}
// Setup setting periodical save timer
QObject::connect(thread, &QThread::started, globalManager.data(), &Manager::startTimer);
QObject::connect(thread, &QThread::finished, globalManager.data(), &Manager::stopTimer);
// Setup manager threading affinity
globalManager->moveToThread(thread);
QObject::connect(thread, &QThread::finished, globalManager.data(), [] {
auto globalManager = DependencyManager::get<Manager>();
Q_ASSERT(qApp && globalManager);
// Move manager back to the main thread (has to be done on owning thread)
globalManager->moveToThread(qApp->thread());
});
thread->start();
qCDebug(shared) << "Settings thread started.";
// Register cleanupPrivateInstance to run inside QCoreApplication's destructor.
qAddPostRoutine(cleanupPrivateInstance);
}
FIXED_Q_COREAPP_STARTUP_FUNCTION(setupPrivateInstance)
// Sets up the settings private instance. Should only be run once at startup. preInit() must be run beforehand,
void init() {
@ -68,6 +78,7 @@ namespace Setting {
QSettings settings;
qCDebug(shared) << "Settings file:" << settings.fileName();
// Backward compatibility for old settings file
if (settings.allKeys().isEmpty()) {
loadOldINIFile(settings);
}
@ -80,11 +91,13 @@ namespace Setting {
qCDebug(shared) << (deleted ? "Deleted" : "Failed to delete") << "settings lock file" << settingsLockFilename;
}
globalManager = DependencyManager::set<Manager>();
// Setup settings manager
DependencyManager::set<Manager>();
setupPrivateInstance();
// Add pre-routine to setup threading
qAddPreRoutine(setupPrivateInstance);
}
void Interface::init() {
if (!DependencyManager::isSet<Manager>()) {
// WARNING: As long as we are using QSettings this should always be triggered for each Setting::Handle

View file

@ -23,11 +23,7 @@ namespace Setting {
// Cleanup timer
stopTimer();
delete _saveTimer;
// Save all settings before exit
saveAll();
// sync will be called in the QSettings destructor
_saveTimer = nullptr;
}
// Custom deleter does nothing, because we need to shutdown later than the dependency manager

View file

@ -63,12 +63,12 @@ extern "C" FILE * __cdecl __iob_func(void) {
#include "OctalCode.h"
#include "SharedLogging.h"
static std::mutex stagedGlobalInstancesMutex;
static std::unordered_map<std::string, QVariant> stagedGlobalInstances;
std::mutex& globalInstancesMutex() {
static std::mutex mutex;
return mutex;
return stagedGlobalInstancesMutex;
}
static void commitGlobalInstances() {
@ -78,7 +78,10 @@ static void commitGlobalInstances() {
}
stagedGlobalInstances.clear();
}
FIXED_Q_COREAPP_STARTUP_FUNCTION(commitGlobalInstances)
void setupGlobalInstances() {
qAddPreRoutine(commitGlobalInstances);
}
QVariant getGlobalInstance(const char* propertyName) {
if (qApp) {

View file

@ -25,23 +25,6 @@
#include <QtCore/QCoreApplication>
#include <QUuid>
// Workaround for https://bugreports.qt.io/browse/QTBUG-54479
// Wrap target function inside another function that holds
// a unique string identifier and uses it to ensure it only runs once
// by storing a state within the qApp
// We cannot used std::call_once with a static once_flag because
// this is used in shared libraries that are linked by several DLLs
// (ie. plugins), meaning the static will be useless in that case
#define FIXED_Q_COREAPP_STARTUP_FUNCTION(AFUNC) \
static void AFUNC ## _fixed() { \
const auto propertyName = std::string(Q_FUNC_INFO) + __FILE__; \
if (!qApp->property(propertyName.c_str()).toBool()) { \
AFUNC(); \
qApp->setProperty(propertyName.c_str(), QVariant(true)); \
} \
} \
Q_COREAPP_STARTUP_FUNCTION(AFUNC ## _fixed)
// When writing out avatarEntities to a QByteArray, if the parentID is the ID of MyAvatar, use this ID instead. This allows
// the value to be reset when the sessionID changes.
const QUuid AVATAR_SELF_ID = QUuid("{00000000-0000-0000-0000-000000000001}");
@ -53,21 +36,7 @@ std::unique_ptr<T>& globalInstancePointer() {
return instancePtr;
}
template <typename T>
void setGlobalInstance(const char* propertyName, T* instance) {
globalInstancePointer<T>().reset(instance);
}
template <typename T>
bool destroyGlobalInstance() {
std::unique_ptr<T>& instancePtr = globalInstancePointer<T>();
if (instancePtr.get()) {
instancePtr.reset();
return true;
}
return false;
}
void setupGlobalInstances();
std::mutex& globalInstancesMutex();
QVariant getGlobalInstance(const char* propertyName);
void setGlobalInstance(const char* propertyName, const QVariant& variant);
@ -78,7 +47,6 @@ void setGlobalInstance(const char* propertyName, const QVariant& variant);
template <typename T, typename... Args>
T* globalInstance(const char* propertyName, Args&&... args) {
static T* resultInstance { nullptr };
static std::mutex mutex;
if (!resultInstance) {
std::unique_lock<std::mutex> lock(globalInstancesMutex());
if (!resultInstance) {

View file

@ -19,15 +19,16 @@
using namespace std;
int main(int argc, char * argv[]) {
int main(int argc, char* argv[]) {
QCoreApplication::setApplicationName(BuildInfo::AC_CLIENT_SERVER_NAME);
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupGlobalInstances();
Setting::init();
ACClientApp app(argc, argv);
return app.exec();
}

View file

@ -25,9 +25,10 @@ int main(int argc, char * argv[]) {
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupGlobalInstances();
Setting::init();
ATPClientApp app(argc, argv);
return app.exec();
}

View file

@ -10,11 +10,17 @@
#include "Oven.h"
#include <BuildInfo.h>
#include <SettingInterface.h>
#include <SharedUtil.h>
int main (int argc, char** argv) {
QCoreApplication::setOrganizationName("High Fidelity");
QCoreApplication::setApplicationName("Oven");
QCoreApplication::setOrganizationName(BuildInfo::MODIFIED_ORGANIZATION);
QCoreApplication::setOrganizationDomain(BuildInfo::ORGANIZATION_DOMAIN);
QCoreApplication::setApplicationVersion(BuildInfo::VERSION);
setupGlobalInstances();
// init the settings interface so we can save and load settings
Setting::init();