Fix deadlocked application on windows at startup (logs,vsync initialization,thread initialization)

Fix cleaning memory at exit.
This commit is contained in:
Julien Wadel 2024-10-23 17:29:47 +02:00
parent 94cbdbcc0f
commit ea8d2aafe7
8 changed files with 86 additions and 44 deletions

View file

@ -267,10 +267,6 @@ App::App(int &argc, char *argv[])
QCoreApplication::setApplicationVersion(APPLICATION_SEMVER);
// If not OpenGL, createRender is never call.
QQuickWindow::setGraphicsApi(QSGRendererInterface::OpenGL);
// Ignore vertical sync. This way, we avoid blinking on resizes(and other refresh like layouts etc.).
auto ignoreVSync = QSurfaceFormat::defaultFormat();
ignoreVSync.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(ignoreVSync);
setWindowIcon(QIcon(Constants::WindowIconPath));
lInfo() << "Loading Fonts";
QDirIterator it(":/font/", QDirIterator::Subdirectories);
@ -382,9 +378,10 @@ void App::setSelf(QSharedPointer<App>(me)) {
new SafeConnection<App, CliModel>(me, CliModel::getInstance()), &QObject::deleteLater);
mCliModelConnection->makeConnectToCore(&App::receivedMessage, [this](int, const QByteArray &byteArray) {
QString command(byteArray);
if (command.isEmpty())
mCliModelConnection->invokeToModel([command]() { CliModel::getInstance()->runProcess(); });
else {
if (command.isEmpty()) {
lDebug() << log().arg("Check with CliModel for commands");
mCliModelConnection->invokeToModel([]() { CliModel::getInstance()->runProcess(); });
} else {
qInfo() << QStringLiteral("Received command from other application: `%1`.").arg(command);
mCliModelConnection->invokeToModel([command]() { CliModel::getInstance()->executeCommand(command); });
}
@ -398,6 +395,10 @@ App *App::getInstance() {
return dynamic_cast<App *>(QApplication::instance());
}
QThread *App::getLinphoneThread() {
return App::getInstance()->mLinphoneThread;
}
Notifier *App::getNotifier() const {
return mNotifier;
}
@ -415,8 +416,10 @@ void App::init() {
mParser->process(*this);
if (!mLinphoneThread->isRunning()) {
lDebug() << log().arg("Starting Thread");
lInfo() << log().arg("Starting Thread");
mLinphoneThread->start();
while (!mLinphoneThread->getThreadId()) // Wait for running thread
processEvents();
}
lInfo() << log().arg("Display server : %1").arg(platformName());
@ -430,10 +433,14 @@ void App::initCore() {
QMetaObject::invokeMethod(
mLinphoneThread->getThreadId(),
[this]() mutable {
lInfo() << log().arg("Starting Core");
CoreModel::getInstance()->start();
auto coreStarted = CoreModel::getInstance()->getCore()->getGlobalState() == linphone::GlobalState::On;
lDebug() << log().arg("Creating SettingsModel");
SettingsModel::create();
lDebug() << log().arg("Creating SettingsCore");
auto settings = SettingsCore::create();
lDebug() << log().arg("Creating Ui");
QMetaObject::invokeMethod(App::getInstance()->thread(), [this, settings, coreStarted] {
// QML
mEngine = new QQmlApplicationEngine(this);
@ -490,6 +497,22 @@ void App::initCore() {
Qt::UniqueConnection);
setLocale(settings->getConfigLocale());
if (mSettings) {
mEngine->setObjectOwnership(mSettings.get(), QQmlEngine::CppOwnership);
setAutoStart(mSettings->getAutoStart());
setQuitOnLastWindowClosed(mSettings->getExitOnClose());
connect(mSettings.get(), &SettingsCore::exitOnCloseChanged, this, &App::onExitOnCloseChanged,
Qt::UniqueConnection);
setLocale(mSettings->getConfigLocale());
QObject::connect(mSettings.get(), &SettingsCore::autoStartChanged, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
setAutoStart(mSettings->getAutoStart());
});
QObject::connect(mSettings.get(), &SettingsCore::configLocaleChanged, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
if (mSettings) setLocale(mSettings->getConfigLocale());
});
}
const QUrl url(u"qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml"_qs);
QObject::connect(
mEngine, &QQmlApplicationEngine::objectCreated, this,
@ -510,23 +533,16 @@ void App::initCore() {
#endif // ifndef __APPLE__
static bool firstOpen = true;
if (!firstOpen || !mParser->isSet("minimized")) {
lDebug() << log().arg("Openning window");
window->show();
}
} else lInfo() << log().arg("Stay minimized");
firstOpen = false;
}
},
Qt::QueuedConnection);
QObject::connect(mSettings.get(), &SettingsCore::autoStartChanged, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
setAutoStart(mSettings->getAutoStart());
});
QObject::connect(mSettings.get(), &SettingsCore::configLocaleChanged, [this]() {
mustBeInMainThread(log().arg(Q_FUNC_INFO));
setLocale(mSettings->getConfigLocale());
});
mEngine->load(url);
});
// coreModel.reset();
},
Qt::BlockingQueuedConnection);
}
@ -628,9 +644,12 @@ void App::clean() {
delete mEngine;
}
mEngine = nullptr;
mSettings = nullptr; // Need it because of SettingsModel singleton for letting thread to remove it.
// Wait 500ms to let time for log te be stored.
// mNotifier destroyed in mEngine deletion as it is its parent
qApp->processEvents(QEventLoop::AllEvents, 500);
// Hack: exec() must be used to process cleaning QSharedPointers memory. processEvents doesn't work.
QTimer::singleShot(500, [this]() { exit(0); });
exec();
if (mLinphoneThread) {
mLinphoneThread->exit();
mLinphoneThread->wait();
@ -698,7 +717,8 @@ void App::sendCommand() {
receivedMessage(0, i.toLocal8Bit());
}
}
} else if (isPrimary()) { // Run waiting process
} else if (isPrimary()) {
lDebug() << "Run waiting process";
receivedMessage(0, "");
}
}
@ -825,8 +845,8 @@ QSharedPointer<SettingsCore> App::getSettings() const {
void App::onExitOnCloseChanged() {
setSysTrayIcon(); // Restore button depends from this option
setQuitOnLastWindowClosed(mSettings->getExitOnClose());
setAutoStart(mSettings->getAutoStart());
if (mSettings) setQuitOnLastWindowClosed(mSettings->getExitOnClose());
if (mSettings) setAutoStart(mSettings->getAutoStart());
}
#ifdef Q_OS_LINUX
@ -952,7 +972,7 @@ void App::setSysTrayIcon() {
// trayIcon: Right click actions.
QAction *restoreAction = nullptr;
if (!mSettings->getExitOnClose()) {
if (mSettings && !mSettings->getExitOnClose()) {
restoreAction = new QAction(root);
auto setRestoreActionText = [restoreAction](bool visible) {
restoreAction->setText(visible ? tr("Cacher") : tr("Afficher"));

View file

@ -46,6 +46,7 @@ public:
~App();
void setSelf(QSharedPointer<App>(me));
static App *getInstance();
static QThread *getLinphoneThread();
Notifier *getNotifier() const;
// App::postModelAsync(<lambda>) => run lambda in model thread and continue.

View file

@ -313,8 +313,8 @@ void SettingsCore::setSelf(QSharedPointer<SettingsCore> me) {
HideSettings)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
hideAccountSettings, HideAccountSettings)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
hideFps, HideFps)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool, hideFps,
HideFps)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,
disableCallRecordings, DisableCallRecordings)
DEFINE_CORE_GETSET_CONNECT(mSettingsModelConnection, SettingsCore, SettingsModel, settingsModel, bool,

View file

@ -1,12 +1,13 @@
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QLocale>
#include <QTranslator>
#include <qloggingcategory.h>
#include "core/App.hpp"
#include "core/logger/QtLogger.hpp"
#include <QLocale>
#include <QSurfaceFormat>
#include <QTranslator>
#include <iostream>
#include <qloggingcategory.h>
#ifdef QT_QML_DEBUG
#include <QQmlDebuggingEnabler>
#endif
@ -36,8 +37,12 @@ void cleanStream() {
}
#endif
}
void fallbackLog(QtMsgType type, const QMessageLogContext &context, const QString &msg) {
QString message = QtLogger::formatLog(context.file, context.line, msg);
std::cout << message.toStdString() << std::endl;
}
int main(int argc, char *argv[]) {
qInstallMessageHandler(fallbackLog); // Use for messages prior QCoreApplication generation.
/*
#if defined _WIN32
// log in console only if launched from console
@ -48,37 +53,52 @@ int main(int argc, char *argv[]) {
#endif
*/
// Useful to share camera on Fullscreen (other context) or multiscreens
qDebug() << "[Main] Setting ShareOpenGLContexts";
QApplication::setAttribute(Qt::AA_ShareOpenGLContexts);
qDebug() << "[Main] Disabling VSync";
// Ignore vertical sync. This way, we avoid blinking on resizes(and other refresh like layouts etc.).
auto ignoreVSync = QSurfaceFormat::defaultFormat();
ignoreVSync.setSwapInterval(0);
QSurfaceFormat::setDefaultFormat(ignoreVSync);
// Disable QML cache. Avoid malformed cache.
qDebug() << "[Main] Disabling QML disk cache";
qputenv("QML_DISABLE_DISK_CACHE", "true");
qDebug() << "[Main] Setting application to UTF8";
setlocale(LC_CTYPE, ".UTF8");
qDebug() << "[Main] Creating application";
auto app = QSharedPointer<App>::create(argc, argv);
#ifdef ACCESSBILITY_WORKAROUND
QAccessible::installUpdateHandler(DummyUpdateHandler);
QAccessible::installRootObjectHandler(DummyRootObjectHandler);
#endif
if (app->isSecondary()) {
qDebug() << "[Main] Sending command from secondary application";
app->sendCommand();
qInfo() << QStringLiteral("Running secondary app success. Kill it now.");
qInfo() << QStringLiteral("[Main] Running secondary app success. Kill it now.");
app->clean();
cleanStream();
return EXIT_SUCCESS;
} else {
qDebug() << "[Main] Initializing core for primary application";
app->initCore();
qDebug() << "[Main] Preparing application's connections";
app->setSelf(app);
}
int result = 0;
do {
qDebug() << "[Main] Sending command from primary application";
app->sendCommand();
qInfo() << "[Main] Running application";
result = app->exec();
} while (result == (int)App::StatusCode::gRestartCode);
qWarning() << "[Main] Exiting app with the code : " << result;
QString message = "[Main] Exiting app with the code : " + QString::number(result);
if (result) qInfo() << message;
else qWarning() << message;
app->clean();
app = nullptr;
cleanStream();
return result;
}

View file

@ -395,9 +395,10 @@ void CliModel::addProcess(ProcessCommand process) {
void CliModel::runProcess() {
if (mQueue.size() > 0) {
lInfo() << log().arg("Processing command from queue");
mQueue.first().run();
mQueue.pop_front();
}
} else lInfo() << log().arg("Queue is empty. Nothing to do.");
}
void CliModel::resetProcesses() {

View file

@ -31,13 +31,13 @@ DEFINE_ABSTRACT_OBJECT(SettingsModel)
using namespace std;
const std::string SettingsModel::UiSection("ui");
std::shared_ptr<SettingsModel> SettingsModel::gCoreModel;
std::shared_ptr<SettingsModel> SettingsModel::gSettingsModel;
SettingsModel::SettingsModel() {
mustBeInLinphoneThread(log().arg(Q_FUNC_INFO));
connect(CoreModel::getInstance()->thread(), &QThread::finished, this, [this]() {
// Model thread
gCoreModel = nullptr;
gSettingsModel = nullptr;
});
auto core = CoreModel::getInstance()->getCore();
mConfig = core->getConfig();
@ -69,13 +69,14 @@ SettingsModel::~SettingsModel() {
}
shared_ptr<SettingsModel> SettingsModel::create() {
// auto model = Utils::makeQObject_ptr<SettingsModel>();
auto model = make_shared<SettingsModel>();
gCoreModel = model;
gSettingsModel = model;
return model;
}
shared_ptr<SettingsModel> SettingsModel::getInstance() {
return gCoreModel;
return gSettingsModel;
}
bool SettingsModel::isReadOnly(const std::string &section, const std::string &name) const {

View file

@ -196,7 +196,7 @@ private:
MediastreamerUtils::SimpleCaptureGraph *mSimpleCaptureGraph = nullptr;
int mCaptureGraphListenerCount = 0;
static std::shared_ptr<SettingsModel> gCoreModel;
static std::shared_ptr<SettingsModel> gSettingsModel;
DECLARE_ABSTRACT_OBJECT
};

View file

@ -20,16 +20,15 @@
#include "Thread.hpp"
#include "core/App.hpp"
#include "model/core/CoreModel.hpp"
#include <QDebug>
Thread::Thread(QObject *parent) : QThread(parent) {
}
void Thread::run() {
mThreadId = new QObject();
setlocale(LC_CTYPE, ".UTF8");
int toExit = false;
mThreadId = new QObject();
while (!toExit) {
int result = exec();
if (result <= 0) toExit = true;
@ -44,7 +43,7 @@ QObject *Thread::getThreadId() {
}
bool Thread::isInLinphoneThread() {
return CoreModel::getInstance() && QThread::currentThread() == CoreModel::getInstance()->thread();
return QThread::currentThread() == App::getLinphoneThread();
}
bool Thread::mustBeInLinphoneThread(const QString &context) {