Skip to content

Commit d3bcc6d

Browse files
vmorozacoates-ms
andauthored
[0.62] Cherry pick PR #5071 for CallJsFunction queuing (#5193)
* [0.62] Cherry pick PR #5071 for CallJsFunction queuing * Change files Co-authored-by: Andrew Coates <30809111+acoates-ms@users.noreply.github.com>
1 parent 04eb1f9 commit d3bcc6d

File tree

4 files changed

+73
-15
lines changed

4 files changed

+73
-15
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "[0.62] Cherry pick PR #5071 for CallJsFunction queuing",
4+
"packageName": "react-native-windows",
5+
"email": "vmorozov@microsoft.com",
6+
"dependentChangeType": "patch",
7+
"date": "2020-06-12T17:45:34.556Z"
8+
}

vnext/Microsoft.ReactNative/ReactHost/React.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ enum class ReactInstanceState {
4848
WaitingForDebugger,
4949
Loaded,
5050
HasError,
51+
Unloaded,
5152
};
5253

5354
/**An Office wrapper that extends FB's React Instance and makes it a 1:1 relationship with the bundle,

vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,13 @@ winrt::Microsoft::ReactNative::IReactPropertyBag ReactContext::Properties() noex
7676

7777
void ReactContext::CallJSFunction(std::string &&module, std::string &&method, folly::dynamic &&params) noexcept {
7878
if (auto instance = m_reactInstance.GetStrongPtr()) {
79-
if (instance->State() == ReactInstanceState::Loaded) {
80-
if (auto fbInstance = instance->GetInnerInstance()) {
81-
fbInstance->callJSFunction(std::move(module), std::move(method), std::move(params));
82-
}
83-
}
79+
instance->CallJsFunction(std::move(module), std::move(method), std::move(params));
8480
}
8581
}
8682

8783
void ReactContext::DispatchEvent(int64_t viewTag, std::string &&eventName, folly::dynamic &&eventData) noexcept {
8884
if (auto instance = m_reactInstance.GetStrongPtr()) {
89-
if (instance->State() == ReactInstanceState::Loaded) {
90-
instance->DispatchEvent(viewTag, std::move(eventName), std::move(eventData));
91-
}
85+
instance->DispatchEvent(viewTag, std::move(eventName), std::move(eventData));
9286
}
9387
}
9488

@@ -371,6 +365,7 @@ void ReactInstanceWin::LoadJSBundles() noexcept {
371365
instanceWrapper->loadBundleSync(Mso::Copy(options.Identity));
372366
} catch (...) {
373367
strongThis->m_state = ReactInstanceState::HasError;
368+
strongThis->AbandonJSCallQueue();
374369
strongThis->OnReactInstanceLoaded(Mso::ExceptionErrorProvider().MakeErrorCode(std::current_exception()));
375370
return;
376371
}
@@ -390,8 +385,10 @@ void ReactInstanceWin::OnReactInstanceLoaded(const Mso::ErrorCode &errorCode) no
390385
strongThis->m_isLoaded = true;
391386
if (!errorCode) {
392387
strongThis->m_state = ReactInstanceState::Loaded;
388+
strongThis->DrainJSCallQueue();
393389
} else {
394390
strongThis->m_state = ReactInstanceState::HasError;
391+
strongThis->AbandonJSCallQueue();
395392
}
396393

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

416413
m_isDestroyed = true;
414+
m_state = ReactInstanceState::Unloaded;
415+
AbandonJSCallQueue();
417416

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

584583
void ReactInstanceWin::OnErrorWithMessage(const std::string &errorMessage) noexcept {
585584
m_state = ReactInstanceState::HasError;
585+
AbandonJSCallQueue();
586586

587587
if (m_redboxHandler && m_redboxHandler->isDevSupportEnabled()) {
588588
ErrorInfo errorInfo;
@@ -636,24 +636,63 @@ void ReactInstanceWin::OnDebuggerAttach() noexcept {
636636
m_updateUI();
637637
}
638638

639+
void ReactInstanceWin::DrainJSCallQueue() noexcept {
640+
// Handle all items in the queue one by one.
641+
for (;;) {
642+
JSCallEntry entry; // To avoid callJSFunction under the lock
643+
{
644+
std::scoped_lock lock{m_mutex};
645+
if (m_state == ReactInstanceState::Loaded && !m_jsCallQueue.empty()) {
646+
entry = std::move(m_jsCallQueue.front());
647+
m_jsCallQueue.pop_front();
648+
} else {
649+
break;
650+
}
651+
}
652+
653+
if (auto instance = m_instance.LoadWithLock()) {
654+
instance->callJSFunction(std::move(entry.ModuleName), std::move(entry.MethodName), std::move(entry.Args));
655+
}
656+
}
657+
}
658+
659+
void ReactInstanceWin::AbandonJSCallQueue() noexcept {
660+
std::deque<JSCallEntry> jsCallQueue; // To avoid destruction under the lock
661+
{
662+
std::scoped_lock lock{m_mutex};
663+
if (m_state == ReactInstanceState::HasError || m_state == ReactInstanceState::Unloaded) {
664+
jsCallQueue = std::move(m_jsCallQueue);
665+
}
666+
}
667+
}
668+
639669
void ReactInstanceWin::CallJsFunction(
640670
std::string &&moduleName,
641671
std::string &&method,
642672
folly::dynamic &&params) noexcept {
643-
// callJSFunction can be called from any thread. The native bridge will post the call to the right queue internally.
644-
if (m_state == ReactInstanceState::Loaded) {
673+
bool shouldCall{false}; // To call callJSFunction outside of lock
674+
{
675+
std::scoped_lock lock{m_mutex};
676+
if (m_state == ReactInstanceState::Loaded && m_jsCallQueue.empty()) {
677+
shouldCall = true;
678+
} else if (
679+
m_state == ReactInstanceState::Loading || m_state == ReactInstanceState::WaitingForDebugger ||
680+
(m_state == ReactInstanceState::Loaded && !m_jsCallQueue.empty())) {
681+
m_jsCallQueue.push_back(JSCallEntry{std::move(moduleName), std::move(method), std::move(params)});
682+
}
683+
// otherwise ignore the call
684+
}
685+
686+
if (shouldCall) {
645687
if (auto instance = m_instance.LoadWithLock()) {
646688
instance->callJSFunction(std::move(moduleName), std::move(method), std::move(params));
647689
}
648690
}
649691
}
650692

651693
void ReactInstanceWin::DispatchEvent(int64_t viewTag, std::string &&eventName, folly::dynamic &&eventData) noexcept {
652-
if (m_state == ReactInstanceState::Loaded) {
653-
if (auto instance = m_instanceWrapper.LoadWithLock()) {
654-
instance->DispatchEvent(viewTag, eventName, std::move(eventData));
655-
}
656-
}
694+
folly::dynamic params = folly::dynamic::array(viewTag, std::move(eventName), std::move(eventData));
695+
CallJsFunction("RCTEventEmitter", "receiveEvent", std::move(params));
657696
}
658697

659698
facebook::react::INativeUIManager *ReactInstanceWin::NativeUIManager() noexcept {

vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,15 @@ class ReactInstanceWin final : public Mso::ActiveObject<IReactInstanceInternal,
116116
friend struct LoadedCallbackGuard;
117117
void OnReactInstanceLoaded(const Mso::ErrorCode &errorCode) noexcept;
118118

119+
void DrainJSCallQueue() noexcept;
120+
void AbandonJSCallQueue() noexcept;
121+
122+
struct JSCallEntry {
123+
std::string ModuleName;
124+
std::string MethodName;
125+
folly::dynamic Args;
126+
};
127+
119128
#if defined(USE_V8)
120129
static std::string getApplicationLocalFolder();
121130
#endif
@@ -162,6 +171,7 @@ class ReactInstanceWin final : public Mso::ActiveObject<IReactInstanceInternal,
162171
Mso::CntPtr<react::uwp::AppearanceChangeListener> m_appearanceListener;
163172
std::string m_bundleRootPath;
164173
Mso::DispatchQueue m_uiQueue;
174+
std::deque<JSCallEntry> m_jsCallQueue;
165175
};
166176

167177
} // namespace Mso::React

0 commit comments

Comments
 (0)