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.
This commit is contained in:
Mikkel Krautz 2015-02-21 12:02:20 +01:00
parent 8e31de788f
commit bb0ccc4629
4 changed files with 71 additions and 1 deletions

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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");

View File

@ -230,6 +230,8 @@ struct Settings {
OverlaySettings os;
int iOverlayWinHelperRestartCooldownMsec;
int iLCDUserViewMinColWidth;
int iLCDUserViewSplitterWidth;
QMap<QString, bool> qmLCDDevices;