mirror of
https://gitlab.linphone.org/BC/public/linphone-desktop.git
synced 2026-01-18 03:48:09 +00:00
Fix deadlocked application on windows at startup (logs,vsync initialization,thread initialization)
Fix cleaning memory at exit.
This commit is contained in:
parent
94cbdbcc0f
commit
ea8d2aafe7
8 changed files with 86 additions and 44 deletions
|
|
@ -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"));
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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 §ion, const std::string &name) const {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue