Merge pull request #7853 from zzmp/fix/shutdown

Fix hang on exit from dying scripts
This commit is contained in:
Brad Hefta-Gaub 2016-05-16 10:17:34 -07:00
commit 166fbdece5
7 changed files with 33 additions and 38 deletions

View file

@ -1156,10 +1156,16 @@ void Application::cleanupBeforeQuit() {
getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts getEntities()->shutdown(); // tell the entities system we're shutting down, so it will stop running scripts
// Clear any queued processing (I/O, FBX/OBJ/Texture parsing)
QThreadPool::globalInstance()->clear();
DependencyManager::get<ScriptEngines>()->saveScripts(); DependencyManager::get<ScriptEngines>()->saveScripts();
DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts DependencyManager::get<ScriptEngines>()->shutdownScripting(); // stop all currently running global scripts
DependencyManager::destroy<ScriptEngines>(); DependencyManager::destroy<ScriptEngines>();
// Cleanup all overlays after the scripts, as scripts might add more
_overlays.cleanupAllOverlays();
// first stop all timers directly or by invokeMethod // first stop all timers directly or by invokeMethod
// depending on what thread they run in // depending on what thread they run in
locationUpdateTimer.stop(); locationUpdateTimer.stop();

View file

@ -36,15 +36,8 @@
#include <QtQuick/QQuickWindow> #include <QtQuick/QQuickWindow>
Overlays::Overlays() : _nextOverlayID(1) { Overlays::Overlays() :
connect(qApp, &Application::beforeAboutToQuit, [=] { _nextOverlayID(1) {}
cleanupAllOverlays();
});
}
Overlays::~Overlays() {
}
void Overlays::cleanupAllOverlays() { void Overlays::cleanupAllOverlays() {
{ {

View file

@ -62,7 +62,6 @@ class Overlays : public QObject {
public: public:
Overlays(); Overlays();
~Overlays();
void init(); void init();
void update(float deltatime); void update(float deltatime);
@ -73,6 +72,8 @@ public:
Overlay::Pointer getOverlay(unsigned int id) const; Overlay::Pointer getOverlay(unsigned int id) const;
OverlayPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; } OverlayPanel::Pointer getPanel(unsigned int id) const { return _panels[id]; }
void cleanupAllOverlays();
public slots: public slots:
/// adds an overlay with the specific properties /// adds an overlay with the specific properties
unsigned int addOverlay(const QString& type, const QVariant& properties); unsigned int addOverlay(const QString& type, const QVariant& properties);
@ -145,7 +146,6 @@ signals:
private: private:
void cleanupOverlaysToDelete(); void cleanupOverlaysToDelete();
void cleanupAllOverlays();
QMap<unsigned int, Overlay::Pointer> _overlaysHUD; QMap<unsigned int, Overlay::Pointer> _overlaysHUD;
QMap<unsigned int, Overlay::Pointer> _overlaysWorld; QMap<unsigned int, Overlay::Pointer> _overlaysWorld;

View file

@ -62,8 +62,6 @@
#include "MIDIEvent.h" #include "MIDIEvent.h"
std::atomic<bool> ScriptEngine::_stoppingAllScripts { false };
static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3"; static const QString SCRIPT_EXCEPTION_FORMAT = "[UncaughtException] %1 in %2:%3";
Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature) Q_DECLARE_METATYPE(QScriptEngine::FunctionSignature)
@ -756,7 +754,7 @@ void ScriptEngine::addEventHandler(const EntityItemID& entityID, const QString&
QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) { QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fileName, int lineNumber) {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
return QScriptValue(); // bail early return QScriptValue(); // bail early
} }
@ -792,7 +790,7 @@ QScriptValue ScriptEngine::evaluate(const QString& sourceCode, const QString& fi
} }
void ScriptEngine::run() { void ScriptEngine::run() {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
return; // bail early - avoid setting state in init(), as evaluate() will bail too return; // bail early - avoid setting state in init(), as evaluate() will bail too
} }
@ -1025,7 +1023,7 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int
} }
QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename(); qCDebug(scriptengine) << "Script.setInterval() while shutting down is ignored... parent script:" << getFilename();
return NULL; // bail early return NULL; // bail early
} }
@ -1034,7 +1032,7 @@ QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS)
} }
QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename(); qCDebug(scriptengine) << "Script.setTimeout() while shutting down is ignored... parent script:" << getFilename();
return NULL; // bail early return NULL; // bail early
} }
@ -1086,7 +1084,7 @@ void ScriptEngine::print(const QString& message) {
// If no callback is specified, the included files will be loaded synchronously and will block execution until // If no callback is specified, the included files will be loaded synchronously and will block execution until
// all of the files have finished loading. // all of the files have finished loading.
void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
qCDebug(scriptengine) << "Script.include() while shutting down is ignored..." qCDebug(scriptengine) << "Script.include() while shutting down is ignored..."
<< "includeFiles:" << includeFiles << "parent script:" << getFilename(); << "includeFiles:" << includeFiles << "parent script:" << getFilename();
return; // bail early return; // bail early
@ -1184,7 +1182,7 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac
} }
void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
qCDebug(scriptengine) << "Script.include() while shutting down is ignored... " qCDebug(scriptengine) << "Script.include() while shutting down is ignored... "
<< "includeFile:" << includeFile << "parent script:" << getFilename(); << "includeFile:" << includeFile << "parent script:" << getFilename();
return; // bail early return; // bail early
@ -1199,7 +1197,7 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) {
// as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which // as a stand-alone script. To accomplish this, the ScriptEngine class just emits a signal which
// the Application or other context will connect to in order to know to actually load the script // the Application or other context will connect to in order to know to actually load the script
void ScriptEngine::load(const QString& loadFile) { void ScriptEngine::load(const QString& loadFile) {
if (_stoppingAllScripts) { if (DependencyManager::get<ScriptEngines>()->isStopped()) {
qCDebug(scriptengine) << "Script.load() while shutting down is ignored... " qCDebug(scriptengine) << "Script.load() while shutting down is ignored... "
<< "loadFile:" << loadFile << "parent script:" << getFilename(); << "loadFile:" << loadFile << "parent script:" << getFilename();
return; // bail early return; // bail early

View file

@ -83,6 +83,10 @@ public:
/// run the script in the callers thread, exit when stop() is called. /// run the script in the callers thread, exit when stop() is called.
void run(); void run();
void waitTillDoneRunning();
QString getFilename() const;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can // NOTE - these are NOT intended to be public interfaces available to scripts, the are only Q_INVOKABLE so we can
// properly ensure they are only called on the correct thread // properly ensure they are only called on the correct thread
@ -199,8 +203,6 @@ protected:
qint64 _lastUpdate; qint64 _lastUpdate;
void init(); void init();
QString getFilename() const;
void waitTillDoneRunning();
bool evaluatePending() const { return _evaluatesPending > 0; } bool evaluatePending() const { return _evaluatesPending > 0; }
void timerFired(); void timerFired();
void stopAllTimers(); void stopAllTimers();
@ -232,9 +234,6 @@ protected:
QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty. QUrl currentSandboxURL {}; // The toplevel url string for the entity script that loaded the code being executed, else empty.
void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation); void doWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, std::function<void()> operation);
void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args); void callWithEnvironment(const EntityItemID& entityID, const QUrl& sandboxURL, QScriptValue function, QScriptValue thisObject, QScriptValueList args);
friend class ScriptEngines;
static std::atomic<bool> _stoppingAllScripts;
}; };
#endif // hifi_ScriptEngine_h #endif // hifi_ScriptEngine_h

