From fd93b99f03fc3e8a8ccb0b19b93377ab8c98122e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 24 Feb 2015 14:02:46 -0800 Subject: [PATCH] more hacking on script shutdown behavior --- interface/src/Application.cpp | 12 +- .../src/EntityTreeRenderer.cpp | 3 - libraries/script-engine/src/ScriptEngine.cpp | 264 ++++++++++++++---- libraries/script-engine/src/ScriptEngine.h | 9 +- 4 files changed, 231 insertions(+), 57 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 32854a1781..7184021239 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3552,9 +3552,11 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUserLoaded, bool loadScriptFromEditor, bool activateMainWindow) { - qDebug() << "Application::loadScript() " << scriptFilename << "line:" << __LINE__; + qDebug() << "Application::loadScript() ---- BEGIN ---- Script:" << scriptFilename; + if (isAboutToQuit()) { - qDebug() << "Application::loadScript() WHILE QUITTING.... what to do????" << scriptFilename << "line:" << __LINE__; + qDebug() << "Requests to load scripts while quitting are ignored. Script:" << scriptFilename; + qDebug() << "Application::loadScript() ---- END ---- Script:" << scriptFilename; return NULL; } @@ -3563,6 +3565,8 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor && !_scriptEnginesHash[scriptURLString]->isFinished()) { + qDebug() << "Application::loadScript() from _scriptEnginesHash[scriptURLString].... Script:" << scriptFilename; + qDebug() << "Application::loadScript() ---- END ---- Script:" << scriptFilename; return _scriptEnginesHash[scriptURLString]; } @@ -3581,6 +3585,7 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser connect(scriptEngine, &ScriptEngine::errorLoadingScript, this, &Application::handleScriptLoadError); // get the script engine object to load the script at the designated script URL + qDebug() << "Application::loadScript() about to call loadURL() scriptUrl:" << scriptUrl; scriptEngine->loadURL(scriptUrl); } @@ -3589,6 +3594,9 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser _window->activateWindow(); } + qDebug() << "Application::loadScript() newly created scriptEngine.... Script:" << scriptFilename; + qDebug() << "Application::loadScript() ---- END ---- Script:" << scriptFilename; + return scriptEngine; } diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 9deafe17da..8648cea285 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -59,10 +59,8 @@ 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. - if (_sandboxScriptEngine) { // NOTE: is it possible this is a problem? I think that we hook the script engine object up to a deleteLater() // call inside of registerScriptEngineWithApplicationServices() but do we not call that for _sandboxScriptEngine??? @@ -72,7 +70,6 @@ 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 fe568ab077..e99cbea0e5 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -94,33 +94,32 @@ 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() { - qDebug() << "ScriptEngine::~ScriptEngine() " << getFilename() << "[" << ((void*)this) << "]" << " -------- BEGIN -----------"; + qDebug() << "ScriptEngine::~ScriptEngine() ------- BEGIN ------- script:" << getFilename(); + // 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 + // because that method removes scripts from its list as it iterates them 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 -----------"; + qDebug() << "ScriptEngine::~ScriptEngine() ------- END ------- script:" << getFilename(); } QSet ScriptEngine::_allKnownScriptEngines; QMutex ScriptEngine::_allScriptsMutex; bool ScriptEngine::_stoppingAllScripts = false; +bool ScriptEngine::_doneRunningThisScript = false; void ScriptEngine::stopAllScripts(QObject* application) { + qDebug() << "ScriptEngine::stopAllScripts() ------- BEGIN -------"; - qDebug() << "ScriptEngine::stopAllScripts() -------- BEGIN -----------"; _allScriptsMutex.lock(); _stoppingAllScripts = true; @@ -128,44 +127,92 @@ void ScriptEngine::stopAllScripts(QObject* application) { 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(); + qDebug() << "ScriptEngine::stopAllScripts() considering:" << scriptName + << "isRunning():" << scriptEngine->isRunning() + << "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 the script is running, but still evaluating then we need to wait for its evaluation step to + // complete. After that we can handle the stop process appropriately 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...."; + while (scriptEngine->evaluatePending()) { + QEventLoop loop; + QObject::connect(scriptEngine, &ScriptEngine::evaluationFinished, &loop, &QEventLoop::quit); + qDebug() << "ScriptEngine::stopAllScripts() while(evaluatePending()) STARTING loop.exec() script:" << scriptName; + loop.exec(); + qDebug() << "ScriptEngine::stopAllScripts() while(evaluatePending()) AFTER loop.exec() script:" << scriptName; + } } - qDebug() << "ScriptEngine::stopAllScripts() handling script: " << scriptName << "line:" << __LINE__; - - QEventLoop loop; - QObject::connect(scriptEngine, &ScriptEngine::doneRunning, &loop, &QEventLoop::quit); - - 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__; + + qDebug() << "ScriptEngine::stopAllScripts() -- WAITING for waitTillDoneRunning() script:" << scriptName; + scriptEngine->waitTillDoneRunning(); + qDebug() << "ScriptEngine::stopAllScripts() -- AFTER waitTillDoneRunning() script:" << scriptName; i.remove(); } else { - qDebug() << "ScriptEngine::stopAllScripts() script: " << scriptName << " was not running... skipping it's stop() call."; } } _stoppingAllScripts = false; _allScriptsMutex.unlock(); - qDebug() << "ScriptEngine::stopAllScripts() -------- DONE -----------"; + qDebug() << "ScriptEngine::stopAllScripts() ------- DONE -------"; +} + + +void ScriptEngine::waitTillDoneRunning() { + QString scriptName = getFilename(); + qDebug() << "ScriptEngine::waitTillDoneRunning() ------- BEGIN ------- script:" << scriptName; + qDebug() << " _isFinished:" << _isFinished; + qDebug() << " _isRunning:" << _isRunning; + qDebug() << " _isInitialized:" << _isInitialized; + qDebug() << " _evaluatesPending:" << _evaluatesPending; + + if (_isRunning) { + qDebug() << "ScriptEngine::waitTillDoneRunning() SETTING _doneRunningThisScript = false ################################### line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + _doneRunningThisScript = false; + _isWaitingForDoneRunning = true; + + // What can we do here??? + // we will be calling this on the main Application thread, inside of stopAllScripts() + // we want the application thread to continue to process events, because the script will + // likely need to marshall messages across to the main thread. + while (!_doneRunningThisScript && _isRunning) { + + // and run a QEventLoop??? + QEventLoop loop; + QObject::connect(this, &ScriptEngine::doneRunning, &loop, &QEventLoop::quit); + qDebug() << "ScriptEngine::waitTillDoneRunning() STARTING loop.exec() script:" << scriptName; + qDebug() << " _doneRunningThisScript:" << _doneRunningThisScript; + qDebug() << " _isRunning:" << _isRunning; + qDebug() << " _isWaitingForDoneRunning:" << _isWaitingForDoneRunning; + qDebug() << " _isFinished:" << _isFinished; + qDebug() << " _isInitialized:" << _isInitialized; + qDebug() << " _evaluatesPending:" << _evaluatesPending; + loop.exec(); + qDebug() << "ScriptEngine::waitTillDoneRunning() AFTER loop.exec() script:" << scriptName; + + // process events + qDebug() << "ScriptEngine::waitTillDoneRunning() STARTING QCoreApplication::processEvents() script:" << scriptName; + QCoreApplication::processEvents(); + qDebug() << "ScriptEngine::waitTillDoneRunning() AFTER QCoreApplication::processEvents() script:" << scriptName; + + if (_doneRunningThisScript) { + qDebug() << "ScriptEngine::waitTillDoneRunning() _doneRunningThisScript after processEvents... breaking!!! script:" << scriptName; + break; + } + } + + _isWaitingForDoneRunning = false; + } + + qDebug() << "ScriptEngine::waitTillDoneRunning() ------- DONE ------- script:" << scriptName; } QString ScriptEngine::getFilename() const { @@ -377,12 +424,23 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func } void ScriptEngine::evaluate() { + if (_stoppingAllScripts) { + qDebug() << "ScriptEngine::evaluate() while shutting down is ignored..."; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return; // bail early + } + + qDebug() << "ScriptEngine::evaluate() -- BEGIN script:" << getFilename() << "[" << this << "]"; if (!_isInitialized) { init(); } + qDebug() << "ScriptEngine::evaluate() -- about to call own evaluate(program) script:" << getFilename() << "[" << this << "]"; QScriptValue result = evaluate(_scriptContents); + qDebug() << "ScriptEngine::evaluate() -- AFTER own evaluate(program) script:" << getFilename() << "[" << this << "]"; + // TODO: why do we check this twice? It seems like the call to clearExcpetions() in the lower level evaluate call + // will cause this code to never actually run... if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at (" << _fileNameString << ") line" << line << ":" << result.toString(); @@ -392,17 +450,30 @@ void ScriptEngine::evaluate() { } QScriptValue ScriptEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { - qDebug() << "ScriptEngine::evaluate() " << getFilename() << " line:" << __LINE__; - _evaluatePending = true; + if (_stoppingAllScripts) { + qDebug() << "ScriptEngine::evaluate(program) while shutting down is ignored..."; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return QScriptValue(); // bail early + } + + qDebug() << "ScriptEngine::evaluate(program) -- BEGIN script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; + _evaluatesPending++; + qDebug() << "ScriptEngine::evaluate(program) -- after _evaluatesPending++ script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; + qDebug() << "ScriptEngine::evaluate(program) -- BEFORE QScriptEngine::evaluate() script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; QScriptValue result = QScriptEngine::evaluate(program, fileName, lineNumber); + qDebug() << "ScriptEngine::evaluate(program) -- AFTER QScriptEngine::evaluate() script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; if (hasUncaughtException()) { int line = uncaughtExceptionLineNumber(); qDebug() << "Uncaught exception at (" << _fileNameString << " : " << fileName << ") line" << line << ": " << result.toString(); } + _evaluatesPending--; + qDebug() << "ScriptEngine::evaluate(program) -- after _evaluatesPending-- script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; + + qDebug() << "ScriptEngine::evaluate(program) -- ABOUT TO emit evaluationFinished() script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; emit evaluationFinished(result, hasUncaughtException()); + qDebug() << "ScriptEngine::evaluate(program) -- AFTER emit evaluationFinished() script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; clearExceptions(); - _evaluatePending = false; - qDebug() << "ScriptEngine::evaluate() " << getFilename() << " line:" << __LINE__; + qDebug() << "ScriptEngine::evaluate(program) -- DONE script:" << getFilename() << "_evaluatesPending:" << _evaluatesPending << "[" << this << "]"; return result; } @@ -419,49 +490,60 @@ void ScriptEngine::sendAvatarBillboardPacket() { } void ScriptEngine::run() { - qDebug() << "ScriptEngine::run() " << getFilename() << " -------- BEGIN -----------"; + // TODO: can we add a short circuit for _stoppingAllScripts here? What does it mean to not start running if + // we're in the process of stopping? + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; if (!_isInitialized) { - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; init(); - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; } - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; _isRunning = true; _isFinished = false; - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; emit runningStateChanged(); - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; QScriptValue result = evaluate(_scriptContents); - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; - 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__; + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + while (!_isFinished) { + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + int usecToSleep = (thisFrame++ * SCRIPT_DATA_CALLBACK_USECS) - startTime.nsecsElapsed() / 1000; // nsec to usec if (usecToSleep > 0) { usleep(usecToSleep); } if (_isFinished) { + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; break; } + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + QCoreApplication::processEvents(); + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + if (_isFinished) { + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; break; } @@ -589,16 +671,38 @@ void ScriptEngine::run() { emit update(deltaTime); } lastUpdate = now; + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + } - qDebug() << "ScriptEngine::run() " << getFilename() << " line:" << __LINE__; + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; stopAllTimers(); // make sure all our timers are stopped if the script is ending + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename(); + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } emit scriptEnding(); + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; // kill the avatar identity timer delete _avatarIdentityTimer; + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + if (_entityScriptingInterface.getEntityPacketSender()->serversExist()) { // release the queue of edit entity messages. _entityScriptingInterface.getEntityPacketSender()->releaseQueuedMessages(); @@ -609,19 +713,44 @@ void ScriptEngine::run() { } } + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + // If we were on a thread, then wait till it's done if (thread()) { + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; thread()->quit(); + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + + qDebug() << "ScriptEngine::run() line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; } emit finished(_fileNameString); - _isRunning = false; + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + _isRunning = false; emit runningStateChanged(); + qDebug() << "ABOUT TO emit doneRunning() script:" << getFilename() << "[" << this << "]"; emit doneRunning(); - qDebug() << "ScriptEngine::run() " << getFilename() << " -------- DONE -----------" << " line:" << __LINE__; + qDebug() << "AFTER emit doneRunning() script:" << getFilename() << "[" << this << "]"; + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } + + qDebug() << "ScriptEngine::run() SETTING _doneRunningThisScript = true ################################### line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + _doneRunningThisScript = true; + + if (_isWaitingForDoneRunning) { + qDebug() << "ScriptEngine::run() ** _isWaitingForDoneRunning == true ** line:" << __LINE__ << " script:" << getFilename() << "[" << this << "]"; + } } // NOTE: This is private because it must be called on the same thread that created the timers, which is why @@ -636,9 +765,10 @@ void ScriptEngine::stopAllTimers() { } void ScriptEngine::stop() { - qDebug() << "ScriptEngine::stop() " << getFilename() << "[" << ((void*)this) << "]"; + qDebug() << "ScriptEngine::stop() -- START -- line:" << __LINE__ << " script:" << getFilename(); _isFinished = true; emit runningStateChanged(); + qDebug() << "ScriptEngine::stop() -- DONE -- line:" << __LINE__ << " script:" << getFilename(); } void ScriptEngine::timerFired() { @@ -674,10 +804,22 @@ QObject* ScriptEngine::setupTimerWithInterval(const QScriptValue& function, int } QObject* ScriptEngine::setInterval(const QScriptValue& function, int intervalMS) { + if (_stoppingAllScripts) { + qDebug() << "Script.setInterval() while shutting down is ignored..."; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return NULL; // bail early + } + return setupTimerWithInterval(function, intervalMS, false); } QObject* ScriptEngine::setTimeout(const QScriptValue& function, int timeoutMS) { + if (_stoppingAllScripts) { + qDebug() << "Script.setTimeout() while shutting down is ignored..."; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return NULL; // bail early + } + return setupTimerWithInterval(function, timeoutMS, true); } @@ -723,6 +865,12 @@ void ScriptEngine::print(const QString& message) { // If no callback is specified, the included files will be loaded synchronously and will block execution until // all of the files have finished loading. void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callback) { + if (_stoppingAllScripts) { + qDebug() << "Script.include() while shutting down is ignored..."; + qDebug() << " includeFiles:" << includeFiles; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return; // bail early + } QList urls; for (QString file : includeFiles) { urls.append(resolvePath(file)); @@ -736,7 +884,9 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac if (contents.isNull()) { qDebug() << "Error loading file: " << url; } else { + qDebug() << "ScriptEngine::include() BEFORE evaluate() line:" << __LINE__ << " parent script:" << getFilename() << "[" << this << "]" << " url:" << url << "_evaluatesPending:" << _evaluatesPending; QScriptValue result = evaluate(contents, url.toString()); + qDebug() << "ScriptEngine::include() AFTER evaluate() line:" << __LINE__ << " parent script:" << getFilename() << "[" << this << "]" << " url:" << url << "_evaluatesPending:" << _evaluatesPending; } } @@ -762,17 +912,31 @@ void ScriptEngine::include(const QStringList& includeFiles, QScriptValue callbac } void ScriptEngine::include(const QString& includeFile, QScriptValue callback) { + if (_stoppingAllScripts) { + qDebug() << "Script.include() while shutting down is ignored..."; + qDebug() << " includeFile:" << includeFile; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return; // bail early + } + QStringList urls; urls.append(includeFile); include(urls, callback); } +// NOTE: The load() command is similar to the include() command except that it loads the script +// 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 void ScriptEngine::load(const QString& loadFile) { - qDebug() << "ScriptEngine::load() " << getFilename() << "[" << ((void*)this) << "]" << "line:" << __LINE__; + if (_stoppingAllScripts) { + qDebug() << "Script.load() while shutting down is ignored..."; + qDebug() << " loadFile:" << loadFile; + qDebug() << " parent script:" << getFilename() << "[" << this << "]"; + return; // bail early + } + 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 a672646c79..51cacd5e58 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -85,7 +86,7 @@ public: bool isFinished() const { return _isFinished; } bool isRunning() const { return _isRunning; } - bool evaluatePending() const { return _evaluatePending; } + bool evaluatePending() const { return _evaluatesPending > 0; } void setUserLoaded(bool isUserLoaded) { _isUserLoaded = isUserLoaded; } bool isUserLoaded() const { return _isUserLoaded; } @@ -95,6 +96,8 @@ public: QString getFilename() const; static void stopAllScripts(QObject* application); + + void waitTillDoneRunning(); public slots: void loadURL(const QUrl& scriptURL); @@ -132,7 +135,8 @@ protected: QString _parentURL; bool _isFinished; bool _isRunning; - bool _evaluatePending = false; + int _evaluatesPending = 0; + bool _isWaitingForDoneRunning = false; bool _isInitialized; bool _isAvatar; QTimer* _avatarIdentityTimer; @@ -171,6 +175,7 @@ private: static QSet _allKnownScriptEngines; static QMutex _allScriptsMutex; static bool _stoppingAllScripts; + static bool _doneRunningThisScript; }; #endif // hifi_ScriptEngine_h