History

Call back to Javascript asynchronously, so that we don't block and the script's engine doesn't have thread conflicts.

Howard Stearns committed 11 years ago

Commit 9fd61901 parent 574a51e8 files · +80 −42Browse files

Showing the changes to libraries/script-engine/src/ScriptEngine.cpp (1 of 8 files). Show all

Unified · Split

libraries/script-engine/src/ScriptEngine.cpp+34−3
@@ -258,6 +258,15 @@ void ScriptEngine::errorInLoadingScript(const QUrl& url) { · 257 unmodified lines
258258 }
259259 }
260260
261+// Even though we never pass AnimVariantMap directly to and from javascript, the queued invokeMethod of
262+// invokeAnimationCallback requires that the type be registered.
263+static QScriptValue animVarMapToScriptValue(QScriptEngine* engine, const AnimVariantMap& parameters) {
264+ return parameters.animVariantMapToScriptValue(engine);
265+}
266+static void animVarMapFromScriptValue(const QScriptValue& value, AnimVariantMap& parameters) {
267+ parameters.animVariantMapFromScriptValue(value);
268+}
269+
261270 void ScriptEngine::init() {
262271 if (_isInitialized) {
263272 return; // only initialize once
@@ -316,6 +325,7 @@ void ScriptEngine::init() { · 52 unmodified lines
316325 registerGlobalObject("Vec3", &_vec3Library);
317326 registerGlobalObject("Uuid", &_uuidLibrary);
318327 registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
328+ qScriptRegisterMetaType(this, animVarMapToScriptValue, animVarMapFromScriptValue);
319329
320330 // constants
321331 globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE)));
@@ -718,6 +728,21 @@ void ScriptEngine::stop() { · 396 unmodified lines
718728 }
719729 }
720730
731+// Other threads can invoke this through invokeMethod, which causes the callback to be asynchronously executed in this script's thread.
732+void ScriptEngine::invokeAnimationCallback(QScriptValue callback, AnimVariantMap parameters) {
733+ checkThread();
734+ QScriptValue javascriptParametgers = parameters.animVariantMapToScriptValue(this);
735+ QScriptValueList callingArguments;
736+ callingArguments << javascriptParametgers;
737+ QScriptValue result = callback.call(QScriptValue(), callingArguments);
738+ // We want to give the result back to the rig, but we don't have the rig or the avatar. But the global does.
739+ // This is sort of like going through DependencyManager.get.
740+ QScriptValue resultHandler = globalObject().property("MyAvatar").property("animationStateHandlerResult");
741+ QScriptValueList resultArguments;
742+ resultArguments << callback << result;
743+ resultHandler.call(QScriptValue(), resultArguments); // Call it synchronously, on our own time and thread.
744+}
745+
721746 void ScriptEngine::timerFired() {
722747 QTimer* callingTimer = reinterpret_cast<QTimer*>(sender());
723748 QScriptValue timerFunction = _timerFunctionMap.value(callingTimer);
@@ -898,14 +923,20 @@ void ScriptEngine::load(const QString& loadFile) { · 174 unmodified lines
898923 }
899924 }
900925
901−// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
902−void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {
926+bool ScriptEngine::checkThread() const {
903927 if (QThread::currentThread() != thread()) {
904928 qDebug() << "*** ERROR *** ScriptEngine::generalHandler() called on wrong thread [" << QThread::currentThread() << "], invoking on correct thread [" << thread() << "]";
905929 assert(false);
906− return;
930+ return true;
907931 }
932+ return false;
933+}
908934
935+// Look up the handler associated with eventName and entityID. If found, evalute the argGenerator thunk and call the handler with those args
936+void ScriptEngine::generalHandler(const EntityItemID& entityID, const QString& eventName, std::function<QScriptValueList()> argGenerator) {
937+ if (checkThread()) {
938+ return;
939+ }
909940 if (!_registeredHandlers.contains(entityID)) {
910941 return;
911942 }