Drop support for webworkers

Reviewed By: AaaChiuuu

Differential Revision: D4916449

fbshipit-source-id: a447233d3b7cfee98db2ce00f1c0505d513e2429
This commit is contained in:
Pieter De Baets
2017-04-25 05:29:45 -07:00
committed by Facebook Github Bot
parent a20882f62e
commit 34bc6bd2ae
64 changed files with 266 additions and 1639 deletions

View File

@@ -16,7 +16,6 @@ LOCAL_SRC_FILES := \
JSCNativeModules.cpp \
JSCPerfStats.cpp \
JSCTracing.cpp \
JSCWebWorker.cpp \
JSIndexedRAMBundle.cpp \
MethodCall.cpp \
ModuleRegistry.cpp \

View File

@@ -129,14 +129,11 @@ cxx_library(
CXXREACT_PUBLIC_HEADERS = [
"CxxNativeModule.h",
"Executor.h",
"ExecutorToken.h",
"ExecutorTokenFactory.h",
"Instance.h",
"JSBigString.h",
"JSBundleType.h",
"JSCExecutor.h",
"JSCNativeModules.h",
"JSCWebWorker.h",
"JSIndexedRAMBundle.h",
"JSModulesUnbundle.h",
"MessageQueueThread.h",

View File

@@ -15,15 +15,15 @@ namespace facebook {
namespace react {
std::function<void(folly::dynamic)> makeCallback(
std::weak_ptr<Instance> instance, ExecutorToken token, const folly::dynamic& callbackId) {
std::weak_ptr<Instance> instance, const folly::dynamic& callbackId) {
if (!callbackId.isInt()) {
throw std::invalid_argument("Expected callback(s) as final argument");
}
auto id = callbackId.getInt();
return [winstance = std::move(instance), token, id](folly::dynamic args) {
return [winstance = std::move(instance), id](folly::dynamic args) {
if (auto instance = winstance.lock()) {
instance->callJSCallback(token, id, std::move(args));
instance->callJSCallback(id, std::move(args));
}
};
}
@@ -75,12 +75,7 @@ folly::dynamic CxxNativeModule::getConstants() {
return constants;
}
bool CxxNativeModule::supportsWebWorkers() {
// TODO(andrews): web worker support in cxxmodules
return false;
}
void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) {
void CxxNativeModule::invoke(unsigned int reactMethodId, folly::dynamic&& params) {
if (reactMethodId >= methods_.size()) {
throw std::invalid_argument(folly::to<std::string>("methodId ", reactMethodId,
" out of range [0..", methods_.size(), "]"));
@@ -106,10 +101,10 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo
}
if (method.callbacks == 1) {
first = convertCallback(makeCallback(instance_, token, params[params.size() - 1]));
first = convertCallback(makeCallback(instance_, params[params.size() - 1]));
} else if (method.callbacks == 2) {
first = convertCallback(makeCallback(instance_, token, params[params.size() - 2]));
second = convertCallback(makeCallback(instance_, token, params[params.size() - 1]));
first = convertCallback(makeCallback(instance_, params[params.size() - 2]));
second = convertCallback(makeCallback(instance_, params[params.size() - 1]));
}
params.resize(params.size() - method.callbacks);
@@ -146,8 +141,7 @@ void CxxNativeModule::invoke(ExecutorToken token, unsigned int reactMethodId, fo
});
}
MethodCallResult CxxNativeModule::callSerializableNativeHook(
ExecutorToken token, unsigned int hookId, folly::dynamic&& args) {
MethodCallResult CxxNativeModule::callSerializableNativeHook(unsigned int hookId, folly::dynamic&& args) {
if (hookId >= methods_.size()) {
throw std::invalid_argument(
folly::to<std::string>("methodId ", hookId, " out of range [0..", methods_.size(), "]"));

View File

@@ -11,7 +11,7 @@ namespace react {
class Instance;
std::function<void(folly::dynamic)> makeCallback(
std::weak_ptr<Instance> instance, ExecutorToken token, const folly::dynamic& callbackId);
std::weak_ptr<Instance> instance, const folly::dynamic& callbackId);
class CxxNativeModule : public NativeModule {
public:
@@ -27,10 +27,8 @@ public:
std::string getName() override;
std::vector<MethodDescriptor> getMethods() override;
folly::dynamic getConstants() override;
bool supportsWebWorkers() override;
void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) override;
MethodCallResult callSerializableNativeHook(
ExecutorToken token, unsigned int hookId, folly::dynamic&& args) override;
void invoke(unsigned int reactMethodId, folly::dynamic&& params) override;
MethodCallResult callSerializableNativeHook(unsigned int hookId, folly::dynamic&& args) override;
private:
void lazyInit();

View File

@@ -38,10 +38,6 @@ class ExecutorDelegate {
public:
virtual ~ExecutorDelegate() {}
virtual void registerExecutor(std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> queue) = 0;
virtual std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) = 0;
virtual std::shared_ptr<ModuleRegistry> getModuleRegistry() = 0;
virtual void callNativeModules(

View File

@@ -1,54 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cxxreact/Executor.h>
namespace facebook {
namespace react {
/**
* This class exists so that we have a type for the shared_ptr on ExecutorToken
* that implements a virtual destructor.
*/
class PlatformExecutorToken {
public:
virtual ~PlatformExecutorToken() {}
};
/**
* Class corresponding to a JS VM that can call into native modules. This is
* passed to native modules to allow their JS module calls/callbacks to be
* routed back to the proper JS VM on the proper thread.
*/
class ExecutorToken {
public:
/**
* This should only be used by the implementation of the platform ExecutorToken.
* Do not use as a client of ExecutorToken.
*/
explicit ExecutorToken(std::shared_ptr<PlatformExecutorToken> platformToken) :
platformToken_(platformToken) {}
std::shared_ptr<PlatformExecutorToken> getPlatformExecutorToken() const {
return platformToken_;
}
bool operator==(const ExecutorToken& other) const {
return platformToken_.get() == other.platformToken_.get();
}
private:
std::shared_ptr<PlatformExecutorToken> platformToken_;
};
} }
namespace std {
template<>
struct hash<facebook::react::ExecutorToken> {
size_t operator()(const facebook::react::ExecutorToken& token) const {
return (size_t) token.getPlatformExecutorToken().get();
}
};
}

View File

@@ -1,25 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#pragma once
#include <cxxreact/Executor.h>
#include <cxxreact/ExecutorToken.h>
namespace facebook {
namespace react {
/**
* Class that knows how to create the platform-specific implementation
* of ExecutorToken.
*/
class ExecutorTokenFactory {
public:
virtual ~ExecutorTokenFactory() {}
/**
* Creates a new ExecutorToken.
*/
virtual ExecutorToken createExecutorToken() const = 0;
};
} }

View File

@@ -128,20 +128,15 @@ void *Instance::getJavaScriptContext() {
return nativeToJsBridge_->getJavaScriptContext();
}
void Instance::callJSFunction(ExecutorToken token, std::string&& module, std::string&& method,
folly::dynamic&& params) {
void Instance::callJSFunction(std::string&& module, std::string&& method, folly::dynamic&& params) {
callback_->incrementPendingJSCalls();
nativeToJsBridge_->callFunction(token, std::move(module), std::move(method), std::move(params));
nativeToJsBridge_->callFunction(std::move(module), std::move(method), std::move(params));
}
void Instance::callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params) {
void Instance::callJSCallback(uint64_t callbackId, folly::dynamic&& params) {
SystraceSection s("<callback>");
callback_->incrementPendingJSCalls();
nativeToJsBridge_->invokeCallback(token, (double) callbackId, std::move(params));
}
ExecutorToken Instance::getMainExecutorToken() {
return nativeToJsBridge_->getMainExecutorToken();
nativeToJsBridge_->invokeCallback((double) callbackId, std::move(params));
}
void Instance::handleMemoryPressureUiHidden() {

View File

@@ -19,8 +19,6 @@ struct InstanceCallback {
virtual void onBatchComplete() = 0;
virtual void incrementPendingJSCalls() = 0;
virtual void decrementPendingJSCalls() = 0;
virtual ExecutorToken createExecutorToken() = 0;
virtual void onExecutorStopped(ExecutorToken) = 0;
};
class Instance {
@@ -50,11 +48,9 @@ class Instance {
void stopProfiler(const std::string& title, const std::string& filename);
void setGlobalVariable(std::string propName, std::unique_ptr<const JSBigString> jsonValue);
void *getJavaScriptContext();
void callJSFunction(ExecutorToken token, std::string&& module, std::string&& method,
folly::dynamic&& params);
void callJSCallback(ExecutorToken token, uint64_t callbackId, folly::dynamic&& params);
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId,
unsigned int methodId, folly::dynamic&& args);
void callJSFunction(std::string&& module, std::string&& method, folly::dynamic&& params);
void callJSCallback(uint64_t callbackId, folly::dynamic&& params);
MethodCallResult callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
// This method is experimental, and may be modified or removed.
template <typename T>
Value callFunctionSync(const std::string& module, const std::string& method, T&& args) {
@@ -62,13 +58,12 @@ class Instance {
return nativeToJsBridge_->callFunctionSync(module, method, std::forward<T>(args));
}
ExecutorToken getMainExecutorToken();
void handleMemoryPressureUiHidden();
void handleMemoryPressureModerate();
void handleMemoryPressureCritical();
private:
void callNativeModules(ExecutorToken token, folly::dynamic&& calls, bool isEndOfBatch);
void callNativeModules(folly::dynamic&& calls, bool isEndOfBatch);
std::shared_ptr<InstanceCallback> callback_;
std::unique_ptr<NativeToJsBridge> nativeToJsBridge_;

View File

@@ -132,16 +132,13 @@ static JSValueRef nativeInjectHMRUpdate(
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate, std::shared_ptr<MessageQueueThread> jsQueue) {
return std::unique_ptr<JSExecutor>(
new JSCExecutor(delegate, jsQueue, m_cacheDir, m_jscConfig));
return folly::make_unique<JSCExecutor>(delegate, jsQueue, m_jscConfig);
}
JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
const std::string& cacheDir,
const folly::dynamic& jscConfig) throw(JSException) :
m_delegate(delegate),
m_deviceCacheDir(cacheDir),
m_messageQueueThread(messageQueueThread),
m_nativeModules(delegate ? delegate->getModuleRegistry() : nullptr),
m_jscConfig(jscConfig) {
@@ -154,52 +151,6 @@ JSCExecutor::JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
}
}
JSCExecutor::JSCExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
int workerId,
JSCExecutor *owner,
std::string scriptURL,
std::unordered_map<std::string, std::string> globalObjAsJSON,
const folly::dynamic& jscConfig) :
m_delegate(delegate),
m_workerId(workerId),
m_owner(owner),
m_deviceCacheDir(owner->m_deviceCacheDir),
m_messageQueueThread(messageQueueThread),
m_nativeModules(delegate->getModuleRegistry()),
m_jscConfig(jscConfig) {
// We post initOnJSVMThread here so that the owner doesn't have to wait for
// initialization on its own thread
m_messageQueueThread->runOnQueue([this, scriptURL,
globalObjAsJSON=std::move(globalObjAsJSON)] () {
initOnJSVMThread();
installNativeHook<&JSCExecutor::nativePostMessage>("postMessage");
for (auto& it : globalObjAsJSON) {
setGlobalVariable(std::move(it.first),
folly::make_unique<JSBigStdString>(std::move(it.second)));
}
// Try to load the script from the network if script is a URL
// NB: For security, this will only work in debug builds
std::unique_ptr<const JSBigString> script;
if (scriptURL.find("http://") == 0 || scriptURL.find("https://") == 0) {
std::stringstream outfileBuilder;
outfileBuilder << m_deviceCacheDir << "/workerScript" << m_workerId << ".js";
script = folly::make_unique<JSBigStdString>(
WebWorkerUtil::loadScriptFromNetworkSync(scriptURL, outfileBuilder.str()));
} else {
// TODO(9604438): Protect against script does not exist
script = WebWorkerUtil::loadScriptFromAssets(scriptURL);
}
// TODO(9994180): Throw on error
loadApplicationScript(std::move(script), std::move(scriptURL));
});
}
JSCExecutor::~JSCExecutor() {
CHECK(*m_isDestroyed) << "JSCExecutor::destroy() must be called before its destructor!";
}
@@ -260,11 +211,6 @@ void JSCExecutor::initOnJSVMThread() throw(JSException) {
installNativeHook<&JSCExecutor::nativeFlushQueueImmediate>("nativeFlushQueueImmediate");
installNativeHook<&JSCExecutor::nativeCallSyncHook>("nativeCallSyncHook");
// Webworker support
installNativeHook<&JSCExecutor::nativeStartWorker>("nativeStartWorker");
installNativeHook<&JSCExecutor::nativePostMessageToWorker>("nativePostMessageToWorker");
installNativeHook<&JSCExecutor::nativeTerminateWorker>("nativeTerminateWorker");
installGlobalFunction(m_context, "nativeLoggingHook", JSNativeHooks::loggingHook);
installGlobalFunction(m_context, "nativePerformanceNow", JSNativeHooks::nowHook);
@@ -299,16 +245,6 @@ void JSCExecutor::initOnJSVMThread() throw(JSException) {
}
void JSCExecutor::terminateOnJSVMThread() {
// terminateOwnedWebWorker mutates m_ownedWorkers so collect all the workers
// to terminate first
std::vector<int> workerIds;
for (auto& it : m_ownedWorkers) {
workerIds.push_back(it.first);
}
for (int workerId : workerIds) {
terminateOwnedWebWorker(workerId);
}
m_nativeModules.reset();
#ifdef WITH_INSPECTOR
@@ -635,115 +571,6 @@ void JSCExecutor::loadModule(uint32_t moduleId) {
evaluateScript(m_context, source, sourceUrl);
}
int JSCExecutor::addWebWorker(
std::string scriptURL,
JSValueRef workerRef,
JSValueRef globalObjRef) {
static std::atomic_int nextWorkerId(1);
int workerId = nextWorkerId++;
Object globalObj = Value(m_context, globalObjRef).asObject();
auto workerJscConfig = m_jscConfig;
workerJscConfig["isWebWorker"] = true;
std::shared_ptr<MessageQueueThread> workerMQT =
WebWorkerUtil::createWebWorkerThread(workerId, m_messageQueueThread.get());
std::unique_ptr<JSCExecutor> worker;
workerMQT->runOnQueueSync([this, &worker, &workerMQT, &scriptURL, &globalObj, workerId, &workerJscConfig] () {
worker.reset(new JSCExecutor(m_delegate, workerMQT, workerId, this, std::move(scriptURL),
globalObj.toJSONMap(), workerJscConfig));
});
Object workerObj = Value(m_context, workerRef).asObject();
workerObj.makeProtected();
JSCExecutor *workerPtr = worker.get();
std::shared_ptr<MessageQueueThread> sharedMessageQueueThread = worker->m_messageQueueThread;
m_delegate->registerExecutor(
std::move(worker),
std::move(sharedMessageQueueThread));
m_ownedWorkers.emplace(
std::piecewise_construct,
std::forward_as_tuple(workerId),
std::forward_as_tuple(workerPtr, std::move(workerObj)));
return workerId;
}
void JSCExecutor::postMessageToOwnedWebWorker(int workerId, JSValueRef message) {
auto worker = m_ownedWorkers.at(workerId).executor;
std::string msgString = Value(m_context, message).toJSONString();
std::shared_ptr<bool> isWorkerDestroyed = worker->m_isDestroyed;
worker->m_messageQueueThread->runOnQueue([isWorkerDestroyed, worker, msgString] () {
if (*isWorkerDestroyed) {
return;
}
worker->receiveMessageFromOwner(msgString);
});
}
void JSCExecutor::postMessageToOwner(JSValueRef msg) {
std::string msgString = Value(m_context, msg).toJSONString();
std::shared_ptr<bool> 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);
});
}
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[] = { createMessageObject(json) };
onmessageValue.asObject().callAsFunction(1, args);
flush();
}
void JSCExecutor::receiveMessageFromOwner(const std::string& msgString) {
CHECK(m_owner) << "Received message in a Executor that doesn't have an owner!";
JSValueRef args[] = { createMessageObject(msgString) };
Value onmessageValue = Object::getGlobalObject(m_context).getProperty("onmessage");
onmessageValue.asObject().callAsFunction(1, args);
}
void JSCExecutor::terminateOwnedWebWorker(int workerId) {
auto& workerRegistration = m_ownedWorkers.at(workerId);
std::shared_ptr<MessageQueueThread> workerMQT = workerRegistration.executor->m_messageQueueThread;
m_ownedWorkers.erase(workerId);
workerMQT->runOnQueueSync([this, &workerMQT] {
workerMQT->quitSynchronous();
std::unique_ptr<JSExecutor> worker = m_delegate->unregisterExecutor(*this);
worker->destroy();
worker.reset();
});
}
Object JSCExecutor::createMessageObject(const std::string& msgJson) {
Value rebornJSMsg = Value::fromJSON(m_context, String(m_context, msgJson.c_str()));
Object messageObject = Object::create(m_context);
messageObject.setProperty("data", rebornJSMsg);
return messageObject;
}
// Native JS hooks
template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
void JSCExecutor::installNativeHook(const char* name) {
@@ -758,18 +585,6 @@ JSValueRef JSCExecutor::getNativeModule(JSObjectRef object, JSStringRef property
return m_nativeModules.getModule(m_context, propertyName);
}
JSValueRef JSCExecutor::nativePostMessage(
size_t argumentCount,
const JSValueRef arguments[]) {
if (argumentCount != 1) {
throw std::invalid_argument("Got wrong number of args");
}
JSValueRef msg = arguments[0];
postMessageToOwner(msg);
return Value::makeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeRequire(
size_t argumentCount,
const JSValueRef arguments[]) {
@@ -801,57 +616,6 @@ JSValueRef JSCExecutor::nativeFlushQueueImmediate(
return Value::makeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeStartWorker(
size_t argumentCount,
const JSValueRef arguments[]) {
if (argumentCount != 3) {
throw std::invalid_argument("Got wrong number of args");
}
std::string scriptFile = Value(m_context, arguments[0]).toString().str();
JSValueRef worker = arguments[1];
JSValueRef globalObj = arguments[2];
int workerId = addWebWorker(scriptFile, worker, globalObj);
return Value::makeNumber(m_context, workerId);
}
JSValueRef JSCExecutor::nativePostMessageToWorker(
size_t argumentCount,
const JSValueRef arguments[]) {
if (argumentCount != 2) {
throw std::invalid_argument("Got wrong number of args");
}
double workerDouble = Value(m_context, arguments[0]).asNumber();
if (workerDouble != workerDouble) {
throw std::invalid_argument("Got invalid worker id");
}
postMessageToOwnedWebWorker((int) workerDouble, arguments[1]);
return Value::makeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeTerminateWorker(
size_t argumentCount,
const JSValueRef arguments[]) {
if (argumentCount != 1) {
throw std::invalid_argument("Got wrong number of args");
}
double workerDouble = Value(m_context, arguments[0]).asNumber();
if (workerDouble != workerDouble) {
std::invalid_argument("Got invalid worker id");
}
terminateOwnedWebWorker((int) workerDouble);
return Value::makeUndefined(m_context);
}
JSValueRef JSCExecutor::nativeCallSyncHook(
size_t argumentCount,
const JSValueRef arguments[]) {

View File

@@ -8,7 +8,6 @@
#include <unordered_map>
#include <cxxreact/Executor.h>
#include <cxxreact/ExecutorToken.h>
#include <cxxreact/JSCNativeModules.h>
#include <folly/Optional.h>
#include <folly/json.h>
@@ -23,10 +22,9 @@ class MessageQueueThread;
class RN_EXPORT JSCExecutorFactory : public JSExecutorFactory {
public:
JSCExecutorFactory(const std::string& cacheDir, const folly::dynamic& jscConfig) :
m_cacheDir(cacheDir),
m_jscConfig(jscConfig) {}
virtual std::unique_ptr<JSExecutor> createJSExecutor(
JSCExecutorFactory(const folly::dynamic& jscConfig) :
m_jscConfig(jscConfig) {}
std::unique_ptr<JSExecutor> createJSExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> jsQueue) override;
private:
@@ -34,17 +32,6 @@ private:
folly::dynamic m_jscConfig;
};
class JSCExecutor;
class WorkerRegistration : public noncopyable {
public:
explicit WorkerRegistration(JSCExecutor* executor_, Object jsObj_) :
executor(executor_),
jsObj(std::move(jsObj_)) {}
JSCExecutor *executor;
Object jsObj;
};
template <typename T>
struct ValueEncoder;
@@ -55,7 +42,6 @@ public:
*/
explicit JSCExecutor(std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
const std::string& cacheDir,
const folly::dynamic& jscConfig) throw(JSException);
~JSCExecutor() override;
@@ -104,11 +90,7 @@ public:
private:
JSGlobalContextRef m_context;
std::shared_ptr<ExecutorDelegate> m_delegate;
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<bool> m_isDestroyed = std::shared_ptr<bool>(new bool(false));
std::unordered_map<int, WorkerRegistration> m_ownedWorkers;
std::string m_deviceCacheDir;
std::shared_ptr<MessageQueueThread> m_messageQueueThread;
std::unique_ptr<JSModulesUnbundle> m_unbundle;
JSCNativeModules m_nativeModules;
@@ -120,18 +102,6 @@ private:
folly::Optional<Object> m_flushedQueueJS;
folly::Optional<Object> m_callFunctionReturnResultAndFlushedQueueJS;
/**
* WebWorker constructor. Must be invoked from thread this Executor will run on.
*/
JSCExecutor(
std::shared_ptr<ExecutorDelegate> delegate,
std::shared_ptr<MessageQueueThread> messageQueueThread,
int workerId,
JSCExecutor *owner,
std::string scriptURL,
std::unordered_map<std::string, std::string> globalObjAsJSON,
const folly::dynamic& jscConfig);
void initOnJSVMThread() throw(JSException);
// This method is experimental, and may be modified or removed.
Value callFunctionSyncWithValue(
@@ -143,30 +113,10 @@ private:
void flushQueueImmediate(Value&&);
void loadModule(uint32_t moduleId);
int addWebWorker(std::string scriptURL, JSValueRef workerRef, JSValueRef globalObjRef);
void postMessageToOwnedWebWorker(int worker, JSValueRef message);
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);
template<JSValueRef (JSCExecutor::*method)(size_t, const JSValueRef[])>
void installNativeHook(const char* name);
JSValueRef getNativeModule(JSObjectRef object, JSStringRef propertyName);
JSValueRef nativeStartWorker(
size_t argumentCount,
const JSValueRef arguments[]);
JSValueRef nativePostMessageToWorker(
size_t argumentCount,
const JSValueRef arguments[]);
JSValueRef nativeTerminateWorker(
size_t argumentCount,
const JSValueRef arguments[]);
JSValueRef nativePostMessage(
size_t argumentCount,
const JSValueRef arguments[]);
JSValueRef nativeRequire(
size_t argumentCount,
const JSValueRef arguments[]);

View File

@@ -1,135 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include "JSCWebWorker.h"
#include <condition_variable>
#include <mutex>
#include <unordered_map>
#include <folly/Memory.h>
#include <glog/logging.h>
#include <jschelpers/JSCHelpers.h>
#include <jschelpers/Value.h>
#include "MessageQueueThread.h"
#include "Platform.h"
#include "JSCUtils.h"
namespace facebook {
namespace react {
// TODO(9604425): thread safety
static std::unordered_map<JSContextRef, JSCWebWorker*> 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);
workerMessageQueueThread_->runOnQueueSync([this] {
terminateOnWorkerThread();
});
}
void JSCWebWorker::terminateOnWorkerThread() {
s_globalContextRefToJSCWebWorker.erase(context_);
JSC_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_ = JSC_JSGlobalContextCreateInGroup(
false, // no support required for custom JSC
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::unique_ptr<const JSBigString> script = WebWorkerUtil::loadScriptFromAssets(scriptName_);
evaluateScript(context_, jsStringFromBigString(context_, *script), String(context_, 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 = Value::makeError(ctx, "postMessage got wrong number of arguments");
return Value::makeUndefined(ctx);
}
JSValueRef msg = arguments[0];
JSCWebWorker *webWorker = s_globalContextRefToJSCWebWorker.at(JSC_JSContextGetGlobalContext(ctx));
if (!webWorker->isTerminated()) {
webWorker->postMessageToOwner(msg);
}
return Value::makeUndefined(ctx);
}
/*static*/
Object JSCWebWorker::createMessageObject(JSContextRef context, const std::string& msgJson) {
Value rebornJSMsg = Value::fromJSON(context, String(context, msgJson.c_str()));
Object messageObject = Object::create(context);
messageObject.setProperty("data", rebornJSMsg);
return messageObject;
}
}
}

View File

@@ -1,93 +0,0 @@
// Copyright 2004-present Facebook. All Rights Reserved.
#include <atomic>
#include <functional>
#include <mutex>
#include <string>
#include <thread>
#include <queue>
#include <jschelpers/JavaScriptCore.h>
#include <jschelpers/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<MessageQueueThread> 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<void()>&& 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<MessageQueueThread> ownerMessageQueueThread_;
std::unique_ptr<MessageQueueThread> workerMessageQueueThread_;
JSGlobalContextRef context_ = nullptr;
static JSValueRef nativePostMessage(
JSContextRef ctx,
JSObjectRef function,
JSObjectRef thisObject,
size_t argumentCount,
const JSValueRef arguments[],
JSValueRef *exception);
};
}
}

View File

@@ -113,8 +113,7 @@ folly::Optional<ModuleConfig> ModuleRegistry::getConfig(const std::string& name)
}
}
void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& params, int callId) {
void ModuleRegistry::callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId) {
if (moduleId >= modules_.size()) {
throw std::runtime_error(
folly::to<std::string>("moduleId ", moduleId, " out of range [0..", modules_.size(), ")"));
@@ -126,15 +125,15 @@ void ModuleRegistry::callNativeMethod(ExecutorToken token, unsigned int moduleId
}
#endif
modules_[moduleId]->invoke(token, methodId, std::move(params));
modules_[moduleId]->invoke(methodId, std::move(params));
}
MethodCallResult ModuleRegistry::callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) {
MethodCallResult ModuleRegistry::callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params) {
if (moduleId >= modules_.size()) {
throw std::runtime_error(
folly::to<std::string>("moduleId ", moduleId, "out of range [0..", modules_.size(), ")"));
}
return modules_[moduleId]->callSerializableNativeHook(token, methodId, std::move(params));
return modules_[moduleId]->callSerializableNativeHook(methodId, std::move(params));
}
}}

View File

@@ -5,7 +5,6 @@
#include <memory>
#include <vector>
#include <cxxreact/ExecutorToken.h>
#include <cxxreact/NativeModule.h>
#include <folly/Optional.h>
#include <folly/dynamic.h>
@@ -36,9 +35,8 @@ class ModuleRegistry {
folly::Optional<ModuleConfig> getConfig(const std::string& name);
void callNativeMethod(ExecutorToken token, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& params, int callId);
MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
void callNativeMethod(unsigned int moduleId, unsigned int methodId, folly::dynamic&& params, int callId);
MethodCallResult callSerializableNativeHook(unsigned int moduleId, unsigned int methodId, folly::dynamic&& args);
private:
// This is always populated

View File

@@ -5,8 +5,8 @@
#include <string>
#include <vector>
#include <cxxreact/ExecutorToken.h>
#include <folly/dynamic.h>
#include <cxxreact/Executor.h>
namespace facebook {
namespace react {
@@ -27,11 +27,10 @@ class NativeModule {
virtual std::string getName() = 0;
virtual std::vector<MethodDescriptor> getMethods() = 0;
virtual folly::dynamic getConstants() = 0;
virtual bool supportsWebWorkers() = 0;
// TODO mhorowitz: do we need initialize()/onCatalystInstanceDestroy() in C++
// or only Java?
virtual void invoke(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& params) = 0;
virtual MethodCallResult callSerializableNativeHook(ExecutorToken token, unsigned int reactMethodId, folly::dynamic&& args) = 0;
virtual void invoke(unsigned int reactMethodId, folly::dynamic&& params) = 0;
virtual MethodCallResult callSerializableNativeHook(unsigned int reactMethodId, folly::dynamic&& args) = 0;
};
}

View File

@@ -22,23 +22,11 @@ namespace react {
// This class manages calls from JS to native code.
class JsToNativeBridge : public react::ExecutorDelegate {
public:
JsToNativeBridge(NativeToJsBridge* nativeToJs,
std::shared_ptr<ModuleRegistry> registry,
JsToNativeBridge(std::shared_ptr<ModuleRegistry> registry,
std::shared_ptr<InstanceCallback> callback)
: m_nativeToJs(nativeToJs)
, m_registry(registry)
: m_registry(registry)
, m_callback(callback) {}
void registerExecutor(std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> queue) override {
m_nativeToJs->registerExecutor(m_callback->createExecutorToken(), std::move(executor), queue);
}
std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executor) override {
m_callback->onExecutorStopped(m_nativeToJs->getTokenForExecutor(executor));
return m_nativeToJs->unregisterExecutor(executor);
}
std::shared_ptr<ModuleRegistry> getModuleRegistry() override {
return m_registry;
}
@@ -48,15 +36,13 @@ public:
CHECK(m_registry || calls.empty()) <<
"native module calls cannot be completed with no native modules";
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
m_batchHadNativeModuleCalls = m_batchHadNativeModuleCalls || !calls.empty();
// An exception anywhere in here stops processing of the batch. This
// was the behavior of the Android bridge, and since exception handling
// terminates the whole bridge, there's not much point in continuing.
for (auto& call : parseMethodCalls(std::move(calls))) {
m_registry->callNativeMethod(
token, call.moduleId, call.methodId, std::move(call.arguments), call.callId);
m_registry->callNativeMethod(call.moduleId, call.methodId, std::move(call.arguments), call.callId);
}
if (isEndOfBatch) {
// onBatchComplete will be called on the native (module) queue, but
@@ -73,18 +59,14 @@ public:
MethodCallResult callSerializableNativeHook(
JSExecutor& executor, unsigned int moduleId, unsigned int methodId,
folly::dynamic&& args) override {
ExecutorToken token = m_nativeToJs->getTokenForExecutor(executor);
return m_registry->callSerializableNativeHook(token, moduleId, methodId, std::move(args));
return m_registry->callSerializableNativeHook(moduleId, methodId, std::move(args));
}
private:
// These methods are always invoked from an Executor. The NativeToJsBridge
// keeps a reference to the root executor, and when destroy() is
// called, the Executors are all destroyed synchronously on their
// bridges. So, the bridge pointer will will always point to a
// valid object during a call to a delegate method from an exectuto.
NativeToJsBridge* m_nativeToJs;
// keeps a reference to the executor, and when destroy() is called, the
// executor is destroyed synchronously on its queue.
std::shared_ptr<ModuleRegistry> m_registry;
std::shared_ptr<InstanceCallback> m_callback;
bool m_batchHadNativeModuleCalls = false;
@@ -96,14 +78,9 @@ NativeToJsBridge::NativeToJsBridge(
std::shared_ptr<MessageQueueThread> jsQueue,
std::shared_ptr<InstanceCallback> callback)
: m_destroyed(std::make_shared<bool>(false))
, m_mainExecutorToken(callback->createExecutorToken())
, m_delegate(std::make_shared<JsToNativeBridge>(this, registry, callback)) {
std::unique_ptr<JSExecutor> mainExecutor =
jsExecutorFactory->createJSExecutor(m_delegate, jsQueue);
// cached to avoid locked map lookup in the common case
m_mainExecutor = mainExecutor.get();
registerExecutor(m_mainExecutorToken, std::move(mainExecutor), jsQueue);
}
, m_delegate(std::make_shared<JsToNativeBridge>(registry, callback))
, m_executor(jsExecutorFactory->createJSExecutor(m_delegate, jsQueue))
, m_executorMessageQueueThread(std::move(jsQueue)) {}
// This must be called on the same thread on which the constructor was called.
NativeToJsBridge::~NativeToJsBridge() {
@@ -116,7 +93,6 @@ void NativeToJsBridge::loadApplication(
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
runOnExecutorQueue(
m_mainExecutorToken,
[unbundleWrap=folly::makeMoveWrapper(std::move(unbundle)),
startupScript=folly::makeMoveWrapper(std::move(startupScript)),
startupScriptSourceURL=std::move(startupScriptSourceURL)]
@@ -135,14 +111,13 @@ void NativeToJsBridge::loadApplicationSync(
std::unique_ptr<const JSBigString> startupScript,
std::string startupScriptSourceURL) {
if (unbundle) {
m_mainExecutor->setJSModulesUnbundle(std::move(unbundle));
m_executor->setJSModulesUnbundle(std::move(unbundle));
}
m_mainExecutor->loadApplicationScript(std::move(startupScript),
m_executor->loadApplicationScript(std::move(startupScript),
std::move(startupScriptSourceURL));
}
void NativeToJsBridge::callFunction(
ExecutorToken executorToken,
std::string&& module,
std::string&& method,
folly::dynamic&& arguments) {
@@ -160,23 +135,24 @@ void NativeToJsBridge::callFunction(
std::string tracingName;
#endif
runOnExecutorQueue(executorToken, [module = std::move(module), method = std::move(method), arguments = std::move(arguments), tracingName = std::move(tracingName), systraceCookie] (JSExecutor* executor) {
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
tracingName.c_str(),
systraceCookie);
SystraceSection s(tracingName.c_str());
#endif
runOnExecutorQueue([module = std::move(module), method = std::move(method), arguments = std::move(arguments), tracingName = std::move(tracingName), systraceCookie]
(JSExecutor* executor) {
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
tracingName.c_str(),
systraceCookie);
SystraceSection s(tracingName.c_str());
#endif
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
executor->callFunction(module, method, arguments);
});
// This is safe because we are running on the executor's thread: it won't
// destruct until after it's been unregistered (which we check above) and
// that will happen on this thread
executor->callFunction(module, method, arguments);
});
}
void NativeToJsBridge::invokeCallback(ExecutorToken executorToken, double callbackId, folly::dynamic&& arguments) {
void NativeToJsBridge::invokeCallback(double callbackId, folly::dynamic&& arguments) {
int systraceCookie = -1;
#ifdef WITH_FBSYSTRACE
systraceCookie = m_systraceCookie++;
@@ -186,24 +162,23 @@ void NativeToJsBridge::invokeCallback(ExecutorToken executorToken, double callba
systraceCookie);
#endif
runOnExecutorQueue(executorToken, [callbackId, arguments = std::move(arguments), systraceCookie] (JSExecutor* executor) {
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
"<callback>",
systraceCookie);
SystraceSection s("NativeToJsBridge.invokeCallback");
#endif
runOnExecutorQueue([callbackId, arguments = std::move(arguments), systraceCookie]
(JSExecutor* executor) {
#ifdef WITH_FBSYSTRACE
FbSystraceAsyncFlow::end(
TRACE_TAG_REACT_CXX_BRIDGE,
"<callback>",
systraceCookie);
SystraceSection s("NativeToJsBridge.invokeCallback");
#endif
executor->invokeCallback(callbackId, arguments);
});
executor->invokeCallback(callbackId, arguments);
});
}
void NativeToJsBridge::setGlobalVariable(std::string propName,
std::unique_ptr<const JSBigString> jsonValue) {
runOnExecutorQueue(
m_mainExecutorToken,
[propName=std::move(propName), jsonValue=folly::makeMoveWrapper(std::move(jsonValue))]
runOnExecutorQueue([propName=std::move(propName), jsonValue=folly::makeMoveWrapper(std::move(jsonValue))]
(JSExecutor* executor) mutable {
executor->setGlobalVariable(propName, jsonValue.move());
});
@@ -211,149 +186,72 @@ void NativeToJsBridge::setGlobalVariable(std::string propName,
void* NativeToJsBridge::getJavaScriptContext() {
// TODO(cjhopman): this seems unsafe unless we require that it is only called on the main js queue.
return m_mainExecutor->getJavaScriptContext();
return m_executor->getJavaScriptContext();
}
bool NativeToJsBridge::supportsProfiling() {
// Intentionally doesn't post to jsqueue. supportsProfiling() can be called from any thread.
return m_mainExecutor->supportsProfiling();
return m_executor->supportsProfiling();
}
void NativeToJsBridge::startProfiler(const std::string& title) {
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
runOnExecutorQueue([=] (JSExecutor* executor) {
executor->startProfiler(title);
});
}
void NativeToJsBridge::stopProfiler(const std::string& title, const std::string& filename) {
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
runOnExecutorQueue([=] (JSExecutor* executor) {
executor->stopProfiler(title, filename);
});
}
void NativeToJsBridge::handleMemoryPressureUiHidden() {
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
runOnExecutorQueue([=] (JSExecutor* executor) {
executor->handleMemoryPressureUiHidden();
});
}
void NativeToJsBridge::handleMemoryPressureModerate() {
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
runOnExecutorQueue([=] (JSExecutor* executor) {
executor->handleMemoryPressureModerate();
});
}
void NativeToJsBridge::handleMemoryPressureCritical() {
runOnExecutorQueue(m_mainExecutorToken, [=] (JSExecutor* executor) {
runOnExecutorQueue([=] (JSExecutor* executor) {
executor->handleMemoryPressureCritical();
});
}
ExecutorToken NativeToJsBridge::getMainExecutorToken() const {
return m_mainExecutorToken;
}
ExecutorToken NativeToJsBridge::registerExecutor(
ExecutorToken token,
std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> messageQueueThread) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
CHECK(m_executorTokenMap.find(executor.get()) == m_executorTokenMap.end())
<< "Trying to register an already registered executor!";
m_executorTokenMap.emplace(executor.get(), token);
m_executorMap.emplace(
token,
ExecutorRegistration(std::move(executor), messageQueueThread));
return token;
}
std::unique_ptr<JSExecutor> NativeToJsBridge::unregisterExecutor(JSExecutor& executor) {
std::unique_ptr<JSExecutor> ret;
{
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
auto it = m_executorTokenMap.find(&executor);
CHECK(it != m_executorTokenMap.end())
<< "Trying to unregister an executor that was never registered!";
auto it2 = m_executorMap.find(it->second);
ret = std::move(it2->second.executor_);
m_executorTokenMap.erase(it);
m_executorMap.erase(it2);
}
return ret;
}
MessageQueueThread* NativeToJsBridge::getMessageQueueThread(const ExecutorToken& executorToken) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
auto it = m_executorMap.find(executorToken);
if (it == m_executorMap.end()) {
return nullptr;
}
return it->second.messageQueueThread_.get();
}
JSExecutor* NativeToJsBridge::getExecutor(const ExecutorToken& executorToken) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
auto it = m_executorMap.find(executorToken);
if (it == m_executorMap.end()) {
return nullptr;
}
return it->second.executor_.get();
}
ExecutorToken NativeToJsBridge::getTokenForExecutor(JSExecutor& executor) {
std::lock_guard<std::mutex> registrationGuard(m_registrationMutex);
return m_executorTokenMap.at(&executor);
}
void NativeToJsBridge::destroy() {
auto* executorMessageQueueThread = getMessageQueueThread(m_mainExecutorToken);
// All calls made through runOnExecutorQueue have an early exit if
// m_destroyed is true. Setting this before the runOnQueueSync will cause
// pending work to be cancelled and we won't have to wait for it.
*m_destroyed = true;
executorMessageQueueThread->runOnQueueSync([this, executorMessageQueueThread] {
m_mainExecutor->destroy();
executorMessageQueueThread->quitSynchronous();
m_delegate->unregisterExecutor(*m_mainExecutor);
m_mainExecutor = nullptr;
m_executorMessageQueueThread->runOnQueueSync([this] {
m_executor->destroy();
m_executorMessageQueueThread->quitSynchronous();
m_executor = nullptr;
});
}
void NativeToJsBridge::runOnExecutorQueue(ExecutorToken executorToken, std::function<void(JSExecutor*)> task) {
void NativeToJsBridge::runOnExecutorQueue(std::function<void(JSExecutor*)> task) {
if (*m_destroyed) {
return;
}
auto executorMessageQueueThread = getMessageQueueThread(executorToken);
if (executorMessageQueueThread == nullptr) {
LOG(WARNING) << "Dropping JS action for executor that has been unregistered...";
return;
}
std::shared_ptr<bool> isDestroyed = m_destroyed;
executorMessageQueueThread->runOnQueue([this, isDestroyed, executorToken, task=std::move(task)] {
m_executorMessageQueueThread->runOnQueue([this, isDestroyed, task=std::move(task)] {
if (*isDestroyed) {
return;
}
JSExecutor *executor = getExecutor(executorToken);
if (executor == nullptr) {
LOG(WARNING) << "Dropping JS call for executor that has been unregistered...";
return;
}
// The executor is guaranteed to be valid for the duration of the task because:
// 1. the executor is only destroyed after it is unregistered
// 2. the executor is unregistered on this queue
// 3. we just confirmed that the executor hasn't been unregistered above
task(executor);
task(m_executor.get());
});
}

View File

@@ -8,7 +8,6 @@
#include <vector>
#include <cxxreact/Executor.h>
#include <cxxreact/ExecutorToken.h>
#include <cxxreact/JSCExecutor.h>
#include <cxxreact/JSModulesUnbundle.h>
#include <cxxreact/MessageQueueThread.h>
@@ -26,22 +25,9 @@ struct dynamic;
namespace facebook {
namespace react {
struct InstanceCallback;
class ModuleRegistry;
class ExecutorRegistration {
public:
ExecutorRegistration(
std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> executorMessageQueueThread) :
executor_(std::move(executor)),
messageQueueThread_(executorMessageQueueThread) {}
std::unique_ptr<JSExecutor> executor_;
std::shared_ptr<MessageQueueThread> messageQueueThread_;
};
class JsToNativeBridge;
struct InstanceCallback;
// This class manages calls from native code to JS. It also manages
// executors and their threads. All functions here can be called from
@@ -68,16 +54,12 @@ public:
* Executes a function with the module ID and method ID and any additional
* arguments in JS.
*/
void callFunction(
ExecutorToken executorToken,
std::string&& module,
std::string&& method,
folly::dynamic&& args);
void callFunction(std::string&& module, std::string&& method, folly::dynamic&& args);
/**
* Invokes a callback with the cbID, and optional additional arguments in JS.
*/
void invokeCallback(ExecutorToken executorToken, double callbackId, folly::dynamic&& args);
void invokeCallback(double callbackId, folly::dynamic&& args);
/**
* Executes a JS method on the given executor synchronously, returning its
@@ -98,10 +80,10 @@ public:
" after bridge is destroyed"));
}
JSCExecutor *jscExecutor = dynamic_cast<JSCExecutor*>(m_mainExecutor);
JSCExecutor *jscExecutor = dynamic_cast<JSCExecutor*>(m_executor.get());
if (!jscExecutor) {
throw std::invalid_argument(
folly::to<std::string>("Executor type ", typeid(*m_mainExecutor).name(),
folly::to<std::string>("Executor type ", typeid(m_executor.get()).name(),
" does not support synchronous calls"));
}
@@ -131,56 +113,25 @@ public:
void handleMemoryPressureModerate();
void handleMemoryPressureCritical();
/**
* Returns the ExecutorToken corresponding to the main JSExecutor.
*/
ExecutorToken getMainExecutorToken() const;
/**
* Synchronously tears down the bridge and the main executor.
*/
void destroy();
private:
/**
* Registers the given JSExecutor which runs on the given MessageQueueThread
* with the NativeToJsBridge. Part of this registration is transfering
* ownership of this JSExecutor to the NativeToJsBridge for the duration of
* the registration.
*
* Returns a ExecutorToken which can be used to refer to this JSExecutor
* in the NativeToJsBridge.
*/
ExecutorToken registerExecutor(
ExecutorToken token,
std::unique_ptr<JSExecutor> executor,
std::shared_ptr<MessageQueueThread> executorMessageQueueThread);
/**
* Unregisters a JSExecutor that was previously registered with this NativeToJsBridge
* using registerExecutor.
*/
std::unique_ptr<JSExecutor> unregisterExecutor(JSExecutor& executorToken);
void runOnExecutorQueue(ExecutorToken token, std::function<void(JSExecutor*)> task);
void runOnExecutorQueue(std::function<void(JSExecutor*)> task);
// This is used to avoid a race condition where a proxyCallback gets queued
// after ~NativeToJsBridge(), on the same thread. In that case, the callback
// will try to run the task on m_callback which will have been destroyed
// within ~NativeToJsBridge(), thus causing a SIGSEGV.
std::shared_ptr<bool> m_destroyed;
JSExecutor* m_mainExecutor;
ExecutorToken m_mainExecutorToken;
std::shared_ptr<JsToNativeBridge> m_delegate;
std::unordered_map<JSExecutor*, ExecutorToken> m_executorTokenMap;
std::unordered_map<ExecutorToken, ExecutorRegistration> m_executorMap;
std::mutex m_registrationMutex;
std::unique_ptr<JSExecutor> m_executor;
std::shared_ptr<MessageQueueThread> m_executorMessageQueueThread;
#ifdef WITH_FBSYSTRACE
std::atomic_uint_least32_t m_systraceCookie = ATOMIC_VAR_INIT();
#endif
MessageQueueThread* getMessageQueueThread(const ExecutorToken& executorToken);
JSExecutor* getExecutor(const ExecutorToken& executorToken);
ExecutorToken getTokenForExecutor(JSExecutor& executor);
};
} }

View File

@@ -9,12 +9,6 @@ namespace ReactMarker {
LogMarker logMarker;
};
namespace WebWorkerUtil {
WebWorkerQueueFactory createWebWorkerThread;
LoadScriptFromAssets loadScriptFromAssets;
LoadScriptFromNetworkSync loadScriptFromNetworkSync;
};
namespace PerfLogging {
InstallNativeHooks installNativeHooks;
};

View File

@@ -27,17 +27,6 @@ using LogMarker = std::function<void(const ReactMarkerId)>;
extern LogMarker logMarker;
};
namespace WebWorkerUtil {
using WebWorkerQueueFactory = std::function<std::unique_ptr<MessageQueueThread>(int id, MessageQueueThread* ownerMessageQueue)>;
extern WebWorkerQueueFactory createWebWorkerThread;
using LoadScriptFromAssets = std::function<std::unique_ptr<const JSBigString>(const std::string& assetName)>;
extern LoadScriptFromAssets loadScriptFromAssets;
using LoadScriptFromNetworkSync = std::function<std::string(const std::string& url, const std::string& tempfileName)>;
extern LoadScriptFromNetworkSync loadScriptFromNetworkSync;
};
namespace PerfLogging {
using InstallNativeHooks = std::function<void(JSGlobalContextRef)>;
extern InstallNativeHooks installNativeHooks;