mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
281daf1222
Summary: The RuntimeExecutor that Fabric gets from the bridge doesn't call JSIExecutor::flush(). In the legacy NativeModule system, we're supposed to flush the queue of NativeModule calls after every call into JavaScript. The lack of this flushing means that we execute NativeModule calls less frequently with Fabric enabled, and TurboModules disabled. It also means that [the microtask checkpoints we placed inside JSIExecutor::flush()](https://www.internalfb.com/code/fbsource/[62f69606ae81530f7d6f0cba8466ac604934c901]/xplat/js/react-native-github/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp?lines=427%2C445) won't be executed as frequently, with Fabric enabled. Changelog: [Android][Fixed] - Flush NativeModule calls with Fabric on Android, on every Native -> JS call. Reviewed By: JoshuaGross, mdvacca Differential Revision: D28620982 fbshipit-source-id: ae4d1c16c62b6d4a5089e63104ad97f4ed44c440
180 lines
5.8 KiB
C++
180 lines
5.8 KiB
C++
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <condition_variable>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <mutex>
|
|
|
|
#include <ReactCommon/RuntimeExecutor.h>
|
|
#include <cxxreact/NativeToJsBridge.h>
|
|
|
|
#ifndef RN_EXPORT
|
|
#define RN_EXPORT __attribute__((visibility("default")))
|
|
#endif
|
|
|
|
namespace folly {
|
|
struct dynamic;
|
|
}
|
|
|
|
namespace facebook {
|
|
namespace react {
|
|
|
|
class JSBigString;
|
|
class JSExecutorFactory;
|
|
class MessageQueueThread;
|
|
class ModuleRegistry;
|
|
class RAMBundleRegistry;
|
|
|
|
struct InstanceCallback {
|
|
virtual ~InstanceCallback() {}
|
|
virtual void onBatchComplete() {}
|
|
virtual void incrementPendingJSCalls() {}
|
|
virtual void decrementPendingJSCalls() {}
|
|
};
|
|
|
|
class RN_EXPORT Instance {
|
|
public:
|
|
~Instance();
|
|
void initializeBridge(
|
|
std::unique_ptr<InstanceCallback> callback,
|
|
std::shared_ptr<JSExecutorFactory> jsef,
|
|
std::shared_ptr<MessageQueueThread> jsQueue,
|
|
std::shared_ptr<ModuleRegistry> moduleRegistry);
|
|
|
|
void initializeRuntime();
|
|
|
|
void setSourceURL(std::string sourceURL);
|
|
|
|
void loadScriptFromString(
|
|
std::unique_ptr<const JSBigString> string,
|
|
std::string sourceURL,
|
|
bool loadSynchronously);
|
|
static bool isHBCBundle(const char *sourcePath);
|
|
static bool isIndexedRAMBundle(const char *sourcePath);
|
|
static bool isIndexedRAMBundle(std::unique_ptr<const JSBigString> *string);
|
|
void loadRAMBundleFromString(
|
|
std::unique_ptr<const JSBigString> script,
|
|
const std::string &sourceURL);
|
|
void loadRAMBundleFromFile(
|
|
const std::string &sourcePath,
|
|
const std::string &sourceURL,
|
|
bool loadSynchronously);
|
|
void loadRAMBundle(
|
|
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
|
|
std::unique_ptr<const JSBigString> startupScript,
|
|
std::string startupScriptSourceURL,
|
|
bool loadSynchronously);
|
|
bool supportsProfiling();
|
|
void setGlobalVariable(
|
|
std::string propName,
|
|
std::unique_ptr<const JSBigString> jsonValue);
|
|
void *getJavaScriptContext();
|
|
bool isInspectable();
|
|
bool isBatchActive();
|
|
void callJSFunction(
|
|
std::string &&module,
|
|
std::string &&method,
|
|
folly::dynamic &¶ms);
|
|
void callJSCallback(uint64_t callbackId, folly::dynamic &¶ms);
|
|
|
|
// This method is experimental, and may be modified or removed.
|
|
void registerBundle(uint32_t bundleId, const std::string &bundlePath);
|
|
|
|
const ModuleRegistry &getModuleRegistry() const;
|
|
ModuleRegistry &getModuleRegistry();
|
|
|
|
void handleMemoryPressure(int pressureLevel);
|
|
|
|
/**
|
|
* JS CallInvoker is used by TurboModules to schedule work on the JS thread.
|
|
*
|
|
* Why is the bridge creating JS CallInvoker?
|
|
*
|
|
* - After every Native -> JS call in the TurboModule system, the bridge
|
|
* needs to flush all queued NativeModule method calls. The bridge must
|
|
* also dispatch onBatchComplete if the queue of NativeModule method calls
|
|
* was not empty.
|
|
*/
|
|
std::shared_ptr<CallInvoker> getJSCallInvoker();
|
|
|
|
/**
|
|
* Native CallInvoker is used by TurboModules to schedule work on the
|
|
* NativeModule thread(s).
|
|
*
|
|
* Why is the bridge decorating native CallInvoker?
|
|
*
|
|
* - The bridge must be informed of all TurboModule async method calls. Why?
|
|
* When all queued NativeModule method calls are flushed by a call from
|
|
* Native -> JS, if that queue was non-zero in size, JsToNativeBridge
|
|
* dispatches onBatchComplete. When we turn our NativeModules to
|
|
* TurboModuels, there will be less and less pending NativeModule method
|
|
* calls, so onBatchComplete will not fire as often. Therefore, the bridge
|
|
* needs to know how many TurboModule async method calls have been completed
|
|
* since the last time the bridge was flushed. If this number is non-zero,
|
|
* we fire onBatchComplete.
|
|
*
|
|
* Why can't we just create and return a new native CallInvoker?
|
|
*
|
|
* - On Android, we have one NativeModule thread. That thread is created and
|
|
* managed outisde of NativeToJsBridge. On iOS, we have one MethodQueue per
|
|
* module. Those MethodQueues are also created and managed outside of
|
|
* NativeToJsBridge. Therefore, we need to pass in a CallInvoker that
|
|
* schedules work on the respective thread.
|
|
*/
|
|
std::shared_ptr<CallInvoker> getDecoratedNativeCallInvoker(
|
|
std::shared_ptr<CallInvoker> nativeInvoker);
|
|
|
|
/**
|
|
* RuntimeExecutor is used by Fabric to access the jsi::Runtime.
|
|
*/
|
|
RuntimeExecutor getRuntimeExecutor();
|
|
|
|
private:
|
|
void callNativeModules(folly::dynamic &&calls, bool isEndOfBatch);
|
|
void loadBundle(
|
|
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
|
|
std::unique_ptr<const JSBigString> startupScript,
|
|
std::string startupScriptSourceURL);
|
|
void loadBundleSync(
|
|
std::unique_ptr<RAMBundleRegistry> bundleRegistry,
|
|
std::unique_ptr<const JSBigString> startupScript,
|
|
std::string startupScriptSourceURL);
|
|
|
|
std::shared_ptr<InstanceCallback> callback_;
|
|
std::shared_ptr<NativeToJsBridge> nativeToJsBridge_;
|
|
std::shared_ptr<ModuleRegistry> moduleRegistry_;
|
|
|
|
std::mutex m_syncMutex;
|
|
std::condition_variable m_syncCV;
|
|
bool m_syncReady = false;
|
|
|
|
class JSCallInvoker : public CallInvoker {
|
|
private:
|
|
std::weak_ptr<NativeToJsBridge> m_nativeToJsBridge;
|
|
std::mutex m_mutex;
|
|
bool m_shouldBuffer = true;
|
|
std::list<std::function<void()>> m_workBuffer;
|
|
|
|
void scheduleAsync(std::function<void()> &&work);
|
|
|
|
public:
|
|
void setNativeToJsBridgeAndFlushCalls(
|
|
std::weak_ptr<NativeToJsBridge> nativeToJsBridge);
|
|
void invokeAsync(std::function<void()> &&work) override;
|
|
void invokeSync(std::function<void()> &&work) override;
|
|
};
|
|
|
|
std::shared_ptr<JSCallInvoker> jsCallInvoker_ =
|
|
std::make_shared<JSCallInvoker>();
|
|
};
|
|
|
|
} // namespace react
|
|
} // namespace facebook
|