From febb1fbe13173d8a800843200d6dbd420c86f008 Mon Sep 17 00:00:00 2001 From: Andy Street Date: Fri, 19 Feb 2016 08:21:49 -0800 Subject: [PATCH] WebWorkers: Move web worker impl to JSCExecutor Summary: Part of the plan to make web workers able to call native modules. We will reuse the infrastructure already present in JSCExecutor to allow web workers to call native modules via the Bridge. Reviewed By: mhorowitz Differential Revision: D2926896 fb-gh-sync-id: 259b766c46f79bbb5df9d1c648237b81fc1cc1f9 shipit-source-id: 259b766c46f79bbb5df9d1c648237b81fc1cc1f9 --- .../react/bridge/CatalystInstanceImpl.java | 22 ++- .../common/futures/SimpleSettableFuture.java | 2 +- .../src/main/jni/react/JSCExecutor.cpp | 172 ++++++++++++++---- ReactAndroid/src/main/jni/react/JSCExecutor.h | 63 +++++-- .../src/main/jni/react/JSCWebWorker.cpp | 153 ---------------- .../src/main/jni/react/JSCWebWorker.h | 94 ---------- .../src/main/jni/react/MessageQueueThread.h | 20 ++ 7 files changed, 226 insertions(+), 300 deletions(-) delete mode 100644 ReactAndroid/src/main/jni/react/JSCWebWorker.cpp delete mode 100644 ReactAndroid/src/main/jni/react/JSCWebWorker.h diff --git a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java index cc34bf6ff..0cacb9b47 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java +++ b/ReactAndroid/src/main/java/com/facebook/react/bridge/CatalystInstanceImpl.java @@ -27,6 +27,7 @@ import com.facebook.react.bridge.queue.ReactQueueConfigurationSpec; import com.facebook.react.bridge.queue.QueueThreadExceptionHandler; import com.facebook.react.common.ReactConstants; import com.facebook.react.common.annotations.VisibleForTesting; +import com.facebook.react.common.futures.SimpleSettableFuture; import com.facebook.systrace.Systrace; import com.facebook.systrace.TraceListener; @@ -264,6 +265,10 @@ public class CatalystInstanceImpl implements CatalystInstance { // TODO: tell all APIs to shut down mDestroyed = true; mJavaRegistry.notifyCatalystInstanceDestroy(); + + Systrace.unregisterListener(mTraceListener); + + synchronouslyDisposeBridgeOnJSThread(); mReactQueueConfiguration.destroy(); boolean wasIdle = (mPendingJSCalls.getAndSet(0) == 0); if (!wasIdle && !mBridgeIdleListeners.isEmpty()) { @@ -271,12 +276,19 @@ public class CatalystInstanceImpl implements CatalystInstance { listener.onTransitionToBridgeIdle(); } } + } - Systrace.unregisterListener(mTraceListener); - - // We can access the Bridge from any thread now because we know either we are on the JS thread - // or the JS thread has finished via ReactQueueConfiguration#destroy() - mBridge.dispose(); + private void synchronouslyDisposeBridgeOnJSThread() { + final SimpleSettableFuture bridgeDisposeFuture = new SimpleSettableFuture<>(); + mReactQueueConfiguration.getJSQueueThread().runOnQueue( + new Runnable() { + @Override + public void run() { + mBridge.dispose(); + bridgeDisposeFuture.set(null); + } + }); + bridgeDisposeFuture.getOrThrow(); } @Override diff --git a/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java b/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java index 55fc9eb08..9caf34cd6 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java +++ b/ReactAndroid/src/main/java/com/facebook/react/common/futures/SimpleSettableFuture.java @@ -30,7 +30,7 @@ public class SimpleSettableFuture implements Future { * Sets the result. If another thread has called {@link #get}, they will immediately receive the * value. set or setException must only be called once. */ - public void set(T result) { + public void set(@Nullable T result) { checkNotSet(); mResult = result; mReadyLatch.countDown(); diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp index ceb2564d5..5720905e7 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.cpp +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.cpp @@ -3,9 +3,11 @@ #include "JSCExecutor.h" #include -#include +#include +#include #include #include + #include #include #include @@ -88,6 +90,45 @@ JSCExecutor::JSCExecutor(Bridge *bridge, const std::string& cacheDir) : m_bridge(bridge), m_deviceCacheDir(cacheDir), m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()) { + initOnJSVMThread(); +} + +JSCExecutor::JSCExecutor( + Bridge *bridge, + int workerId, + JSCExecutor *owner, + const std::string& script) : + m_bridge(bridge), + m_workerId(workerId), + m_owner(owner), + m_deviceCacheDir(owner->m_deviceCacheDir), + m_messageQueueThread(MessageQueues::getCurrentMessageQueueThread()) { + // We post initOnJSVMThread here so that the owner doesn't have to wait for + // initialization on its own thread + m_messageQueueThread->runOnQueue([this, script] () { + initOnJSVMThread(); + + installGlobalFunction(m_context, "postMessage", nativePostMessage); + + // TODO(9604438): Protect against script does not exist + std::string scriptSrc = WebWorkerUtil::loadScriptFromAssets(script); + // TODO(9994180): Throw on error + loadApplicationScript(scriptSrc, script); + }); +} + +JSCExecutor::~JSCExecutor() { + *m_isDestroyed = true; + if (m_messageQueueThread->isOnThread()) { + terminateOnJSVMThread(); + } else { + m_messageQueueThread->runOnQueueSync([this] () { + terminateOnJSVMThread(); + }); + } +} + +void JSCExecutor::initOnJSVMThread() { m_context = JSGlobalContextCreateInGroup(nullptr, nullptr); s_globalContextRefToJSCExecutor[m_context] = this; installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate); @@ -114,18 +155,20 @@ JSCExecutor::JSCExecutor(Bridge *bridge, const std::string& cacheDir) : #endif } -JSCExecutor::~JSCExecutor() { - // terminateWebWorker mutates m_webWorkers so collect all the workers to terminate first +void JSCExecutor::terminateOnJSVMThread() { + // terminateOwnedWebWorker mutates m_ownedWorkers so collect all the workers + // to terminate first std::vector workerIds; - for (auto it = m_webWorkers.begin(); it != m_webWorkers.end(); it++) { - workerIds.push_back(it->first); + for (auto& it : m_ownedWorkers) { + workerIds.push_back(it.first); } for (int workerId : workerIds) { - terminateWebWorker(workerId); + terminateOwnedWebWorker(workerId); } s_globalContextRefToJSCExecutor.erase(m_context); JSGlobalContextRelease(m_context); + m_context = nullptr; } void JSCExecutor::loadApplicationScript( @@ -162,6 +205,10 @@ void JSCExecutor::loadApplicationUnbundle( } void JSCExecutor::flush() { + if (m_owner != nullptr) { + // Web workers don't support native modules yet + return; + } // TODO: Make this a first class function instead of evaling. #9317773 std::string calls = executeJSCallWithJSC(m_context, "flushedQueue", std::vector()); m_bridge->callNativeModules(calls, true); @@ -253,56 +300,111 @@ void JSCExecutor::loadModule(uint32_t moduleId) { evaluateScript(m_context, source, sourceUrl); } -// WebWorker impl +int JSCExecutor::addWebWorker( + const std::string& script, + JSValueRef workerRef) { + static std::atomic_int nextWorkerId(1); + int workerId = nextWorkerId++; -JSGlobalContextRef JSCExecutor::getContext() { - return m_context; + auto workerMQT = WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get()); + std::unique_ptr worker; + workerMQT->runOnQueueSync([this, &worker, &script, workerId] () { + worker.reset(new JSCExecutor(m_bridge, workerId, this, script)); + }); + + Object workerObj = Value(m_context, workerRef).asObject(); + workerObj.makeProtected(); + + m_ownedWorkers.emplace(std::piecewise_construct, std::forward_as_tuple(workerId), std::forward_as_tuple(std::move(worker), std::move(workerObj))); + + return workerId; } -std::shared_ptr JSCExecutor::getMessageQueueThread() { - return m_messageQueueThread; +void JSCExecutor::postMessageToOwnedWebWorker(int workerId, JSValueRef message, JSValueRef *exn) { + auto worker = m_ownedWorkers.at(workerId).getExecutor(); + std::string msgString = Value(m_context, message).toJSONString(); + + std::shared_ptr isWorkerDestroyed = worker->m_isDestroyed; + worker->m_messageQueueThread->runOnQueue([isWorkerDestroyed, worker, msgString] () { + if (*isWorkerDestroyed) { + return; + } + worker->receiveMessageFromOwner(msgString); + }); } -void JSCExecutor::onMessageReceived(int workerId, const std::string& json) { - Object& worker = m_webWorkerJSObjs.at(workerId); +void JSCExecutor::postMessageToOwner(JSValueRef msg) { + std::string msgString = Value(m_context, msg).toJSONString(); + std::shared_ptr ownerIsDestroyed = m_owner->m_isDestroyed; + m_owner->m_messageQueueThread->runOnQueue([workerId=m_workerId, owner=m_owner, ownerIsDestroyed, msgString] () { + if (*ownerIsDestroyed) { + return; + } + owner->receiveMessageFromOwnedWebWorker(workerId, msgString); + }); +} - Value onmessageValue = worker.getProperty("onmessage"); +void JSCExecutor::receiveMessageFromOwnedWebWorker(int workerId, const std::string& json) { + Object* workerObj; + try { + workerObj = &m_ownedWorkers.at(workerId).jsObj; + } catch (std::out_of_range& e) { + // Worker was already terminated + return; + } + + Value onmessageValue = workerObj->getProperty("onmessage"); if (onmessageValue.isUndefined()) { return; } - JSValueRef args[] = { JSCWebWorker::createMessageObject(m_context, json) }; + JSValueRef args[] = { createMessageObject(json) }; onmessageValue.asObject().callAsFunction(1, args); flush(); } -int JSCExecutor::addWebWorker(const std::string& script, JSValueRef workerRef) { - static std::atomic_int nextWorkerId(0); - int workerId = nextWorkerId++; +void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) { + CHECK(m_owner) << "Received message in a Executor that doesn't have an owner!"; - m_webWorkers.emplace(std::piecewise_construct, std::forward_as_tuple(workerId), std::forward_as_tuple(workerId, this, script)); - Object workerObj = Value(m_context, workerRef).asObject(); - workerObj.makeProtected(); - m_webWorkerJSObjs.emplace(workerId, std::move(workerObj)); - return workerId; + JSValueRef args[] = { createMessageObject(msgString) }; + Value onmessageValue = Object::getGlobalObject(m_context).getProperty("onmessage"); + onmessageValue.asObject().callAsFunction(1, args); } -void JSCExecutor::postMessageToWebWorker(int workerId, JSValueRef message, JSValueRef *exn) { - JSCWebWorker& worker = m_webWorkers.at(workerId); - worker.postMessage(message); +void JSCExecutor::terminateOwnedWebWorker(int workerId) { + auto worker = m_ownedWorkers.at(workerId).getExecutor(); + std::shared_ptr workerMQT = worker->m_messageQueueThread; + m_ownedWorkers.erase(workerId); + workerMQT->quitSynchronous(); } -void JSCExecutor::terminateWebWorker(int workerId) { - JSCWebWorker& worker = m_webWorkers.at(workerId); - - worker.terminate(); - - m_webWorkers.erase(workerId); - m_webWorkerJSObjs.erase(workerId); +Object JSCExecutor::createMessageObject(const std::string& msgJson) { + Value rebornJSMsg = Value::fromJSON(m_context, String(msgJson.c_str())); + Object messageObject = Object::create(m_context); + messageObject.setProperty("data", rebornJSMsg); + return messageObject; } // Native JS hooks +JSValueRef JSCExecutor::nativePostMessage( + JSContextRef ctx, + JSObjectRef function, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef *exception) { + if (argumentCount != 1) { + *exception = makeJSCException(ctx, "postMessage got wrong number of arguments"); + return JSValueMakeUndefined(ctx); + } + JSValueRef msg = arguments[0]; + JSCExecutor *webWorker = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx)); + + webWorker->postMessageToOwner(msg); + + return JSValueMakeUndefined(ctx); +} static JSValueRef makeInvalidModuleIdJSCException( JSContextRef ctx, @@ -433,7 +535,7 @@ JSValueRef JSCExecutor::nativePostMessageToWorker( return JSValueMakeUndefined(ctx); } - executor->postMessageToWebWorker((int) workerDouble, arguments[1], exception); + executor->postMessageToOwnedWebWorker((int) workerDouble, arguments[1], exception); return JSValueMakeUndefined(ctx); } @@ -464,7 +566,7 @@ JSValueRef JSCExecutor::nativeTerminateWorker( return JSValueMakeUndefined(ctx); } - executor->terminateWebWorker((int) workerDouble); + executor->terminateOwnedWebWorker((int) workerDouble); return JSValueMakeUndefined(ctx); } diff --git a/ReactAndroid/src/main/jni/react/JSCExecutor.h b/ReactAndroid/src/main/jni/react/JSCExecutor.h index dbba889ab..c60f33422 100644 --- a/ReactAndroid/src/main/jni/react/JSCExecutor.h +++ b/ReactAndroid/src/main/jni/react/JSCExecutor.h @@ -6,9 +6,10 @@ #include #include #include + #include "Executor.h" #include "JSCHelpers.h" -#include "JSCWebWorker.h" +#include "Value.h" namespace facebook { namespace react { @@ -23,10 +24,26 @@ private: std::string cacheDir_; }; -class JSCExecutor : public JSExecutor, public JSCWebWorkerOwner { +class JSCExecutor; +class WorkerRegistration : public noncopyable { +public: + explicit WorkerRegistration(std::unique_ptr executor, Object jsObj) : + jsObj(std::move(jsObj)), + executor(std::move(executor)) {} + + JSCExecutor* getExecutor() { + return executor.get(); + } + + Object jsObj; +private: + std::unique_ptr executor; +}; + +class JSCExecutor : public JSExecutor { public: /** - * Should be invoked from the JS thread. + * Must be invoked from thread this Executor will run on. */ explicit JSCExecutor(Bridge *bridge, const std::string& cacheDir); ~JSCExecutor() override; @@ -56,25 +73,40 @@ public: virtual void handleMemoryPressureCritical() override; void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback); - virtual void onMessageReceived(int workerId, const std::string& message) override; - virtual JSGlobalContextRef getContext() override; - virtual std::shared_ptr getMessageQueueThread() override; private: JSGlobalContextRef m_context; - std::unordered_map m_webWorkers; - std::unordered_map m_webWorkerJSObjs; Bridge *m_bridge; + int m_workerId = 0; // if this is a worker executor, this is non-zero + JSCExecutor *m_owner = nullptr; // if this is a worker executor, this is non-null + std::shared_ptr m_isDestroyed = std::shared_ptr(new bool(false)); + std::unordered_map m_ownedWorkers; std::string m_deviceCacheDir; std::shared_ptr m_messageQueueThread; std::unique_ptr m_unbundle; - int addWebWorker(const std::string& script, JSValueRef workerRef); - void postMessageToWebWorker(int worker, JSValueRef message, JSValueRef *exn); + /** + * WebWorker constructor. Must be invoked from thread this Executor will run on. + */ + explicit JSCExecutor( + Bridge *bridge, + int workerId, + JSCExecutor *owner, + const std::string& script); + + void initOnJSVMThread(); + void terminateOnJSVMThread(); void flush(); - void terminateWebWorker(int worker); - void loadModule(uint32_t moduleId); void flushQueueImmediate(std::string queueJSON); + void loadModule(uint32_t moduleId); + + int addWebWorker(const std::string& script, JSValueRef workerRef); + void postMessageToOwnedWebWorker(int worker, JSValueRef message, JSValueRef *exn); + void postMessageToOwner(JSValueRef result); + void receiveMessageFromOwnedWebWorker(int workerId, const std::string& message); + void receiveMessageFromOwner(const std::string &msgString); + void terminateOwnedWebWorker(int worker); + Object createMessageObject(const std::string& msgData); static JSValueRef nativeStartWorker( JSContextRef ctx, @@ -97,6 +129,13 @@ private: size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception); + static JSValueRef nativePostMessage( + JSContextRef ctx, + JSObjectRef function, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef *exception); static JSValueRef nativeRequire( JSContextRef ctx, JSObjectRef function, diff --git a/ReactAndroid/src/main/jni/react/JSCWebWorker.cpp b/ReactAndroid/src/main/jni/react/JSCWebWorker.cpp deleted file mode 100644 index e8e710bdf..000000000 --- a/ReactAndroid/src/main/jni/react/JSCWebWorker.cpp +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include "JSCWebWorker.h" - -#include -#include -#include -#include - -#include -#include - -#include "JSCHelpers.h" -#include "MessageQueueThread.h" -#include "Platform.h" -#include "Value.h" - -#include - -namespace facebook { -namespace react { - -// TODO(9604425): thread safety -static std::unordered_map s_globalContextRefToJSCWebWorker; - -JSCWebWorker::JSCWebWorker(int id, JSCWebWorkerOwner *owner, std::string scriptSrc) : - id_(id), - scriptName_(std::move(scriptSrc)), - owner_(owner) { - ownerMessageQueueThread_ = owner->getMessageQueueThread(); - CHECK(ownerMessageQueueThread_) << "Owner MessageQueue must not be null"; - workerMessageQueueThread_ = WebWorkerUtil::createWebWorkerThread(id, ownerMessageQueueThread_.get()); - CHECK(workerMessageQueueThread_) << "Failed to create worker thread"; - - workerMessageQueueThread_->runOnQueue([this] () { - initJSVMAndLoadScript(); - }); -} - - -JSCWebWorker::~JSCWebWorker() { - CHECK(isTerminated()) << "Didn't terminate the web worker before releasing it!";; -} - -void JSCWebWorker::postMessage(JSValueRef msg) { - std::string msgString = Value(owner_->getContext(), msg).toJSONString(); - - workerMessageQueueThread_->runOnQueue([this, msgString] () { - if (isTerminated()) { - return; - } - - JSValueRef args[] = { createMessageObject(context_, msgString) }; - Value onmessageValue = Object::getGlobalObject(context_).getProperty("onmessage"); - onmessageValue.asObject().callAsFunction(1, args); - }); -} - -void JSCWebWorker::terminate() { - if (isTerminated()) { - return; - } - isTerminated_.store(true, std::memory_order_release); - - if (workerMessageQueueThread_->isOnThread()) { - terminateOnWorkerThread(); - } else { - std::mutex signalMutex; - std::condition_variable signalCv; - bool terminationComplete = false; - - workerMessageQueueThread_->runOnQueue([&] () mutable { - std::lock_guard lock(signalMutex); - - terminateOnWorkerThread(); - terminationComplete = true; - - signalCv.notify_one(); - }); - - std::unique_lock lock(signalMutex); - signalCv.wait(lock, [&terminationComplete] { return terminationComplete; }); - } -} - -void JSCWebWorker::terminateOnWorkerThread() { - s_globalContextRefToJSCWebWorker.erase(context_); - JSGlobalContextRelease(context_); - context_ = nullptr; - workerMessageQueueThread_->quitSynchronous(); -} - -bool JSCWebWorker::isTerminated() { - return isTerminated_.load(std::memory_order_acquire); -} - -void JSCWebWorker::initJSVMAndLoadScript() { - CHECK(!isTerminated()) << "Worker was already finished!"; - CHECK(!context_) << "Worker JS VM was already created!"; - - context_ = JSGlobalContextCreateInGroup( - NULL, // use default JS 'global' object - NULL // create new group (i.e. new VM) - ); - s_globalContextRefToJSCWebWorker[context_] = this; - - // TODO(9604438): Protect against script does not exist - std::string script = WebWorkerUtil::loadScriptFromAssets(scriptName_); - evaluateScript(context_, String(script.c_str()), String(scriptName_.c_str())); - - installGlobalFunction(context_, "postMessage", nativePostMessage); -} - -void JSCWebWorker::postMessageToOwner(JSValueRef msg) { - std::string msgString = Value(context_, msg).toJSONString(); - ownerMessageQueueThread_->runOnQueue([this, msgString] () { - owner_->onMessageReceived(id_, msgString); - }); -} - -JSValueRef JSCWebWorker::nativePostMessage( - JSContextRef ctx, - JSObjectRef function, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[], - JSValueRef *exception) { - if (argumentCount != 1) { - *exception = makeJSCException(ctx, "postMessage got wrong number of arguments"); - return JSValueMakeUndefined(ctx); - } - JSValueRef msg = arguments[0]; - JSCWebWorker *webWorker = s_globalContextRefToJSCWebWorker.at(JSContextGetGlobalContext(ctx)); - - if (webWorker->isTerminated()) { - return JSValueMakeUndefined(ctx); - } - - webWorker->postMessageToOwner(msg); - - return JSValueMakeUndefined(ctx); -} - -/*static*/ -Object JSCWebWorker::createMessageObject(JSContextRef context, const std::string& msgJson) { - Value rebornJSMsg = Value::fromJSON(context, String(msgJson.c_str())); - Object messageObject = Object::create(context); - messageObject.setProperty("data", rebornJSMsg); - return std::move(messageObject); -} - -} -} diff --git a/ReactAndroid/src/main/jni/react/JSCWebWorker.h b/ReactAndroid/src/main/jni/react/JSCWebWorker.h deleted file mode 100644 index 73d983cdf..000000000 --- a/ReactAndroid/src/main/jni/react/JSCWebWorker.h +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2004-present Facebook. All Rights Reserved. - -#include -#include -#include -#include -#include -#include - -#include - -#include "Value.h" - -namespace facebook { -namespace react { - -class MessageQueueThread; - -/** - * A class that can own the lifecycle, receive messages from, and dispatch messages - * to JSCWebWorkers. - */ -class JSCWebWorkerOwner { -public: - /** - * Called when a worker has posted a message with `postMessage`. - */ - virtual void onMessageReceived(int workerId, const std::string& message) = 0; - virtual JSGlobalContextRef getContext() = 0; - - /** - * Should return the owner's MessageQueueThread. Calls to onMessageReceived will be enqueued - * on this thread. - */ - virtual std::shared_ptr getMessageQueueThread() = 0; -}; - -/** - * Implementation of a web worker for JSC. The web worker should be created from the owner's - * (e.g., owning JSCExecutor instance) JS MessageQueueThread. The worker is responsible for - * creating its own MessageQueueThread. - * - * During operation, the JSCExecutor should call postMessage **from its own MessageQueueThread** - * to send messages to the worker. The worker will handle enqueueing those messages on its own - * MessageQueueThread as appropriate. When the worker has a message to post to the owner, it will - * enqueue a call to owner->onMessageReceived on the owner's MessageQueueThread. - */ -class JSCWebWorker { -public: - explicit JSCWebWorker(int id, JSCWebWorkerOwner *owner, std::string script); - ~JSCWebWorker(); - - /** - * Post a message to be received by the worker on its thread. This must be called from - * ownerMessageQueueThread_. - */ - void postMessage(JSValueRef msg); - - /** - * Synchronously quits the current worker and cleans up its VM. - */ - void terminate(); - - /** - * Whether terminate() has been called on this worker. - */ - bool isTerminated(); - - static Object createMessageObject(JSContextRef context, const std::string& msgData); -private: - void initJSVMAndLoadScript(); - void postRunnableToEventLoop(std::function&& runnable); - void postMessageToOwner(JSValueRef result); - void terminateOnWorkerThread(); - - int id_; - std::atomic_bool isTerminated_ = ATOMIC_VAR_INIT(false); - std::string scriptName_; - JSCWebWorkerOwner *owner_ = nullptr; - std::shared_ptr ownerMessageQueueThread_; - std::unique_ptr workerMessageQueueThread_; - JSGlobalContextRef context_ = nullptr; - - static JSValueRef nativePostMessage( - JSContextRef ctx, - JSObjectRef function, - JSObjectRef thisObject, - size_t argumentCount, - const JSValueRef arguments[], - JSValueRef *exception); -}; - -} -} diff --git a/ReactAndroid/src/main/jni/react/MessageQueueThread.h b/ReactAndroid/src/main/jni/react/MessageQueueThread.h index 544b91142..f6352ab0d 100644 --- a/ReactAndroid/src/main/jni/react/MessageQueueThread.h +++ b/ReactAndroid/src/main/jni/react/MessageQueueThread.h @@ -2,7 +2,9 @@ #pragma once +#include #include +#include namespace facebook { namespace react { @@ -14,6 +16,24 @@ class MessageQueueThread { virtual bool isOnThread() = 0; // quitSynchronous() should synchronously ensure that no further tasks will run on the queue. virtual void quitSynchronous() = 0; + + void runOnQueueSync(std::function&& runnable) { + std::mutex signalMutex; + std::condition_variable signalCv; + bool runnableComplete = false; + + runOnQueue([&] () mutable { + std::lock_guard lock(signalMutex); + + runnable(); + runnableComplete = true; + + signalCv.notify_one(); + }); + + std::unique_lock lock(signalMutex); + signalCv.wait(lock, [&runnableComplete] { return runnableComplete; }); + } }; }}