Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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