View file

@ -119,26 +119,27 @@ void ScriptEngines::registerScriptInitializer(ScriptInitializer initializer) {
} }
void ScriptEngines::addScriptEngine(ScriptEngine* engine) { void ScriptEngines::addScriptEngine(ScriptEngine* engine) {
_allScriptsMutex.lock(); if (_isStopped) {
engine->deleteLater();
} else {
QMutexLocker locker(&_allScriptsMutex);
_allKnownScriptEngines.insert(engine); _allKnownScriptEngines.insert(engine);
_allScriptsMutex.unlock(); }
} }
void ScriptEngines::removeScriptEngine(ScriptEngine* engine) { void ScriptEngines::removeScriptEngine(ScriptEngine* engine) {
// If we're not already in the middle of stopping all scripts, then we should remove ourselves // If we're not already in the middle of stopping all scripts, then we should remove ourselves
// from the list of running scripts. We don't do this if we're in the process of stopping all scripts // from the list of running scripts. We don't do this if we're in the process of stopping all scripts
// because that method removes scripts from its list as it iterates them // because that method removes scripts from its list as it iterates them
if (!_stoppingAllScripts) { if (!_isStopped) {
_allScriptsMutex.lock(); QMutexLocker locker(&_allScriptsMutex);
_allKnownScriptEngines.remove(engine); _allKnownScriptEngines.remove(engine);
_allScriptsMutex.unlock();
} }
} }
void ScriptEngines::shutdownScripting() { void ScriptEngines::shutdownScripting() {
_allScriptsMutex.lock(); _isStopped = true;
_stoppingAllScripts = true; QMutexLocker locker(&_allScriptsMutex);
ScriptEngine::_stoppingAllScripts = true;
qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size(); qCDebug(scriptengine) << "Stopping all scripts.... currently known scripts:" << _allKnownScriptEngines.size();
QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines); QMutableSetIterator<ScriptEngine*> i(_allKnownScriptEngines);
@ -174,8 +175,6 @@ void ScriptEngines::shutdownScripting() {
i.remove(); i.remove();
} }
} }
_stoppingAllScripts = false;
_allScriptsMutex.unlock();
qCDebug(scriptengine) << "DONE Stopping all scripts...."; qCDebug(scriptengine) << "DONE Stopping all scripts....";
} }
@ -499,7 +498,6 @@ void ScriptEngines::launchScriptEngine(ScriptEngine* scriptEngine) {
} }
} }
void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEngine* engine) { void ScriptEngines::onScriptFinished(const QString& rawScriptURL, ScriptEngine* engine) {
bool removed = false; bool removed = false;
{ {

View file

@ -86,16 +86,17 @@ protected:
void onScriptEngineLoaded(const QString& scriptFilename); void onScriptEngineLoaded(const QString& scriptFilename);
void onScriptEngineError(const QString& scriptFilename); void onScriptEngineError(const QString& scriptFilename);
void launchScriptEngine(ScriptEngine* engine); void launchScriptEngine(ScriptEngine* engine);
bool isStopped() const { return _isStopped; }
QReadWriteLock _scriptEnginesHashLock; QReadWriteLock _scriptEnginesHashLock;
QHash<QUrl, ScriptEngine*> _scriptEnginesHash; QHash<QUrl, ScriptEngine*> _scriptEnginesHash;
QSet<ScriptEngine*> _allKnownScriptEngines; QSet<ScriptEngine*> _allKnownScriptEngines;
QMutex _allScriptsMutex; QMutex _allScriptsMutex;
std::atomic<bool> _stoppingAllScripts { false };
std::list<ScriptInitializer> _scriptInitializers; std::list<ScriptInitializer> _scriptInitializers;
mutable Setting::Handle<QString> _scriptsLocationHandle; mutable Setting::Handle<QString> _scriptsLocationHandle;
ScriptsModel _scriptsModel; ScriptsModel _scriptsModel;
ScriptsModelFilter _scriptsModelFilter; ScriptsModelFilter _scriptsModelFilter;
std::atomic<bool> _isStopped { false };
}; };
QUrl normalizeScriptURL(const QUrl& rawScriptURL); QUrl normalizeScriptURL(const QUrl& rawScriptURL);