Batch native method calls in 5ms increments

Reviewed By: @mkonicek

Differential Revision: D2535803

fb-gh-sync-id: 8363a83d6966cfa4c2ea46358b10e4139333329f
This commit is contained in:
Andy Street 2015-10-13 08:00:36 -07:00 коммит произвёл facebook-github-bot-4
Родитель 88a880b1fc
Коммит e0b2c2e34e
9 изменённых файлов: 93 добавлений и 21 удалений

Просмотреть файл

@ -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;