Skip to content
This repository has been archived by the owner on Feb 12, 2023. It is now read-only.

Commit

Permalink
fix: Various IPC event handling and related bugs on startup
Browse files Browse the repository at this point in the history
Fixes #1926 : When an IPC event was processed locally, if the window was closed before the core could start, the event handler would be forever stuck in the background waiting for the core to start. We fix this by substituting QApplication::quit() by a Nexus::quit() function and a Nexus::isRunning() function, which gives us a condition for exiting blocking processEvents() loops. We cannot simply use QApplication::quit(), because this function has no effect before the start of the event loop.

The problem was further exacerbated by the Tox URI event handler being (incorrectly) blocking. The IPC owner would block in this event handler, and the sender of the event would give up waiting and process the event itself a second time, potentially triggering the first bug. We fix the event handlers accordingly to be (mostly) non-blocking.

Also fixes a related deadlock between ~Core and ~Profile in the case of an early exit
  • Loading branch information
tux3 committed Feb 17, 2017
1 parent 1d307bc commit c75ee8a
Show file tree
Hide file tree
Showing 11 changed files with 122 additions and 26 deletions.
9 changes: 6 additions & 3 deletions src/core/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,13 @@ Core::~Core()
Q_ARG(bool, false));
}
coreThread->exit(0);
while (coreThread->isRunning())
if (QThread::currentThread() != coreThread)
{
qApp->processEvents();
coreThread->wait(500);
while (coreThread->isRunning())
{
qApp->processEvents();
coreThread->wait(500);
}
}

deadifyTox();
Expand Down
4 changes: 4 additions & 0 deletions src/ipc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,10 @@ bool IPC::isCurrentOwner()
}
}

/**
* @brief Register a handler for an IPC event
* @param handler The handler callback. Should not block for more than a second, at worst
*/
void IPC::registerEventHandler(const QString &name, IPCEventHandler handler)
{
eventHandlers[name] = handler;
Expand Down
9 changes: 6 additions & 3 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -295,16 +295,19 @@ int main(int argc, char *argv[])
}
}

Nexus::getInstance().start();
Nexus& nexus = Nexus::getInstance();
nexus.start();

// Event was not handled by already running instance therefore we handle it ourselves
if (eventType == "uri")
handleToxURI(firstParam.toUtf8());
else if (eventType == "save")
handleToxSave(firstParam.toUtf8());

// Run
int errorcode = a.exec();
// Run (unless we already quit before starting!)
int errorcode = 0;
if (nexus.isRunning())
errorcode = a.exec();

Nexus::destroyInstance();
CameraSource::destroyInstance();
Expand Down
33 changes: 25 additions & 8 deletions src/net/toxuri.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,25 @@ bool toxURIEventHandler(const QByteArray& eventData)
*/
bool handleToxURI(const QString &toxURI)
{
Core* core = Core::getInstance();
Nexus& nexus = Nexus::getInstance();
Core* core = nexus.getCore();

while (!core)
{
core = Core::getInstance();
if (!nexus.isRunning())
return false;

core = nexus.getCore();
qApp->processEvents();
}

while (!core->isReady())
{
if (!nexus.isRunning())
return false;

qApp->processEvents();
}

QString toxaddr = toxURI.mid(4);

Expand All @@ -70,18 +79,26 @@ bool handleToxURI(const QString &toxURI)
toxId = Toxme::lookup(toxaddr);
if (!toxId.isValid())
{
QMessageBox::warning(0, "qTox",
ToxURIDialog::tr("%1 is not a valid Toxme address.")
.arg(toxaddr));
QMessageBox *messageBox = new QMessageBox(QMessageBox::Warning, "qTox",
QMessageBox::tr("%1 is not a valid Toxme address.")
.arg(toxaddr), QMessageBox::Ok, nullptr);
messageBox->setButtonText(QMessageBox::Ok, QMessageBox::tr("Ok"));
QObject::connect(messageBox, &QMessageBox::finished, messageBox, &QMessageBox::deleteLater);
messageBox->show();
return false;
}
}

