Batch native method calls in 5ms increments
Reviewed By: @mkonicek Differential Revision: D2535803 fb-gh-sync-id: 8363a83d6966cfa4c2ea46358b10e4139333329f
This commit is contained in:
Родитель
88a880b1fc
Коммит
e0b2c2e34e
|
@ -25,6 +25,7 @@ let stringifySafe = require('stringifySafe');
|
|||
let MODULE_IDS = 0;
|
||||
let METHOD_IDS = 1;
|
||||
let PARAMS = 2;
|
||||
let MIN_TIME_BETWEEN_FLUSHES_MS = 5;
|
||||
|
||||
let SPY_MODE = false;
|
||||
|
||||
|
@ -53,6 +54,7 @@ class MessageQueue {
|
|||
this._methodTable = {};
|
||||
this._callbacks = [];
|
||||
this._callbackID = 0;
|
||||
this._lastFlush = 0;
|
||||
|
||||
[
|
||||
'invokeCallbackAndReturnFlushedQueue',
|
||||
|
@ -125,6 +127,14 @@ class MessageQueue {
|
|||
this._queue[MODULE_IDS].push(module);
|
||||
this._queue[METHOD_IDS].push(method);
|
||||
this._queue[PARAMS].push(params);
|
||||
|
||||
var now = new Date().getTime();
|
||||
if (global.nativeFlushQueueImmediate &&
|
||||
now - this._lastFlush >= MIN_TIME_BETWEEN_FLUSHES_MS) {
|
||||
global.nativeFlushQueueImmediate(this._queue);
|
||||
this._queue = [[],[],[]];
|
||||
this._lastFlush = now;
|
||||
}
|
||||
if (__DEV__ && SPY_MODE && isFinite(module)) {
|
||||
console.log('JS->N : ' + this._remoteModuleTable[module] + '.' +
|
||||
this._remoteMethodTable[module][method] + '(' + JSON.stringify(params) + ')');
|
||||
|
@ -133,6 +143,7 @@ class MessageQueue {
|
|||
|
||||
__callFunction(module, method, args) {
|
||||
BridgeProfiling.profile(() => `${module}.${method}(${stringifySafe(args)})`);
|
||||
this._lastFlush = new Date().getTime();
|
||||
if (isFinite(module)) {
|
||||
method = this._methodTable[module][method];
|
||||
module = this._moduleTable[module];
|
||||
|
@ -148,6 +159,7 @@ class MessageQueue {
|
|||
__invokeCallback(cbID, args) {
|
||||
BridgeProfiling.profile(
|
||||
() => `MessageQueue.invokeCallback(${cbID}, ${stringifySafe(args)})`);
|
||||
this._lastFlush = new Date().getTime();
|
||||
let callback = this._callbacks[cbID];
|
||||
if (!callback || __DEV__) {
|
||||
let debug = this._debugInfo[cbID >> 1];
|
||||
|
|
|
@ -16,9 +16,12 @@ namespace react {
|
|||
class JSThreadState {
|
||||
public:
|
||||
JSThreadState(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Bridge::Callback&& callback) :
|
||||
m_jsExecutor(jsExecutorFactory->createJSExecutor()),
|
||||
m_callback(callback)
|
||||
{}
|
||||
{
|
||||
m_jsExecutor = jsExecutorFactory->createJSExecutor([this, callback] (std::string queueJSON) {
|
||||
m_callback(parseMethodCalls(queueJSON), false /* = isEndOfBatch */);
|
||||
});
|
||||
}
|
||||
|
||||
void executeApplicationScript(const std::string& script, const std::string& sourceURL) {
|
||||
m_jsExecutor->executeApplicationScript(script, sourceURL);
|
||||
|
@ -29,7 +32,7 @@ public:
|
|||
const std::string& methodName,
|
||||
const std::vector<folly::dynamic>& arguments) {
|
||||
auto returnedJSON = m_jsExecutor->executeJSCall(moduleName, methodName, arguments);
|
||||
m_callback(parseMethodCalls(returnedJSON));
|
||||
m_callback(parseMethodCalls(returnedJSON), true /* = isEndOfBatch */);
|
||||
}
|
||||
|
||||
void setGlobalVariable(const std::string& propName, const std::string& jsonValue) {
|
||||
|
@ -58,11 +61,11 @@ Bridge::Bridge(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Callback call
|
|||
m_destroyed(std::shared_ptr<bool>(new bool(false)))
|
||||
{
|
||||
auto destroyed = m_destroyed;
|
||||
auto proxyCallback = [this, destroyed] (std::vector<MethodCall> calls) {
|
||||
auto proxyCallback = [this, destroyed] (std::vector<MethodCall> calls, bool isEndOfBatch) {
|
||||
if (*destroyed) {
|
||||
return;
|
||||
}
|
||||
m_callback(std::move(calls));
|
||||
m_callback(std::move(calls), isEndOfBatch);
|
||||
};
|
||||
m_threadState.reset(new JSThreadState(jsExecutorFactory, std::move(proxyCallback)));
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace react {
|
|||
class JSThreadState;
|
||||
class Bridge : public Countable {
|
||||
public:
|
||||
typedef std::function<void(std::vector<MethodCall>)> Callback;
|
||||
typedef std::function<void(std::vector<MethodCall>, bool isEndOfBatch)> Callback;
|
||||
|
||||
Bridge(const RefPtr<JSExecutorFactory>& jsExecutorFactory, Callback callback);
|
||||
virtual ~Bridge();
|
||||
|
|
|
@ -18,9 +18,11 @@ namespace react {
|
|||
|
||||
class JSExecutor;
|
||||
|
||||
typedef std::function<void(std::string)> FlushImmediateCallback;
|
||||
|
||||
class JSExecutorFactory : public Countable {
|
||||
public:
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor() = 0;
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor(FlushImmediateCallback cb) = 0;
|
||||
virtual ~JSExecutorFactory() {};
|
||||
};
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <fb/log.h>
|
||||
#include <folly/json.h>
|
||||
#include <folly/String.h>
|
||||
|
@ -26,12 +27,21 @@ using fbsystrace::FbSystraceSection;
|
|||
namespace facebook {
|
||||
namespace react {
|
||||
|
||||
static std::unordered_map<JSContextRef, JSCExecutor*> s_globalContextRefToJSCExecutor;
|
||||
static JSValueRef nativeFlushQueueImmediate(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
static JSValueRef nativeLoggingHook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[], JSValueRef *exception);
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception);
|
||||
|
||||
static JSValueRef evaluateScriptWithJSC(
|
||||
JSGlobalContextRef ctx,
|
||||
|
@ -47,12 +57,15 @@ static JSValueRef evaluateScriptWithJSC(
|
|||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor() {
|
||||
return std::unique_ptr<JSExecutor>(new JSCExecutor());
|
||||
std::unique_ptr<JSExecutor> JSCExecutorFactory::createJSExecutor(FlushImmediateCallback cb) {
|
||||
return std::unique_ptr<JSExecutor>(new JSCExecutor(cb));
|
||||
}
|
||||
|
||||
JSCExecutor::JSCExecutor() {
|
||||
JSCExecutor::JSCExecutor(FlushImmediateCallback cb) :
|
||||
m_flushImmediateCallback(cb) {
|
||||
m_context = JSGlobalContextCreateInGroup(nullptr, nullptr);
|
||||
s_globalContextRefToJSCExecutor[m_context] = this;
|
||||
installGlobalFunction(m_context, "nativeFlushQueueImmediate", nativeFlushQueueImmediate);
|
||||
installGlobalFunction(m_context, "nativeLoggingHook", nativeLoggingHook);
|
||||
#ifdef WITH_JSC_EXTRA_TRACING
|
||||
addNativeTracingHooks(m_context);
|
||||
|
@ -62,6 +75,7 @@ JSCExecutor::JSCExecutor() {
|
|||
}
|
||||
|
||||
JSCExecutor::~JSCExecutor() {
|
||||
s_globalContextRefToJSCExecutor.erase(m_context);
|
||||
JSGlobalContextRelease(m_context);
|
||||
}
|
||||
|
||||
|
@ -132,6 +146,42 @@ void JSCExecutor::stopProfiler(const std::string &titleString, const std::string
|
|||
#endif
|
||||
}
|
||||
|
||||
void JSCExecutor::flushQueueImmediate(std::string queueJSON) {
|
||||
m_flushImmediateCallback(queueJSON);
|
||||
}
|
||||
|
||||
static JSValueRef createErrorString(JSContextRef ctx, const char *msg) {
|
||||
return JSValueMakeString(ctx, String(msg));
|
||||
}
|
||||
|
||||
static JSValueRef nativeFlushQueueImmediate(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
JSObjectRef thisObject,
|
||||
size_t argumentCount,
|
||||
const JSValueRef arguments[],
|
||||
JSValueRef *exception) {
|
||||
if (argumentCount != 1) {
|
||||
*exception = createErrorString(ctx, "Got wrong number of args");
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSCExecutor *executor;
|
||||
try {
|
||||
executor = s_globalContextRefToJSCExecutor.at(JSContextGetGlobalContext(ctx));
|
||||
} catch (std::out_of_range& e) {
|
||||
*exception = createErrorString(ctx, "Global JS context didn't map to a valid executor");
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
JSValueProtect(ctx, arguments[0]);
|
||||
std::string resStr = Value(ctx, arguments[0]).toJSONString();
|
||||
|
||||
executor->flushQueueImmediate(resStr);
|
||||
|
||||
return JSValueMakeUndefined(ctx);
|
||||
}
|
||||
|
||||
static JSValueRef nativeLoggingHook(
|
||||
JSContextRef ctx,
|
||||
JSObjectRef function,
|
||||
|
|
|
@ -11,12 +11,12 @@ namespace react {
|
|||
|
||||
class JSCExecutorFactory : public JSExecutorFactory {
|
||||
public:
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor() override;
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor(FlushImmediateCallback cb) override;
|
||||
};
|
||||
|
||||
class JSCExecutor : public JSExecutor {
|
||||
public:
|
||||
JSCExecutor();
|
||||
explicit JSCExecutor(FlushImmediateCallback flushImmediateCallback);
|
||||
~JSCExecutor() override;
|
||||
virtual void executeApplicationScript(
|
||||
const std::string& script,
|
||||
|
@ -32,10 +32,12 @@ public:
|
|||
virtual void startProfiler(const std::string &titleString) override;
|
||||
virtual void stopProfiler(const std::string &titleString, const std::string &filename) override;
|
||||
|
||||
void flushQueueImmediate(std::string queueJSON);
|
||||
void installNativeHook(const char *name, JSObjectCallAsFunctionCallback callback);
|
||||
|
||||
private:
|
||||
JSGlobalContextRef m_context;
|
||||
FlushImmediateCallback m_flushImmediateCallback;
|
||||
};
|
||||
|
||||
} }
|
||||
|
|
|
@ -567,7 +567,8 @@ static void signalBatchComplete(JNIEnv* env, jobject callback) {
|
|||
|
||||
static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
|
||||
const RefPtr<WeakReference>& weakCallbackQueueThread,
|
||||
std::vector<MethodCall>&& calls) {
|
||||
std::vector<MethodCall>&& calls,
|
||||
bool isEndOfBatch) {
|
||||
auto env = Environment::current();
|
||||
if (env->ExceptionCheck()) {
|
||||
FBLOGW("Dropped calls because of pending exception");
|
||||
|
@ -580,7 +581,7 @@ static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
|
|||
return;
|
||||
}
|
||||
|
||||
auto runnableFunction = std::bind([weakCallback] (std::vector<MethodCall>& calls) {
|
||||
auto runnableFunction = std::bind([weakCallback, isEndOfBatch] (std::vector<MethodCall>& calls) {
|
||||
auto env = Environment::current();
|
||||
if (env->ExceptionCheck()) {
|
||||
FBLOGW("Dropped calls because of pending exception");
|
||||
|
@ -594,8 +595,10 @@ static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (isEndOfBatch) {
|
||||
signalBatchComplete(env, callback);
|
||||
}
|
||||
}
|
||||
}, std::move(calls));
|
||||
|
||||
jobject jNativeRunnable = runnable::createNativeRunnable(env, std::move(runnableFunction));
|
||||
|
@ -606,8 +609,8 @@ static void create(JNIEnv* env, jobject obj, jobject executor, jobject callback,
|
|||
jobject callbackQueueThread) {
|
||||
auto weakCallback = createNew<WeakReference>(callback);
|
||||
auto weakCallbackQueueThread = createNew<WeakReference>(callbackQueueThread);
|
||||
auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls) {
|
||||
dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls));
|
||||
auto bridgeCallback = [weakCallback, weakCallbackQueueThread] (std::vector<MethodCall> calls, bool isEndOfBatch) {
|
||||
dispatchCallbacksToJava(weakCallback, weakCallbackQueueThread, std::move(calls), isEndOfBatch);
|
||||
};
|
||||
auto nativeExecutorFactory = extractRefPtr<JSExecutorFactory>(env, executor);
|
||||
auto bridge = createNew<Bridge>(nativeExecutorFactory, bridgeCallback);
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace react {
|
|||
|
||||
const auto EXECUTOR_BASECLASS = "com/facebook/react/bridge/ProxyJavaScriptExecutor$JavaJSExecutor";
|
||||
|
||||
std::unique_ptr<JSExecutor> ProxyExecutorOneTimeFactory::createJSExecutor() {
|
||||
std::unique_ptr<JSExecutor> ProxyExecutorOneTimeFactory::createJSExecutor(FlushImmediateCallback ignoredCallback) {
|
||||
FBASSERTMSGF(
|
||||
m_executor.get() != nullptr,
|
||||
"Proxy instance should not be null. Did you attempt to call createJSExecutor() on this factory "
|
||||
|
|
|
@ -18,7 +18,7 @@ class ProxyExecutorOneTimeFactory : public JSExecutorFactory {
|
|||
public:
|
||||
ProxyExecutorOneTimeFactory(jni::global_ref<jobject>&& executorInstance) :
|
||||
m_executor(std::move(executorInstance)) {}
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor() override;
|
||||
virtual std::unique_ptr<JSExecutor> createJSExecutor(FlushImmediateCallback ignoredCallback) override;
|
||||
|
||||
private:
|
||||
jni::global_ref<jobject> m_executor;
|
||||
|
|
Загрузка…
Ссылка в новой задаче