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
committed by facebook-github-bot-4
parent 88a880b1fc
commit e0b2c2e34e
9 changed files with 93 additions and 21 deletions

View File

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

View File

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

View File

@@ -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() {};
};

View File

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

View File

@@ -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,
@@ -31,11 +31,13 @@ public:
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;
};
} }

View File

@@ -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,7 +595,9 @@ static void dispatchCallbacksToJava(const RefPtr<WeakReference>& weakCallback,
return;
}
}
signalBatchComplete(env, callback);
if (isEndOfBatch) {
signalBatchComplete(env, callback);
}
}
}, std::move(calls));
@@ -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);

View File

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

View File

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