Skip to content

Commit

Permalink
[0.62] Cherry pick PR #5071 for CallJsFunction queuing (#5193)
Browse files Browse the repository at this point in the history
* [0.62] Cherry pick PR #5071 for CallJsFunction queuing

* Change files

Co-authored-by: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
  • Loading branch information
vmoroz and acoates-ms authored Jun 12, 2020
1 parent 04eb1f9 commit d3bcc6d
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "prerelease",
"comment": "[0.62] Cherry pick PR #5071 for CallJsFunction queuing",
"packageName": "react-native-windows",
"email": "vmorozov@microsoft.com",
"dependentChangeType": "patch",
"date": "2020-06-12T17:45:34.556Z"
}
1 change: 1 addition & 0 deletions vnext/Microsoft.ReactNative/ReactHost/React.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ enum class ReactInstanceState {
WaitingForDebugger,
Loaded,
HasError,
Unloaded,
};

/**An Office wrapper that extends FB's React Instance and makes it a 1:1 relationship with the bundle,
Expand Down
69 changes: 54 additions & 15 deletions vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,19 +76,13 @@ winrt::Microsoft::ReactNative::IReactPropertyBag ReactContext::Properties() noex

void ReactContext::CallJSFunction(std::string &&module, std::string &&method, folly::dynamic &&params) noexcept {
if (auto instance = m_reactInstance.GetStrongPtr()) {
if (instance->State() == ReactInstanceState::Loaded) {
if (auto fbInstance = instance->GetInnerInstance()) {
fbInstance->callJSFunction(std::move(module), std::move(method), std::move(params));
}
}
instance->CallJsFunction(std::move(module), std::move(method), std::move(params));
}
}

void ReactContext::DispatchEvent(int64_t viewTag, std::string &&eventName, folly::dynamic &&eventData) noexcept {
if (auto instance = m_reactInstance.GetStrongPtr()) {
if (instance->State() == ReactInstanceState::Loaded) {
instance->DispatchEvent(viewTag, std::move(eventName), std::move(eventData));
}
instance->DispatchEvent(viewTag, std::move(eventName), std::move(eventData));
}
}

Expand Down Expand Up @@ -371,6 +365,7 @@ void ReactInstanceWin::LoadJSBundles() noexcept {
instanceWrapper->loadBundleSync(Mso::Copy(options.Identity));
} catch (...) {
strongThis->m_state = ReactInstanceState::HasError;
strongThis->AbandonJSCallQueue();
strongThis->OnReactInstanceLoaded(Mso::ExceptionErrorProvider().MakeErrorCode(std::current_exception()));
return;
}
Expand All @@ -390,8 +385,10 @@ void ReactInstanceWin::OnReactInstanceLoaded(const Mso::ErrorCode &errorCode) no
strongThis->m_isLoaded = true;
if (!errorCode) {
strongThis->m_state = ReactInstanceState::Loaded;
strongThis->DrainJSCallQueue();
} else {
strongThis->m_state = ReactInstanceState::HasError;
strongThis->AbandonJSCallQueue();
}

if (auto onLoaded = strongThis->m_options.OnInstanceLoaded.Get()) {
Expand All @@ -414,6 +411,8 @@ Mso::Future<void> ReactInstanceWin::Destroy() noexcept {
}

m_isDestroyed = true;
m_state = ReactInstanceState::Unloaded;
AbandonJSCallQueue();

if (!m_isLoaded) {
OnReactInstanceLoaded(Mso::CancellationErrorProvider().MakeErrorCode(true));
Expand Down Expand Up @@ -583,6 +582,7 @@ std::function<void(std::string)> ReactInstanceWin::GetErrorCallback() noexcept {

void ReactInstanceWin::OnErrorWithMessage(const std::string &errorMessage) noexcept {
m_state = ReactInstanceState::HasError;
AbandonJSCallQueue();

if (m_redboxHandler && m_redboxHandler->isDevSupportEnabled()) {
ErrorInfo errorInfo;
Expand Down Expand Up @@ -636,24 +636,63 @@ void ReactInstanceWin::OnDebuggerAttach() noexcept {
m_updateUI();
}

void ReactInstanceWin::DrainJSCallQueue() noexcept {
// Handle all items in the queue one by one.
for (;;) {
JSCallEntry entry; // To avoid callJSFunction under the lock
{
std::scoped_lock lock{m_mutex};
if (m_state == ReactInstanceState::Loaded && !m_jsCallQueue.empty()) {
entry = std::move(m_jsCallQueue.front());
m_jsCallQueue.pop_front();
} else {
break;
}
}

if (auto instance = m_instance.LoadWithLock()) {
instance->callJSFunction(std::move(entry.ModuleName), std::move(entry.MethodName), std::move(entry.Args));
}
}
}

void ReactInstanceWin::AbandonJSCallQueue() noexcept {
std::deque<JSCallEntry> jsCallQueue; // To avoid destruction under the lock
{
std::scoped_lock lock{m_mutex};
if (m_state == ReactInstanceState::HasError || m_state == ReactInstanceState::Unloaded) {
jsCallQueue = std::move(m_jsCallQueue);
}
}
}

void ReactInstanceWin::CallJsFunction(
std::string &&moduleName,
std::string &&method,
folly::dynamic &&params) noexcept {
// callJSFunction can be called from any thread. The native bridge will post the call to the right queue internally.
if (m_state == ReactInstanceState::Loaded) {
bool shouldCall{false}; // To call callJSFunction outside of lock
{
std::scoped_lock lock{m_mutex};
if (m_state == ReactInstanceState::Loaded && m_jsCallQueue.empty()) {
shouldCall = true;
} else if (
m_state == ReactInstanceState::Loading || m_state == ReactInstanceState::WaitingForDebugger ||
(m_state == ReactInstanceState::Loaded && !m_jsCallQueue.empty())) {
m_jsCallQueue.push_back(JSCallEntry{std::move(moduleName), std::move(method), std::move(params)});
}
// otherwise ignore the call
}

if (shouldCall) {
if (auto instance = m_instance.LoadWithLock()) {
instance->callJSFunction(std::move(moduleName), std::move(method), std::move(params));
}
}
}

void ReactInstanceWin::DispatchEvent(int64_t viewTag, std::string &&eventName, folly::dynamic &&eventData) noexcept {
if (m_state == ReactInstanceState::Loaded) {
if (auto instance = m_instanceWrapper.LoadWithLock()) {
instance->DispatchEvent(viewTag, eventName, std::move(eventData));
}
}
folly::dynamic params = folly::dynamic::array(viewTag, std::move(eventName), std::move(eventData));
CallJsFunction("RCTEventEmitter", "receiveEvent", std::move(params));
}

facebook::react::INativeUIManager *ReactInstanceWin::NativeUIManager() noexcept {
Expand Down
10 changes: 10 additions & 0 deletions vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,15 @@ class ReactInstanceWin final : public Mso::ActiveObject<IReactInstanceInternal,
friend struct LoadedCallbackGuard;
void OnReactInstanceLoaded(const Mso::ErrorCode &errorCode) noexcept;

void DrainJSCallQueue() noexcept;
void AbandonJSCallQueue() noexcept;

struct JSCallEntry {
std::string ModuleName;
std::string MethodName;
folly::dynamic Args;
};

#if defined(USE_V8)
static std::string getApplicationLocalFolder();
#endif
Expand Down Expand Up @@ -162,6 +171,7 @@ class ReactInstanceWin final : public Mso::ActiveObject<IReactInstanceInternal,
Mso::CntPtr<react::uwp::AppearanceChangeListener> m_appearanceListener;
std::string m_bundleRootPath;
Mso::DispatchQueue m_uiQueue;
std::deque<JSCallEntry> m_jsCallQueue;
};

} // namespace Mso::React

0 comments on commit d3bcc6d

Please # to comment.