Skip to content

Commit

Permalink
Batch native method calls in 5ms increments
Browse files Browse the repository at this point in the history
Reviewed By: @mkonicek

Differential Revision: D2535803

fb-gh-sync-id: 8363a83d6966cfa4c2ea46358b10e4139333329f
  • Loading branch information
astreet authored and facebook-github-bot-4 committed Oct 13, 2015
1 parent 88a880b commit e0b2c2e
Show file tree
Hide file tree
Showing 9 changed files with 93 additions and 21 deletions.
12 changes: 12 additions & 0 deletions Libraries/Utilities/MessageQueue.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -53,6 +54,7 @@ class MessageQueue {
this._methodTable = {};
this._callbacks = [];
this._callbackID = 0;
this._lastFlush = 0;

[
'invokeCallbackAndReturnFlushedQueue',
Expand Down Expand Up @@ -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) + ')');
Expand All @@ -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];
Expand All @@ -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];
Expand Down
13 changes: 8 additions & 5 deletions ReactAndroid/src/main/jni/react/Bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -29,7 +32,7 @@ class JSThreadState {
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) {
Expand Down Expand Up @@ -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)));
}
Expand Down
2 changes: 1 addition & 1 deletion ReactAndroid/src/main/jni/react/Bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 3 additions & 1 deletion ReactAndroid/src/main/jni/react/Executor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() {};
};

Expand Down
58 changes: 54 additions & 4 deletions ReactAndroid/src/main/jni/react/JSCExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <algorithm>
#include <sstream>
#include <string>
#include <fb/log.h>
#include <folly/json.h>
#include <folly/String.h>
Expand All @@ -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,
Expand All @@ -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);
Expand All @@ -62,6 +75,7 @@ JSCExecutor::JSCExecutor() {
}

JSCExecutor::~JSCExecutor() {
s_globalContextRefToJSCExecutor.erase(m_context);
JSGlobalContextRelease(m_context);
}

Expand Down Expand Up @@ -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,
Expand Down
8 changes: 5 additions & 3 deletions ReactAndroid/src/main/jni/react/JSCExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -31,11 +31,13 @@ class JSCExecutor : public JSExecutor {
virtual bool supportsProfiling() override;
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;
};

} }
13 changes: 8 additions & 5 deletions ReactAndroid/src/main/jni/react/jni/OnLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand All @@ -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");
Expand All @@ -594,7 +595,9 @@ static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
return;
}
}
signalBatchComplete(env, callback);
if (isEndOfBatch) {
signalBatchComplete(env, callback);
}
}
}, std::move(calls));

Expand All @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion ReactAndroid/src/main/jni/react/jni/ProxyExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down
2 changes: 1 addition & 1 deletion ReactAndroid/src/main/jni/react/jni/ProxyExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit e0b2c2e

Please sign in to comment.