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

View file

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

View file

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

View file

@ -1,12 +1,13 @@
#include <QApplication> #include <QApplication>
#include <QQmlApplicationEngine> #include <QQmlApplicationEngine>
#include <QLocale>
#include <QTranslator>
#include <qloggingcategory.h>
#include "core/App.hpp" #include "core/App.hpp"
#include "core/logger/QtLogger.hpp"
#include <QLocale>
#include <QSurfaceFormat>
#include <QTranslator>
#include <iostream>
#include <qloggingcategory.h>
#ifdef QT_QML_DEBUG #ifdef QT_QML_DEBUG
#include <QQmlDebuggingEnabler> #include <QQmlDebuggingEnabler>
#endif #endif
@ -36,8 +37,12 @@ void cleanStream() {
} }
#endif #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[]) { int main(int argc, char *argv[]) {
qInstallMessageHandler(fallbackLog); // Use for messages prior QCoreApplication generation.
/* /*
#if defined _WIN32 #if defined _WIN32
// log in console only if launched from console // log in console only if launched from console
@ -48,37 +53,52 @@ int main(int argc, char *argv[]) {
#endif #endif
*/ */
// Useful to share camera on Fullscreen (other context) or multiscreens // Useful to share camera on Fullscreen (other context) or multiscreens
qDebug() << "[Main] Setting ShareOpenGLContexts";
QApplication::setAttribute(Qt::AA_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. // Disable QML cache. Avoid malformed cache.
qDebug() << "[Main] Disabling QML disk cache";
qputenv("QML_DISABLE_DISK_CACHE", "true"); qputenv("QML_DISABLE_DISK_CACHE", "true");
qDebug() << "[Main] Setting application to UTF8";
setlocale(LC_CTYPE, ".UTF8"); setlocale(LC_CTYPE, ".UTF8");
qDebug() << "[Main] Creating application";
auto app = QSharedPointer<App>::create(argc, argv); auto app = QSharedPointer<App>::create(argc, argv);
#ifdef ACCESSBILITY_WORKAROUND #ifdef ACCESSBILITY_WORKAROUND
QAccessible::installUpdateHandler(DummyUpdateHandler); QAccessible::installUpdateHandler(DummyUpdateHandler);
QAccessible::installRootObjectHandler(DummyRootObjectHandler); QAccessible::installRootObjectHandler(DummyRootObjectHandler);
#endif #endif
if (app->isSecondary()) { if (app->isSecondary()) {
qDebug() << "[Main] Sending command from secondary application";
app->sendCommand(); app->sendCommand();
qInfo() << QStringLiteral("Running secondary app success. Kill it now."); qInfo() << QStringLiteral("[Main] Running secondary app success. Kill it now.");
app->clean(); app->clean();
cleanStream(); cleanStream();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} else { } else {
qDebug() << "[Main] Initializing core for primary application";
app->initCore(); app->initCore();
qDebug() << "[Main] Preparing application's connections";
app->setSelf(app); app->setSelf(app);
} }
int result = 0; int result = 0;
do { do {
qDebug() << "[Main] Sending command from primary application";
app->sendCommand(); app->sendCommand();
qInfo() << "[Main] Running application";
result = app->exec(); result = app->exec();
} while (result == (int)App::StatusCode::gRestartCode); } 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->clean();
app = nullptr; app = nullptr;
cleanStream();
return result; return result;
} }

View file

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

View file

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

View file

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

View file

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