ProjectExplorer: Fix crashes on shutdown

Ensure that RunControls ramp down asynchronously.

Since RunControls now ramp down asynchronously the plugin can
only report synchronous shut down if no RunControl/OutputPane
was present.

Task-number: QTCREATORBUG-18605
Change-Id: Ib8d1f857f85c74d4f18ecb85db537121a2275668
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
hjk
2017-07-20 14:43:24 +02:00
parent 35c7360892
commit a54cba428a
4 changed files with 59 additions and 62 deletions

View File

@@ -247,11 +247,6 @@ AppOutputPane::AppOutputPane() :
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
this, &AppOutputPane::updateFromSettings);
#ifdef Q_OS_WIN
connect(this, &AppOutputPane::allRunControlsFinished,
WinDebugInterface::instance(), &WinDebugInterface::stop);
#endif
QSettings *settings = Core::ICore::settings();
m_zoom = settings->value(QLatin1String(SETTINGS_KEY), 0).toFloat();
@@ -546,15 +541,10 @@ void AppOutputPane::stopRunControl()
qDebug() << "OutputPane::stopRunControl " << rc;
}
bool AppOutputPane::closeTabs(CloseTabMode mode)
void AppOutputPane::closeTabs(CloseTabMode mode)
{
bool allClosed = true;
for (int t = m_tabWidget->count() - 1; t >= 0; t--)
if (!closeTab(t, mode))
allClosed = false;
if (debug)
qDebug() << "OutputPane::closeTabs() returns " << allClosed;
return allClosed;
closeTab(t, mode);
}
QList<RunControl *> AppOutputPane::allRunControls() const
@@ -564,48 +554,37 @@ QList<RunControl *> AppOutputPane::allRunControls() const
});
}
bool AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode)
void AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode)
{
int index = indexOf(m_tabWidget->widget(tabIndex));
QTC_ASSERT(index != -1, return true);
QTC_ASSERT(index != -1, return);
RunControl *runControl = m_runControlTabs[index].runControl;
Core::OutputWindow *window = m_runControlTabs[index].window;
if (debug)
qDebug() << "OutputPane::closeTab tab " << tabIndex << m_runControlTabs[index].runControl
<< m_runControlTabs[index].window;
qDebug() << "OutputPane::closeTab tab " << tabIndex << runControl << window;
// Prompt user to stop
if (m_runControlTabs[index].runControl->isRunning()) {
switch (closeTabMode) {
case CloseTabNoPrompt:
break;
case CloseTabWithPrompt:
QWidget *tabWidget = m_tabWidget->widget(tabIndex);
if (!m_runControlTabs[index].runControl->promptToStop())
return false;
// The event loop has run, thus the ordering might have changed, a tab might
// have been closed, so do some strange things...
tabIndex = m_tabWidget->indexOf(tabWidget);
index = indexOf(tabWidget);
if (tabIndex == -1 || index == -1)
return false;
break;
}
if (m_runControlTabs[index].runControl->isRunning()) { // yes it might have stopped already, then just close
m_runControlTabs[index].runControl->initiateStop();
return false;
}
if (closeTabMode == CloseTabWithPrompt) {
QWidget *tabWidget = m_tabWidget->widget(tabIndex);
if (!runControl->promptToStop())
return;
// The event loop has run, thus the ordering might have changed, a tab might
// have been closed, so do some strange things...
tabIndex = m_tabWidget->indexOf(tabWidget);
index = indexOf(tabWidget);
if (tabIndex == -1 || index == -1)
return;
}
m_tabWidget->removeTab(tabIndex);
delete m_runControlTabs[index].window;
m_runControlTabs[index].runControl->initiateFinish(); // Will self-destruct.
m_runControlTabs[index].runControl = 0;
delete window;
runControl->initiateFinish(); // Will self-destruct.
m_runControlTabs.removeAt(index);
updateCloseActions();
if (m_runControlTabs.isEmpty())
hide();
return true;
}
bool AppOutputPane::optionallyPromptToStop(RunControl *runControl)
@@ -742,15 +721,14 @@ void AppOutputPane::slotRunControlFinished2(RunControl *sender)
ProjectExplorerPlugin::instance()->updateRunActions();
if (!isRunning())
emit allRunControlsFinished();
}
bool AppOutputPane::isRunning() const
{
return Utils::anyOf(m_runControlTabs, [](const RunControlTab &rt) {
#ifdef Q_OS_WIN
const bool isRunning = Utils::anyOf(m_runControlTabs, [](const RunControlTab &rt) {
return rt.runControl->isRunning();
});
if (!isRunning)
WinDebugInterface::instance()->stop();
#endif
}
bool AppOutputPane::canNext() const

View File

@@ -88,14 +88,10 @@ public:
void setBehaviorOnOutput(RunControl *rc, BehaviorOnOutput mode);
bool aboutToClose() const;
bool closeTabs(CloseTabMode mode);
void closeTabs(CloseTabMode mode);
QList<RunControl *> allRunControls() const;
signals:
void allRunControlsFinished();
public:
// ApplicationOutput specifics
void projectRemoved();
@@ -126,12 +122,11 @@ private:
explicit RunControlTab(RunControl *runControl = nullptr,
Core::OutputWindow *window = nullptr);
QPointer<RunControl> runControl;
Core::OutputWindow *window;
QPointer<Core::OutputWindow> window;
BehaviorOnOutput behaviorOnOutput = Flash;
};
bool isRunning() const;
bool closeTab(int index, CloseTabMode cm = CloseTabWithPrompt);
void closeTab(int index, CloseTabMode cm = CloseTabWithPrompt);
bool optionallyPromptToStop(RunControl *runControl);
int indexOf(const RunControl *) const;
@@ -148,6 +143,7 @@ private:
QWidget *m_mainWidget;
TabWidget *m_tabWidget;
QVector<RunControlTab> m_runControlTabs;
int m_runControlCount = 0;
QAction *m_stopAction;
QAction *m_closeCurrentTabAction;
QAction *m_closeAllTabsAction;

View File

@@ -343,6 +343,9 @@ public:
void runConfigurationConfigurationFinished();
void checkForShutdown();
void timerEvent(QTimerEvent *) override;
QList<QPair<QString, QString> > recentProjects() const;
public:
@@ -405,6 +408,8 @@ public:
QStringList m_profileMimeTypes;
AppOutputPane *m_outputPane = nullptr;
int m_activeRunControlCount = 0;
int m_shutdownWatchDogId = -1;
QHash<QString, std::function<Project *(const Utils::FileName &)>> m_projectCreators;
QList<QPair<QString, QString> > m_recentProjects; // pair of filename, displayname
@@ -1632,10 +1637,12 @@ ExtensionSystem::IPlugin::ShutdownFlag ProjectExplorerPlugin::aboutToShutdown()
removeObject(dd->m_welcomePage);
delete dd->m_welcomePage;
removeObject(this);
if (dd->m_outputPane->closeTabs(AppOutputPane::CloseTabNoPrompt /* No prompt any more */))
if (dd->m_activeRunControlCount == 0)
return SynchronousShutdown;
connect(dd->m_outputPane, &AppOutputPane::allRunControlsFinished,
this, &IPlugin::asynchronousShutdownFinished);
dd->m_outputPane->closeTabs(AppOutputPane::CloseTabNoPrompt /* No prompt any more */);
dd->m_shutdownWatchDogId = dd->startTimer(10 * 1000); // Make sure we shutdown *somehow*
return AsynchronousShutdown;
}
@@ -2032,10 +2039,27 @@ void ProjectExplorerPluginPrivate::startRunControl(RunControl *runControl)
bool popup = (runMode == Constants::NORMAL_RUN_MODE && dd->m_projectExplorerSettings.showRunOutput)
|| (runMode == Constants::DEBUG_RUN_MODE && m_projectExplorerSettings.showDebugOutput);
m_outputPane->setBehaviorOnOutput(runControl, popup ? AppOutputPane::Popup : AppOutputPane::Flash);
connect(runControl, &QObject::destroyed, this, &ProjectExplorerPluginPrivate::checkForShutdown,
Qt::QueuedConnection);
++m_activeRunControlCount;
runControl->initiateStart();
emit m_instance->updateRunActions();
}
void ProjectExplorerPluginPrivate::checkForShutdown()
{
--m_activeRunControlCount;
QTC_ASSERT(m_activeRunControlCount >= 0, m_activeRunControlCount = 0);
if (m_shuttingDown && m_activeRunControlCount == 0)
m_instance->asynchronousShutdownFinished();
}
void ProjectExplorerPluginPrivate::timerEvent(QTimerEvent *ev)
{
if (m_shutdownWatchDogId == ev->timerId())
m_instance->asynchronousShutdownFinished();
}
void ProjectExplorerPlugin::initiateInlineRenaming()
{
dd->handleRenameFile();

View File

@@ -674,7 +674,6 @@ RunControl::~RunControl()
#ifdef WITH_JOURNALD
JournaldWatcher::instance()->unsubscribe(this);
#endif
disconnect();
delete d;
d = nullptr;
}
@@ -698,7 +697,7 @@ void RunControl::initiateStop()
void RunControl::initiateFinish()
{
d->initiateFinish();
QTimer::singleShot(0, d, &RunControlPrivate::initiateFinish);
}
using WorkerCreators = QHash<Core::Id, RunControl::WorkerCreator>;
@@ -1277,7 +1276,7 @@ void RunControlPrivate::setState(RunControlState newState)
case RunControlState::Finished:
emit q->finished();
debugMessage("All finished. Deleting myself");
deleteLater();
q->deleteLater();
break;
default:
break;