Merge pull request #6592 from huffman/script-engine-fixes

Fix concurrency issues for bools in ScriptEngine
This commit is contained in:
James B. Pollack 2015-12-10 10:48:54 -08:00
commit d21e4ff5e1
4 changed files with 94 additions and 48 deletions

View file

@ -4036,7 +4036,7 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("Clipboard", clipboardScriptable);
connect(scriptEngine, SIGNAL(finished(const QString&)), clipboardScriptable, SLOT(deleteLater()));
connect(scriptEngine, SIGNAL(finished(const QString&)), this, SLOT(scriptFinished(const QString&)));
connect(scriptEngine, &ScriptEngine::finished, this, &Application::scriptFinished, Qt::DirectConnection);
connect(scriptEngine, SIGNAL(loadScript(const QString&, bool)), this, SLOT(loadScript(const QString&, bool)));
connect(scriptEngine, SIGNAL(reloadScript(const QString&, bool)), this, SLOT(reloadScript(const QString&, bool)));
@ -4282,10 +4282,13 @@ ScriptEngine* Application::loadScript(const QString& scriptFilename, bool isUser
QUrl scriptUrl(scriptFilename);
const QString& scriptURLString = scriptUrl.toString();
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
{
QReadLocker lock(&_scriptEnginesHashLock);
if (_scriptEnginesHash.contains(scriptURLString) && loadScriptFromEditor
&& !_scriptEnginesHash[scriptURLString]->isFinished()) {
return _scriptEnginesHash[scriptURLString];
return _scriptEnginesHash[scriptURLString];
}
}
ScriptEngine* scriptEngine = new ScriptEngine(NO_SCRIPT, "", &_controllerScriptingInterface);
@ -4325,7 +4328,11 @@ void Application::reloadScript(const QString& scriptName, bool isUserLoaded) {
void Application::handleScriptEngineLoaded(const QString& scriptFilename) {
ScriptEngine* scriptEngine = qobject_cast<ScriptEngine*>(sender());
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
{
QWriteLocker lock(&_scriptEnginesHashLock);
_scriptEnginesHash.insertMulti(scriptFilename, scriptEngine);
}
_runningScriptsWidget->setRunningScripts(getRunningScripts());
UserActivityLogger::getInstance().loadedScript(scriptFilename);
@ -4340,55 +4347,88 @@ void Application::handleScriptLoadError(const QString& scriptFilename) {
QMessageBox::warning(getWindow(), "Error Loading Script", scriptFilename + " failed to load.");
}
void Application::scriptFinished(const QString& scriptName) {
const QString& scriptURLString = QUrl(scriptName).toString();
QHash<QString, ScriptEngine*>::iterator it = _scriptEnginesHash.find(scriptURLString);
if (it != _scriptEnginesHash.end()) {
_scriptEnginesHash.erase(it);
_runningScriptsWidget->scriptStopped(scriptName);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
QStringList Application::getRunningScripts() {
QReadLocker lock(&_scriptEnginesHashLock);
return _scriptEnginesHash.keys();
}
ScriptEngine* Application::getScriptEngine(const QString& scriptHash) {
QReadLocker lock(&_scriptEnginesHashLock);
return _scriptEnginesHash.value(scriptHash, nullptr);
}
void Application::scriptFinished(const QString& scriptName, ScriptEngine* engine) {
bool removed = false;
{
QWriteLocker lock(&_scriptEnginesHashLock);
const QString& scriptURLString = QUrl(scriptName).toString();
for (auto it = _scriptEnginesHash.find(scriptURLString); it != _scriptEnginesHash.end(); ++it) {
if (it.value() == engine) {
_scriptEnginesHash.erase(it);
removed = true;
break;
}
}
}
if (removed) {
postLambdaEvent([this, scriptName]() {
_runningScriptsWidget->scriptStopped(scriptName);
_runningScriptsWidget->setRunningScripts(getRunningScripts());
});
}
}
void Application::stopAllScripts(bool restart) {
if (restart) {
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
auto scriptCache = DependencyManager::get<ScriptCache>();
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
{
QReadLocker lock(&_scriptEnginesHashLock);
if (restart) {
// Delete all running scripts from cache so that they are re-downloaded when they are restarted
auto scriptCache = DependencyManager::get<ScriptCache>();
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (!it.value()->isFinished()) {
scriptCache->deleteScript(it.key());
if (!it.value()->isFinished()) {
scriptCache->deleteScript(it.key());
}
}
}
}
// Stop and possibly restart all currently running scripts
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (it.value()->isFinished()) {
continue;
// Stop and possibly restart all currently running scripts
for (QHash<QString, ScriptEngine*>::const_iterator it = _scriptEnginesHash.constBegin();
it != _scriptEnginesHash.constEnd(); it++) {
if (it.value()->isFinished()) {
continue;
}
if (restart && it.value()->isUserLoaded()) {
connect(it.value(), &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
reloadScript(scriptName);
});
}
QMetaObject::invokeMethod(it.value(), "stop");
//it.value()->stop();
qCDebug(interfaceapp) << "stopping script..." << it.key();
}
if (restart && it.value()->isUserLoaded()) {
connect(it.value(), SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
}
it.value()->stop();
qCDebug(interfaceapp) << "stopping script..." << it.key();
}
getMyAvatar()->clearScriptableSettings();
}
bool Application::stopScript(const QString& scriptHash, bool restart) {
bool stoppedScript = false;
if (_scriptEnginesHash.contains(scriptHash)) {
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
if (restart) {
auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(QUrl(scriptHash));
connect(scriptEngine, SIGNAL(finished(const QString&)), SLOT(reloadScript(const QString&)));
{
QReadLocker lock(&_scriptEnginesHashLock);
if (_scriptEnginesHash.contains(scriptHash)) {
ScriptEngine* scriptEngine = _scriptEnginesHash[scriptHash];
if (restart) {
auto scriptCache = DependencyManager::get<ScriptCache>();
scriptCache->deleteScript(QUrl(scriptHash));
connect(scriptEngine, &ScriptEngine::finished, this, [this](QString scriptName, ScriptEngine* engine) {
reloadScript(scriptName);
});
}
scriptEngine->stop();
stoppedScript = true;
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
}
scriptEngine->stop();
stoppedScript = true;
qCDebug(interfaceapp) << "stopping script..." << scriptHash;
}
if (_scriptEnginesHash.empty()) {
getMyAvatar()->clearScriptableSettings();
@ -4407,6 +4447,7 @@ void Application::reloadOneScript(const QString& scriptName) {
}
void Application::loadDefaultScripts() {
QReadLocker lock(&_scriptEnginesHashLock);
if (!_scriptEnginesHash.contains(DEFAULT_SCRIPTS_JS_URL)) {
loadScript(DEFAULT_SCRIPTS_JS_URL);
}

View file

@ -199,8 +199,8 @@ public:
NodeToJurisdictionMap& getEntityServerJurisdictions() { return _entityServerJurisdictions; }
QStringList getRunningScripts() { return _scriptEnginesHash.keys(); }
ScriptEngine* getScriptEngine(const QString& scriptHash) { return _scriptEnginesHash.value(scriptHash, NULL); }
QStringList getRunningScripts();
ScriptEngine* getScriptEngine(const QString& scriptHash);
float getRenderResolutionScale() const;
@ -333,7 +333,7 @@ private slots:
void loadSettings();
void saveSettings();
void scriptFinished(const QString& scriptName);
void scriptFinished(const QString& scriptName, ScriptEngine* engine);
void saveScripts();
void reloadScript(const QString& scriptName, bool isUserLoaded = true);
@ -501,6 +501,7 @@ private:
TouchEvent _lastTouchEvent;
QReadWriteLock _scriptEnginesHashLock;
RunningScriptsWidget* _runningScriptsWidget;
QHash<QString, ScriptEngine*> _scriptEnginesHash;
bool _runningScriptsWidgetWasVisible;

View file

@ -183,7 +183,7 @@ void ScriptEngine::runInThread() {
QSet<ScriptEngine*> ScriptEngine::_allKnownScriptEngines;
QMutex ScriptEngine::_allScriptsMutex;
bool ScriptEngine::_stoppingAllScripts = false;
std::atomic<bool> ScriptEngine::_stoppingAllScripts { false };
void ScriptEngine::stopAllScripts(QObject* application) {
_allScriptsMutex.lock();
@ -752,7 +752,7 @@ void ScriptEngine::run() {
}
if (_wantSignals) {
emit finished(_fileNameString);
emit finished(_fileNameString, this);
}
_isRunning = false;
@ -775,6 +775,10 @@ void ScriptEngine::stopAllTimers() {
void ScriptEngine::stop() {
if (!_isFinished) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stop");
return;
}
_isFinished = true;
if (_wantSignals) {
emit runningStateChanged();

View file

@ -153,7 +153,7 @@ signals:
void errorLoadingScript(const QString& scriptFilename);
void update(float deltaTime);
void scriptEnding();
void finished(const QString& fileNameString);
void finished(const QString& fileNameString, ScriptEngine* engine);
void cleanupMenuItem(const QString& menuItemString);
void printedMessage(const QString& message);
void errorMessage(const QString& message);
@ -166,8 +166,8 @@ signals:
protected:
QString _scriptContents;
QString _parentURL;
bool _isFinished { false };
bool _isRunning { false };
std::atomic<bool> _isFinished { false };
std::atomic<bool> _isRunning { false };
int _evaluatesPending { 0 };
bool _isInitialized { false };
QHash<QTimer*, QScriptValue> _timerFunctionMap;
@ -193,7 +193,7 @@ protected:
Quat _quatLibrary;
Vec3 _vec3Library;
ScriptUUID _uuidLibrary;
bool _isUserLoaded { false };
std::atomic<bool> _isUserLoaded { false };
bool _isReloading { false };
ArrayBufferClass* _arrayBufferClass;
@ -206,7 +206,7 @@ protected:
static QSet<ScriptEngine*> _allKnownScriptEngines;
static QMutex _allScriptsMutex;
static bool _stoppingAllScripts;
static std::atomic<bool> _stoppingAllScripts;
};
#endif // hifi_ScriptEngine_h