ToxURIDialog dialog(0, toxaddr, QObject::tr("%1 here! Tox me maybe?",
ToxURIDialog *dialog = new ToxURIDialog(0, toxaddr, QObject::tr("%1 here! Tox me maybe?",
"Default message in Tox URI friend requests. Write something appropriate!")
.arg(Nexus::getCore()->getUsername()));
if (dialog.exec() == QDialog::Accepted)
Core::getInstance()->requestFriendship(toxId, dialog.getRequestMessage());
QObject::connect(dialog, &ToxURIDialog::finished, [=](int result) {
if (result == QDialog::Accepted)
Core::getInstance()->requestFriendship(toxId, dialog->getRequestMessage());

dialog->deleteLater();
});
dialog->open();

return true;
}
Expand Down
45 changes: 42 additions & 3 deletions src/nexus.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,20 @@ Nexus::Nexus(QObject *parent) :
QObject(parent),
profile{nullptr},
widget{nullptr},
loginScreen{nullptr}
loginScreen{nullptr},
running{true},
quitOnLastWindowClosed{true}
{
}

Nexus::~Nexus()
{
delete widget;
widget = nullptr;
delete loginScreen;
loginScreen = nullptr;
delete profile;
profile = nullptr;
Settings::getInstance().saveGlobal();
#ifdef Q_OS_MAC
delete globalMenuBar;
Expand Down Expand Up @@ -104,6 +109,14 @@ void Nexus::start()

loginScreen = new LoginScreen();

// We need this LastWindowClosed dance because the LoginScreen may be shown
// and closed in a processEvents() loop before the start of the real
// exec() event loop, meaning we wouldn't receive the onLastWindowClosed,
// and so we wouldn't have a chance to tell the processEvents() loop to quit.
qApp->setQuitOnLastWindowClosed(false);
connect(qApp, &QApplication::lastWindowClosed, this, &Nexus::onLastWindowClosed);
connect(loginScreen, &LoginScreen::closed, this, &Nexus::onLastWindowClosed);

#ifdef Q_OS_MAC
globalMenuBar = new QMenuBar(0);
dockMenu = new QMenu(globalMenuBar);
Expand Down Expand Up @@ -161,14 +174,14 @@ void Nexus::showLogin()
loginScreen->reset();
loginScreen->move(QApplication::desktop()->screen()->rect().center() - loginScreen->rect().center());
loginScreen->show();
((QApplication*)qApp)->setQuitOnLastWindowClosed(true);
quitOnLastWindowClosed = true;
}

void Nexus::showMainGUI()
{
assert(profile);

((QApplication*)qApp)->setQuitOnLastWindowClosed(false);
quitOnLastWindowClosed = false;
loginScreen->close();

// Create GUI
Expand Down Expand Up @@ -220,6 +233,26 @@ void Nexus::showMainGUI()
profile->startCore();
}

/**
* @brief Calls QApplication::quit(), and causes Nexus::isRunning() to return false
*/
void Nexus::quit()
{
running = false;
qApp->quit();
}

/**
* @brief Returns true until Nexus::quit is called.
*
* Any blocking processEvents() loop should check this as a return condition,
* since the application can not quit until control is returned to the event loop.
*/
bool Nexus::isRunning()
{
return running;
}

/**
* @brief Returns the singleton instance.
*/
Expand Down Expand Up @@ -310,6 +343,12 @@ void Nexus::showLoginLater()
QMetaObject::invokeMethod(&getInstance(), "showLogin", Qt::QueuedConnection);
}

void Nexus::onLastWindowClosed()
{
if (quitOnLastWindowClosed)
quit();
}

