Skip to content
Permalink
Browse files

Add restart limiter to OverlayPrivateWin to avoid bombing the system …

…with process spawns.

We need this in case there's a bug in one of the overlay helpers.
Without this, we would restart a crashed helper immediately. If this
bug is triggered every time the helper is run, we'd get into an infinite
restart loop, potentially making the host computer unresponsive.
  • Loading branch information...
mkrautz committed Feb 21, 2015
1 parent 8e31de7 commit bb0ccc4629d1b15c352cf628b403f1bf246bcc2d
Showing with 71 additions and 1 deletion.
  1. +53 −0 src/mumble/Overlay_win.cpp
  2. +7 −0 src/mumble/Overlay_win.h
  3. +9 −1 src/mumble/Settings.cpp
  4. +2 −0 src/mumble/Settings.h
@@ -88,6 +88,10 @@ OverlayPrivateWin::OverlayPrivateWin(QObject *p) : OverlayPrivate(p) {
connect(m_helper_process, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(onHelperProcessExited(int, QProcess::ExitStatus)));

m_helper_restart_timer = new QTimer(this);
m_helper_restart_timer->setSingleShot(true);
connect(m_helper_restart_timer, SIGNAL(timeout()), this, SLOT(onDelayedRestartTimerTriggered()));

m_helper64_exe_path = QString::fromLatin1("%1/mumble_ol_x64.exe").arg(qApp->applicationDirPath());
m_helper64_exe_args = m_helper_exe_args;
m_helper64_process = new QProcess(this);
@@ -100,6 +104,10 @@ OverlayPrivateWin::OverlayPrivateWin(QObject *p) : OverlayPrivate(p) {

connect(m_helper64_process, SIGNAL(finished(int, QProcess::ExitStatus)),
this, SLOT(onHelperProcessExited(int, QProcess::ExitStatus)));

m_helper64_restart_timer = new QTimer(this);
m_helper64_restart_timer->setSingleShot(true);
connect(m_helper64_restart_timer, SIGNAL(timeout()), this, SLOT(onDelayedRestartTimerTriggered()));
}

OverlayPrivateWin::~OverlayPrivateWin() {
@@ -110,8 +118,10 @@ void OverlayPrivateWin::startHelper(QProcess *helper) {
if (helper->state() == QProcess::NotRunning) {
if (helper == m_helper_process) {
helper->start(m_helper_exe_path, m_helper_exe_args);
m_helper_start_time.restart();
} else if (helper == m_helper64_process) {
helper->start(m_helper64_exe_path, m_helper64_exe_args);
m_helper64_start_time.restart();
}
} else {
qWarning("OverlayPrivateWin: startHelper() called while process is already running. skipping.");
@@ -194,11 +204,18 @@ void OverlayPrivateWin::onHelperProcessError(QProcess::ProcessError processError

void OverlayPrivateWin::onHelperProcessExited(int exitCode, QProcess::ExitStatus exitStatus) {
QProcess *helper = qobject_cast<QProcess *>(sender());

QString path;
qint64 elapsedMsec;
QTimer *restartTimer;
if (helper == m_helper_process) {
path = m_helper_exe_path;
elapsedMsec = m_helper_start_time.elapsed();
restartTimer = m_helper_restart_timer;
} else if (helper == m_helper64_process) {
path = m_helper64_exe_path;
elapsedMsec = m_helper64_start_time.elapsed();
restartTimer = m_helper64_restart_timer;
}

const char *helperErrString = OverlayHelperErrorToString(static_cast<OverlayHelperError>(exitCode));
@@ -210,7 +227,43 @@ void OverlayPrivateWin::onHelperProcessExited(int exitCode, QProcess::ExitStatus
// If the helper process exited while we're in 'active'
// mode, restart it.
if (m_active) {
// If the helper was only recently started, be
// a little more patient with restarting it.
// We could be hitting a crash bug in the helper,
// and we don't want to do too much harm in that
// case by spawning thousands of processes.
qint64 cooldownMsec = (qint64) g.s.iOverlayWinHelperRestartCooldownMsec;
if (elapsedMsec < cooldownMsec) {
qint64 delayMsec = cooldownMsec - elapsedMsec;
qWarning("OverlayPrivateWin: waiting %llu seconds until restarting helper process '%s'. last restart was %llu seconds ago.",
(unsigned long long) delayMsec/1000ULL,
qPrintable(path),
(unsigned long long) elapsedMsec/1000ULL);
if (!restartTimer->isActive()) {
restartTimer->start(delayMsec);
}
} else {
startHelper(helper);
}
}
}

void OverlayPrivateWin::onDelayedRestartTimerTriggered() {
if (!m_active) {
return;
}

QTimer *timer = qobject_cast<QTimer *>(sender());

QProcess *helper = NULL;
if (timer == m_helper_restart_timer) {
helper = m_helper_process;
} else if (timer == m_helper64_restart_timer) {
helper = m_helper64_process;
}

if (helper->state() == QProcess::NotRunning) {
startHelper(helper);
}
}

@@ -35,6 +35,8 @@
#include <QString>
#include <QStringList>
#include <QProcess>
#include <QTimer>
#include <QElapsedTimer>

#include "Overlay.h"

@@ -52,15 +54,20 @@ class OverlayPrivateWin : public OverlayPrivate {
void onHelperProcessStarted();
void onHelperProcessError(QProcess::ProcessError);
void onHelperProcessExited(int exitCode, QProcess::ExitStatus exitStatus);
void onDelayedRestartTimerTriggered();

protected:
QProcess *m_helper_process;
QString m_helper_exe_path;
QStringList m_helper_exe_args;
QElapsedTimer m_helper_start_time;
QTimer *m_helper_restart_timer;

QProcess *m_helper64_process;
QString m_helper64_exe_path;
QStringList m_helper64_exe_args;
QElapsedTimer m_helper64_start_time;
QTimer *m_helper64_restart_timer;

bool m_active;
bool m_allow64bit;
@@ -345,6 +345,9 @@ Settings::Settings() {
fAudioMaxDistVolume = 0.80f;
fAudioBloom = 0.5f;

// OverlayPrivateWin
iOverlayWinHelperRestartCooldownMsec = 10000;

iLCDUserViewMinColWidth = 50;
iLCDUserViewSplitterWidth = 2;

@@ -696,7 +699,9 @@ void Settings::load(QSettings* settings_ptr) {
SAVELOAD(bDisableCELT, "audio/disablecelt");
SAVELOAD(disablePublicList, "ui/disablepubliclist");
SAVELOAD(disableConnectDialogEditing, "ui/disableconnectdialogediting");


// OverlayPrivateWin
SAVELOAD(iOverlayWinHelperRestartCooldownMsec, "overlay_win/helper/restart_cooldown_msec");

// LCD
SAVELOAD(iLCDUserViewMinColWidth, "lcd/userview/mincolwidth");
@@ -990,6 +995,9 @@ void Settings::save() {
SAVELOAD(disablePublicList, "ui/disablepubliclist");
SAVELOAD(disableConnectDialogEditing, "ui/disableconnectdialogediting");

// OverlayPrivateWin
SAVELOAD(iOverlayWinHelperRestartCooldownMsec, "overlay_win/helper/restart_cooldown_msec");

// LCD
SAVELOAD(iLCDUserViewMinColWidth, "lcd/userview/mincolwidth");
SAVELOAD(iLCDUserViewSplitterWidth, "lcd/userview/splitterwidth");
@@ -230,6 +230,8 @@ struct Settings {

OverlaySettings os;

int iOverlayWinHelperRestartCooldownMsec;

int iLCDUserViewMinColWidth;
int iLCDUserViewSplitterWidth;
QMap<QString, bool> qmLCDDevices;

0 comments on commit bb0ccc4

Please sign in to comment.
You can’t perform that action at this time.