From 6be8f4c0ec8dcffe07e99f22ecec501e1f07d7a0 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Mon, 23 Feb 2015 17:32:31 -0800 Subject: [PATCH] more work on improving shutdown behavior --- interface/src/Application.cpp | 11 ++- .../src/EntityTreeRenderer.cpp | 2 + libraries/script-engine/src/ScriptEngine.cpp | 81 ++++++++++++++++--- libraries/script-engine/src/ScriptEngine.h | 4 + 4 files changed, 86 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 25ee53f187..32854a1781 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3538,7 +3538,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri connect(workerThread, SIGNAL(finished()), workerThread, SLOT(deleteLater())); // when the application is about to quit, stop our script engine so it unwinds properly - connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); + //connect(this, SIGNAL(aboutToQuit()), scriptEngine, SLOT(stop())); auto nodeList = DependencyManager::get(); connect(nodeList.data(), &NodeList::nodeKilled, scriptEngine, &ScriptEngine::nodeKilled); @@ -3550,7 +3550,14 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri } ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, - bool loadScriptFromEditor, bool activateMainWindow) { + bool loadScriptFromEditor, bool activateMainWindow) { + + qDebug() << "Application::loadScript() " << scriptFilename << "line:" << __LINE__; + if (isAboutToQuit()) { + qDebug() << "Application::loadScript() WHILE QUITTING.... what to do????" << scriptFilename << "line:" << __LINE__; + return NULL; + } + QUrl scriptUrl(scriptFilename); const QString& scriptURLString = scriptUrl.toString(); if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index fdaaa6422b..9deafe17da 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -59,6 +59,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf } EntityTreeRenderer::~EntityTreeRenderer() { + qDebug() << "EntityTreeRenderer::~EntityTreeRenderer() -------- BEGIN -----------"; // NOTE: we don't need to delete _entitiesScriptEngine because it's owned by the application and gets cleaned up // automatically but we do need to delete our sandbox script engine. @@ -71,6 +72,7 @@ EntityTreeRenderer::~EntityTreeRenderer() { delete _sandboxScriptEngine; _sandboxScriptEngine = NULL; } + qDebug() << "EntityTreeRenderer::~EntityTreeRenderer() -------- DONE -----------"; } void EntityTreeRenderer::clear() { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 0919dc5d59..fe568ab077 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,33 +94,78 @@ ScriptEngine::ScriptEngine(const QString& scriptContents, const QString& fileNam _isUserLoaded(false), _arrayBufferClass(new ArrayBufferClass(this)) { + qDebug() << "ScriptEngine::ScriptEngine() " << getFilename() << "[" << ((void*)this) << "]" << " -------- BEGIN -----------"; + _allScriptsMutex.lock(); _allKnownScriptEngines.insert(this); + _allScriptsMutex.unlock(); + qDebug() << "ScriptEngine::ScriptEngine() " << getFilename() << " -------- DONE -----------"; } ScriptEngine::~ScriptEngine() { - _allKnownScriptEngines.remove(this); + qDebug() << "ScriptEngine::~ScriptEngine() " << getFilename() << "[" << ((void*)this) << "]" << " -------- BEGIN -----------"; + if (!_stoppingAllScripts) { + _allScriptsMutex.lock(); + qDebug() << "ScriptEngine::~ScriptEngine() " << getFilename() << " removing from list of scripts"; + _allKnownScriptEngines.remove(this); + _allScriptsMutex.unlock(); + } else { + qDebug() << "ScriptEngine::~ScriptEngine() " << getFilename() << " NOT removing from list of scripts here..."; + } + qDebug() << "ScriptEngine::~ScriptEngine() " << getFilename() << " -------- DONE -----------"; } QSet ScriptEngine::_allKnownScriptEngines; +QMutex ScriptEngine::_allScriptsMutex; +bool ScriptEngine::_stoppingAllScripts = false; void ScriptEngine::stopAllScripts(QObject* application) { + + qDebug() << "ScriptEngine::stopAllScripts() -------- BEGIN -----------"; + _allScriptsMutex.lock(); + _stoppingAllScripts = true; + QMutableSetIterator i(_allKnownScriptEngines); while (i.hasNext()) { ScriptEngine* scriptEngine = i.next(); + qDebug() << "ScriptEngine::stopAllScripts() scriptEngine: " << (void*)scriptEngine; + + QString scriptName = scriptEngine->getFilename(); + + qDebug() << "ScriptEngine::stopAllScripts() considering script: " << scriptName << "evaluatePending:" << scriptEngine->evaluatePending(); + // NOTE: typically all script engines are running. But there's at least one known exception to this, the // "entities sandbox" which is only used to evaluate entities scripts to test their validity before using // them. We don't need to stop scripts that aren't running. if (scriptEngine->isRunning()) { + if (scriptEngine->evaluatePending()) { + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__ << "waiting for evaluation to complete...."; + QEventLoop loop; + QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit); + loop.exec(); + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__ << "evaluation complete...."; + } + + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__; + QEventLoop loop; QObject::connect(scriptEngine, &ScriptEngine::doneRunning, &loop, &QEventLoop::quit); - - scriptEngine->disconnect(application); - scriptEngine->stop(); + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__; + scriptEngine->disconnect(application); + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__; + scriptEngine->stop(); + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__; loop.exec(); + qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__; + i.remove(); + } else { + qDebug() << "ScriptEngine::stopAllScripts() script: " << scriptName << " was not running... skipping it's stop() call."; } } + _stoppingAllScripts = false; + _allScriptsMutex.unlock(); + qDebug() << "ScriptEngine::stopAllScripts() -------- DONE -----------"; } QString ScriptEngine::getFilename() const { @@ -347,6 +392,8 @@ void ScriptEngine::evaluate() { } QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { + qDebug() << "ScriptEngine::evaluate() " << getFilename() << " line:" << __LINE__; + _evaluatePending = true; QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); @@ -354,6 +401,8 @@ QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileN } emit evaluationFinished(result, hasUncaughtException()); clearExceptions(); + _evaluatePending = false; + qDebug() << "ScriptEngine::evaluate() " << getFilename() << " line:" << __LINE__; return result; } @@ -370,30 +419,36 @@ void ScriptEngine::sendAvatarBillboardPacket() { } void ScriptEngine::run() { + qDebug() << "ScriptEngine::run() " << getFilename() << " -------- BEGIN -----------"; + if (!_isInitialized) { + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; init(); + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; } + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; _isRunning = true; _isFinished = false; + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; emit runningStateChanged(); + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; QScriptValue result = evaluate(_scriptContents); - if (hasUncaughtException()) { - int line = uncaughtExceptionLineNumber(); - qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString(); - emit errorMessage("Uncaught exception at (" + _fileNameString + ") line" + QString::number(line) + ":" + result.toString()); - clearExceptions(); - } + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; QElapsedTimer startTime; startTime.start(); int thisFrame = 0; + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; auto nodeList = DependencyManager::get(); qint64 lastUpdate = usecTimestampNow(); + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; while (!_isFinished) { int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec if (usecToSleep > 0) { @@ -535,6 +590,7 @@ void ScriptEngine::run() { } lastUpdate = now; } + qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; stopAllTimers(); // make sure all our timers are stopped if the script is ending @@ -565,6 +621,7 @@ void ScriptEngine::run() { emit runningStateChanged(); emit doneRunning(); + qDebug() << "ScriptEngine::run() " << getFilename() << " -------- DONE -----------" << " line:" << __LINE__; } // NOTE: This is private because it must be called on the same thread that created the timers, which is why @@ -579,6 +636,7 @@ void ScriptEngine::stopAllTimers() { } void ScriptEngine::stop() { + qDebug() << "ScriptEngine::stop() " << getFilename() << "[" << ((void*)this) << "]"; _isFinished = true; emit runningStateChanged(); } @@ -710,8 +768,11 @@ void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { } void ScriptEngine::load(const QString& loadFile) { + qDebug() << "ScriptEngine::load() " << getFilename() << "[" << ((void*)this) << "]" << "line:" << __LINE__; QUrl url = resolvePath(loadFile); + qDebug() << "ScriptEngine::load() " << getFilename() << "[" << ((void*)this) << "]" << "line:" << __LINE__; emit loadScript(url.toString(), false); + qDebug() << "ScriptEngine::load() " << getFilename() << "[" << ((void*)this) << "]" << "line:" << __LINE__; } void ScriptEngine::nodeKilled(SharedNodePointer node) { diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 8963a1e0f6..a672646c79 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -85,6 +85,7 @@ public: bool isFinished() const { return _isFinished; } bool isRunning() const { return _isRunning; } + bool evaluatePending() const { return _evaluatePending; } void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } bool isUserLoaded() const { return _isUserLoaded; } @@ -131,6 +132,7 @@ protected: QString _parentURL; bool _isFinished; bool _isRunning; + bool _evaluatePending = false; bool _isInitialized; bool _isAvatar; QTimer* _avatarIdentityTimer; @@ -167,6 +169,8 @@ private slots: private: static QSet _allKnownScriptEngines; + static QMutex _allScriptsMutex; + static bool _stoppingAllScripts; }; #endif // hifi_ScriptEngine_h