#ifdef Q_OS_MAC
void Nexus::retranslateUi()
{
Expand Down
7 changes: 7 additions & 0 deletions src/nexus.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ class Nexus : public QObject
public:
void start();
void showMainGUI();
void quit();
bool isRunning();

static Nexus& getInstance();
static void destroyInstance();
Expand Down Expand Up @@ -84,6 +86,9 @@ public slots:
QActionGroup* windowActions = nullptr;
#endif

private slots:
void onLastWindowClosed();

private:
explicit Nexus(QObject *parent = 0);
~Nexus();
Expand All @@ -92,6 +97,8 @@ public slots:
Profile* profile;
Widget* widget;
LoginScreen* loginScreen;
bool running;
bool quitOnLastWindowClosed;
};

#endif // NEXUS_H
5 changes: 4 additions & 1 deletion src/persistence/profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,10 @@ Profile::~Profile()
saveToxSave();
}

delete core;
core->deleteLater();
while (coreThread->isRunning())
qApp->processEvents();

delete coreThread;
if (!isRemoved)
{
Expand Down
23 changes: 17 additions & 6 deletions src/widget/gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -322,25 +322,34 @@ QString GUI::passwordDialog(const QString& cancel, const QString& body)

void GUI::_clearContacts()
{
Nexus::getDesktopGUI()->clearContactsList();
Widget* w = Nexus::getDesktopGUI();
if (w)
w->clearContactsList();
}

void GUI::_setEnabled(bool state)
{
Nexus::getDesktopGUI()->setEnabled(state);
Widget* w = Nexus::getDesktopGUI();
if (w)
w->setEnabled(state);
}

void GUI::_setWindowTitle(const QString& title)
{
QWidget* w = getMainWidget();
if (!w)
return;
if (title.isEmpty())
getMainWidget()->setWindowTitle("qTox");
w->setWindowTitle("qTox");
else
getMainWidget()->setWindowTitle("qTox - " + title);
w->setWindowTitle("qTox - " + title);
}

void GUI::_reloadTheme()
{
Nexus::getDesktopGUI()->reloadTheme();
Widget* w = Nexus::getDesktopGUI();
if (w)
w->reloadTheme();
}

void GUI::_showInfo(const QString& title, const QString& msg)
Expand All @@ -366,7 +375,9 @@ void GUI::_showError(const QString& title, const QString& msg)

void GUI::_showUpdateDownloadProgress()
{
Nexus::getDesktopGUI()->showUpdateDownloadProgress();
Widget* w = Nexus::getDesktopGUI();
if (w)
w->showUpdateDownloadProgress();
}

bool GUI::_askQuestion(const QString& title, const QString& msg,
Expand Down
5 changes: 5 additions & 0 deletions src/widget/loginscreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,11 @@ bool LoginScreen::event(QEvent* event)
return QWidget::event(event);
}

void LoginScreen::closeEvent(QCloseEvent*)
{
emit closed();
}

void LoginScreen::onNewProfilePageClicked()
{
ui->stackedWidget->setCurrentIndex(0);
Expand Down
4 changes: 4 additions & 0 deletions src/widget/loginscreen.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ class LoginScreen : public QWidget

signals:
void windowStateChanged(Qt::WindowStates states);
void closed();

protected:
virtual void closeEvent(QCloseEvent *event) final override;

private slots:
void onAutoLoginToggled(int state);
Expand Down
4 changes: 2 additions & 2 deletions src/widget/widget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -594,7 +594,7 @@ void Widget::closeEvent(QCloseEvent *event)
saveWindowGeometry();
saveSplitterGeometry();
QWidget::closeEvent(event);
qApp->quit();
Nexus::getInstance().quit();
}
}

Expand Down Expand Up @@ -647,7 +647,7 @@ void Widget::onFailedToStartCore()
critical.setText(tr("toxcore failed to start, the application will terminate after you close this message."));
critical.setIcon(QMessageBox::Critical);
critical.exec();
qApp->quit();
Nexus::getInstance().quit();
}

void Widget::onBadProxyCore()
Expand Down

0 comments on commit c75ee8a

Please sign in to comment.