ProjectExplorer: Use a real 'finishing' phase for RunControls

Instead of a blunt delete() RunControl::initiateFinish() is triggered
by the closing of the application output instead. The rampdown process
is basically the same as stop() now, except for the other success
signal (new finished()) and the final self-destruction of the
runcontrol.

stop() itself triggers initiateStop() on all running workers in
parallel (before it was in the order of start). This gives
downstream complex worker combinations the flexibility to use any
order it wants by ignoring stop() on 'uninteresting' workers,
and centralizing rampdown e.g. in the main worker. That setup should
be rare in practice, but seems needed in some profiler cases.

Change-Id: I986a152a663754206709ed4df0d4568847afad17
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
hjk
2017-07-13 15:24:04 +02:00
parent c8054d9547
commit cd5ba75505
6 changed files with 162 additions and 33 deletions

View File

@@ -420,7 +420,8 @@ void AppOutputPane::createNewOutputWindow(RunControl *rc)
if (tabIndex != -1) { if (tabIndex != -1) {
RunControlTab &tab = m_runControlTabs[tabIndex]; RunControlTab &tab = m_runControlTabs[tabIndex];
// Reuse this tab // Reuse this tab
delete tab.runControl; if (tab.runControl)
tab.runControl->initiateFinish();
tab.runControl = rc; tab.runControl = rc;
tab.window->setFormatter(rc ? rc->outputFormatter() : nullptr); tab.window->setFormatter(rc ? rc->outputFormatter() : nullptr);
@@ -559,7 +560,7 @@ bool AppOutputPane::closeTabs(CloseTabMode mode)
QList<RunControl *> AppOutputPane::allRunControls() const QList<RunControl *> AppOutputPane::allRunControls() const
{ {
return Utils::transform<QList>(m_runControlTabs,[](const RunControlTab &tab) { return Utils::transform<QList>(m_runControlTabs,[](const RunControlTab &tab) {
return tab.runControl; return tab.runControl.data();
}); });
} }
@@ -596,7 +597,8 @@ bool AppOutputPane::closeTab(int tabIndex, CloseTabMode closeTabMode)
m_tabWidget->removeTab(tabIndex); m_tabWidget->removeTab(tabIndex);
delete m_runControlTabs[index].window; delete m_runControlTabs[index].window;
delete m_runControlTabs[index].runControl; m_runControlTabs[index].runControl->initiateFinish(); // Will self-destruct.
m_runControlTabs[index].runControl = 0;
m_runControlTabs.removeAt(index); m_runControlTabs.removeAt(index);
updateCloseActions(); updateCloseActions();

View File

@@ -25,6 +25,7 @@
#pragma once #pragma once
#include <QPointer>
#include <QVector> #include <QVector>
#include <coreplugin/ioutputpane.h> #include <coreplugin/ioutputpane.h>
@@ -124,7 +125,7 @@ private:
public: public:
explicit RunControlTab(RunControl *runControl = nullptr, explicit RunControlTab(RunControl *runControl = nullptr,
Core::OutputWindow *window = nullptr); Core::OutputWindow *window = nullptr);
RunControl *runControl; QPointer<RunControl> runControl;
Core::OutputWindow *window; Core::OutputWindow *window;
BehaviorOnOutput behaviorOnOutput = Flash; BehaviorOnOutput behaviorOnOutput = Flash;
}; };

View File

@@ -602,7 +602,7 @@ static QString stateName(RunWorkerState s)
SN(RunWorkerState::Done) SN(RunWorkerState::Done)
SN(RunWorkerState::Failed) SN(RunWorkerState::Failed)
} }
return QLatin1String("<unknown>"); return QString("<unknown: %1>").arg(int(s));
# undef SN # undef SN
} }
@@ -616,7 +616,7 @@ public:
RunWorker *q; RunWorker *q;
RunWorkerState state = RunWorkerState::Initialized; RunWorkerState state = RunWorkerState::Initialized;
RunControl *runControl; QPointer<RunControl> runControl;
QList<RunWorker *> dependencies; QList<RunWorker *> dependencies;
QString id; QString id;
@@ -635,6 +635,8 @@ enum class RunControlState
Running, // All good and running. Running, // All good and running.
Stopping, // initiateStop() was called, stop application/tool Stopping, // initiateStop() was called, stop application/tool
Stopped, // all good, but stopped. Can possibly be re-started Stopped, // all good, but stopped. Can possibly be re-started
Finishing, // Application tab manually closed
Finished // Final state, will self-destruct with deleteLater()
}; };
static QString stateName(RunControlState s) static QString stateName(RunControlState s)
@@ -646,8 +648,10 @@ static QString stateName(RunControlState s)
SN(RunControlState::Running) SN(RunControlState::Running)
SN(RunControlState::Stopping) SN(RunControlState::Stopping)
SN(RunControlState::Stopped) SN(RunControlState::Stopped)
SN(RunControlState::Finishing)
SN(RunControlState::Finished)
} }
return QLatin1String("<unknown>"); return QString("<unknown: %1>").arg(int(s));
# undef SN # undef SN
} }
@@ -669,8 +673,11 @@ public:
~RunControlPrivate() ~RunControlPrivate()
{ {
QTC_CHECK(state == RunControlState::Stopped || state == RunControlState::Initialized); QTC_CHECK(state == RunControlState::Finished || state == RunControlState::Initialized);
disconnect();
q = nullptr;
qDeleteAll(m_workers); qDeleteAll(m_workers);
m_workers.clear();
delete outputFormatter; delete outputFormatter;
} }
@@ -685,7 +692,7 @@ public:
void initiateReStart(); void initiateReStart();
void continueStart(); void continueStart();
void initiateStop(); void initiateStop();
void continueStop(); void initiateFinish();
void onWorkerStarted(RunWorker *worker); void onWorkerStarted(RunWorker *worker);
void onWorkerStopped(RunWorker *worker); void onWorkerStopped(RunWorker *worker);
@@ -704,7 +711,7 @@ public:
Utils::Icon icon; Utils::Icon icon;
const QPointer<RunConfiguration> runConfiguration; // Not owned. const QPointer<RunConfiguration> runConfiguration; // Not owned.
QPointer<Project> project; // Not owned. QPointer<Project> project; // Not owned.
Utils::OutputFormatter *outputFormatter = nullptr; QPointer<Utils::OutputFormatter> outputFormatter = nullptr;
std::function<bool(bool*)> promptToStop; std::function<bool(bool*)> promptToStop;
std::vector<RunControl::WorkerFactory> m_factories; std::vector<RunControl::WorkerFactory> m_factories;
@@ -769,6 +776,11 @@ void RunControl::initiateStop()
d->initiateStop(); d->initiateStop();
} }
void RunControl::initiateFinish()
{
d->initiateFinish();
}
using WorkerCreators = QHash<Core::Id, RunControl::WorkerCreator>; using WorkerCreators = QHash<Core::Id, RunControl::WorkerCreator>;
static WorkerCreators &theWorkerCreators() static WorkerCreators &theWorkerCreators()
@@ -890,15 +902,8 @@ void RunControlPrivate::initiateStop()
{ {
checkState(RunControlState::Running); checkState(RunControlState::Running);
setState(RunControlState::Stopping); setState(RunControlState::Stopping);
debugMessage("Queue: Stopping"); debugMessage("Queue: Stopping for all workers");
continueStop();
}
void RunControlPrivate::continueStop()
{
debugMessage("Continue Stopping");
checkState(RunControlState::Stopping);
bool allDone = true; bool allDone = true;
for (RunWorker *worker : m_workers) { for (RunWorker *worker : m_workers) {
if (worker) { if (worker) {
@@ -914,17 +919,17 @@ void RunControlPrivate::continueStop()
allDone = false; allDone = false;
break; break;
case RunWorkerState::Starting: case RunWorkerState::Starting:
worker->d->state = RunWorkerState::Stopping;
debugMessage(" " + workerId + " was Starting, queuing stop"); debugMessage(" " + workerId + " was Starting, queuing stop");
allDone = false; worker->d->state = RunWorkerState::Stopping;
QTimer::singleShot(0, worker, &RunWorker::initiateStop); QTimer::singleShot(0, worker, &RunWorker::initiateStop);
return; // Sic. allDone = false;
break;
case RunWorkerState::Running: case RunWorkerState::Running:
debugMessage(" " + workerId + " was Running, queuing stop"); debugMessage(" " + workerId + " was Running, queuing stop");
worker->d->state = RunWorkerState::Stopping; worker->d->state = RunWorkerState::Stopping;
allDone = false; allDone = false;
QTimer::singleShot(0, worker, &RunWorker::initiateStop); QTimer::singleShot(0, worker, &RunWorker::initiateStop);
return; // Sic. break;
case RunWorkerState::Done: case RunWorkerState::Done:
debugMessage(" " + workerId + " was Done. Good."); debugMessage(" " + workerId + " was Done. Good.");
break; break;
@@ -937,8 +942,59 @@ void RunControlPrivate::continueStop()
} }
} }
if (allDone) { if (allDone) {
debugMessage("All workers stopped. Set runControl to Stopped"); debugMessage("All stopped.");
setState(RunControlState::Stopped); setState(RunControlState::Stopped);
} else {
debugMessage("Not all workers stopped. Waiting...");
}
}
void RunControlPrivate::initiateFinish()
{
setState(RunControlState::Finishing);
debugMessage("Ramping down");
bool allDone = true;
for (RunWorker *worker : m_workers) {
if (worker) {
const QString &workerId = worker->d->id;
debugMessage(" Examining worker " + workerId);
switch (worker->d->state) {
case RunWorkerState::Initialized:
debugMessage(" " + workerId + " was Initialized, setting to Done");
worker->d->state = RunWorkerState::Done;
break;
case RunWorkerState::Stopping:
debugMessage(" " + workerId + " was already Stopping. Keeping it that way");
allDone = false;
break;
case RunWorkerState::Starting:
debugMessage(" " + workerId + " was Starting, queuing stop");
worker->d->state = RunWorkerState::Stopping;
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
allDone = false;
break;
case RunWorkerState::Running:
debugMessage(" " + workerId + " was Running, queuing stop");
worker->d->state = RunWorkerState::Stopping;
allDone = false;
QTimer::singleShot(0, worker, &RunWorker::initiateStop);
break;
case RunWorkerState::Done:
debugMessage(" " + workerId + " was Done. Good.");
break;
case RunWorkerState::Failed:
debugMessage(" " + workerId + " was Failed. Good");
break;
}
} else {
debugMessage("Found unknown deleted worker");
}
}
if (allDone) {
setState(RunControlState::Finished);
} else {
debugMessage("Not all workers finished. Waiting...");
} }
} }
@@ -954,7 +1010,6 @@ void RunControlPrivate::onWorkerStarted(RunWorker *worker)
showError(tr("Unexpected run control state %1 when worker %2 started") showError(tr("Unexpected run control state %1 when worker %2 started")
.arg(stateName(state)) .arg(stateName(state))
.arg(worker->d->id)); .arg(worker->d->id));
//setState(RunControlState::Stopped);
} }
void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg) void RunControlPrivate::onWorkerFailed(RunWorker *worker, const QString &msg)
@@ -978,13 +1033,72 @@ void RunControlPrivate::onWorkerStopped(RunWorker *worker)
worker->d->state = RunWorkerState::Done; worker->d->state = RunWorkerState::Done;
debugMessage(workerId + " stopped expectedly."); debugMessage(workerId + " stopped expectedly.");
break; break;
case RunWorkerState::Done:
worker->d->state = RunWorkerState::Done;
debugMessage(workerId + " stopped twice. Huh? But harmless.");
return; // Sic!
default: default:
debugMessage(workerId + " stopped unexpectedly in state" debugMessage(workerId + " stopped unexpectedly in state"
+ stateName(worker->d->state)); + stateName(worker->d->state));
worker->d->state = RunWorkerState::Failed; worker->d->state = RunWorkerState::Failed;
break; break;
} }
continueStop();
debugMessage("Checking whether all stopped");
bool allDone = true;
for (RunWorker *worker : m_workers) {
if (worker) {
const QString &workerId = worker->d->id;
debugMessage(" Examining worker " + workerId);
switch (worker->d->state) {
case RunWorkerState::Initialized:
debugMessage(" " + workerId + " was Initialized, setting to Done");
worker->d->state = RunWorkerState::Done;
break;
case RunWorkerState::Starting:
worker->d->state = RunWorkerState::Stopping;
debugMessage(" " + workerId + " was Starting, queuing stop");
allDone = false;
break;
case RunWorkerState::Running:
debugMessage(" " + workerId + " was Running, queuing stop");
worker->d->state = RunWorkerState::Stopping;
allDone = false;
break;
case RunWorkerState::Stopping:
debugMessage(" " + workerId + " was already Stopping. Keeping it that way");
allDone = false;
break;
case RunWorkerState::Done:
debugMessage(" " + workerId + " was Done. Good.");
break;
case RunWorkerState::Failed:
debugMessage(" " + workerId + " was Failed. Good");
break;
}
} else {
debugMessage("Found unknown deleted worker");
}
}
if (state == RunControlState::Finishing) {
if (allDone) {
debugMessage("All finished. Deleting myself");
setState(RunControlState::Finished);
} else {
debugMessage("Not all workers finished. Waiting...");
}
} else {
if (allDone) {
if (state == RunControlState::Stopped) {
debugMessage("All workers stopped, but runControl was already stopped.");
} else {
debugMessage("All workers stopped. Set runControl to Stopped");
setState(RunControlState::Stopped);
}
} else {
debugMessage("Not all workers stopped. Waiting...");
}
}
} }
void RunControlPrivate::showError(const QString &msg) void RunControlPrivate::showError(const QString &msg)
@@ -1185,15 +1299,23 @@ bool RunControlPrivate::isAllowedTransition(RunControlState from, RunControlStat
{ {
switch (from) { switch (from) {
case RunControlState::Initialized: case RunControlState::Initialized:
return to == RunControlState::Starting; return to == RunControlState::Starting
|| to == RunControlState::Finishing;
case RunControlState::Starting: case RunControlState::Starting:
return to == RunControlState::Running; return to == RunControlState::Running
|| to == RunControlState::Finishing;
case RunControlState::Running: case RunControlState::Running:
return to == RunControlState::Stopping return to == RunControlState::Stopping
|| to == RunControlState::Stopped; || to == RunControlState::Stopped
|| to == RunControlState::Finishing;
case RunControlState::Stopping: case RunControlState::Stopping:
return to == RunControlState::Stopped; return to == RunControlState::Stopped
|| to == RunControlState::Finishing;
case RunControlState::Stopped: case RunControlState::Stopped:
return to == RunControlState::Finishing;
case RunControlState::Finishing:
return to == RunControlState::Finished;
case RunControlState::Finished:
return false; return false;
} }
return false; return false;
@@ -1223,11 +1345,13 @@ void RunControlPrivate::setState(RunControlState newState)
break; break;
case RunControlState::Stopped: case RunControlState::Stopped:
q->setApplicationProcessHandle(Utils::ProcessHandle()); q->setApplicationProcessHandle(Utils::ProcessHandle());
foreach (auto worker, m_workers)
if (worker)
worker->onFinished();
emit q->stopped(); emit q->stopped();
break; break;
case RunControlState::Finished:
emit q->finished();
debugMessage("All finished. Deleting myself");
deleteLater();
break;
default: default:
break; break;
} }

View File

@@ -397,6 +397,7 @@ public:
void initiateStart(); void initiateStart();
void initiateReStart(); void initiateReStart();
void initiateStop(); void initiateStop();
void initiateFinish();
bool promptToStop(bool *optionalPrompt = nullptr) const; bool promptToStop(bool *optionalPrompt = nullptr) const;
void setPromptToStop(const std::function<bool(bool *)> &promptToStop); void setPromptToStop(const std::function<bool(bool *)> &promptToStop);
@@ -480,6 +481,7 @@ signals:
void aboutToStart(); void aboutToStart();
void started(); void started();
void stopped(); void stopped();
void finished();
void applicationProcessHandleChanged(QPrivateSignal); // Use setApplicationProcessHandle void applicationProcessHandleChanged(QPrivateSignal); // Use setApplicationProcessHandle
private: private:

View File

@@ -562,6 +562,7 @@ RunWorker *MemcheckTool::createRunWorker(RunControl *runControl)
connect(runTool, &MemcheckToolRunner::internalParserError, this, &MemcheckTool::internalParserError); connect(runTool, &MemcheckToolRunner::internalParserError, this, &MemcheckTool::internalParserError);
connect(runTool, &MemcheckToolRunner::stopped, this, &MemcheckTool::engineFinished); connect(runTool, &MemcheckToolRunner::stopped, this, &MemcheckTool::engineFinished);
m_stopAction->disconnect();
connect(m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop); connect(m_stopAction, &QAction::triggered, runControl, &RunControl::initiateStop);
m_toolBusy = true; m_toolBusy = true;

View File

@@ -106,7 +106,6 @@ void ValgrindToolRunner::stop()
{ {
m_isStopping = true; m_isStopping = true;
m_runner.stop(); m_runner.stop();
reportStopped(); // FIXME: Restrict to non-running scenarios?
} }
QString ValgrindToolRunner::executable() const QString ValgrindToolRunner::executable() const