diff --git a/change/react-native-windows-2020-05-12-11-18-16-MS_UIQueue.json b/change/react-native-windows-2020-05-12-11-18-16-MS_UIQueue.json new file mode 100644 index 00000000000..aeec541b06c --- /dev/null +++ b/change/react-native-windows-2020-05-12-11-18-16-MS_UIQueue.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Use DispatcherQueue instead of CoreDispatcher for Mso::DispatchQueue", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-12T18:18:15.956Z" +} diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h index 87f071637f2..161adc8f9c8 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h @@ -36,8 +36,8 @@ struct SampleModuleCppImpl { void Initialize(ReactContext const &reactContext) noexcept { const ReactPropertyId myProp1{L"Prop1"}; const ReactPropertyId myProp2{L"Prop2"}; - DEBUG_OUTPUT("globalProps.Prop1:", *reactContext.Properties().Get(myProp1)); - DEBUG_OUTPUT("instanceProps.Prop2:", winrt::to_string(*reactContext.Properties().Get(myProp2))); + DEBUG_OUTPUT("C++ Properties.Prop1:", *reactContext.Properties().Get(myProp1)); + DEBUG_OUTPUT("C++ Properties.Prop2:", winrt::to_string(*reactContext.Properties().Get(myProp2))); m_timer = winrt::Windows::System::Threading::ThreadPoolTimer::CreatePeriodicTimer( [this](const winrt::Windows::System::Threading::ThreadPoolTimer) noexcept { diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs index 655e8391d7b..732219ba1e4 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCS/SampleModuleCS.cs @@ -26,8 +26,8 @@ internal sealed class SampleModuleCS [ReactInitializer] public void Initialize(ReactContext reactContext) { - Debug.WriteLine($"C# globalProps.Prop1: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop1"))}"); - Debug.WriteLine($"C# instanceProps.Prop2: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop2"))}"); + Debug.WriteLine($"C# Properties.Prop1: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop1"))}"); + Debug.WriteLine($"C# Properties.Prop2: {reactContext.Handle.Properties.Get(ReactPropertyBagHelper.GetName(null, "Prop2"))}"); _timer = ThreadPoolTimer.CreatePeriodicTimer(new TimerElapsedHandler((timer) => { diff --git a/packages/playground/windows/playground-win32.sln b/packages/playground/windows/playground-win32.sln index 3339e428ab1..705febd73f7 100644 --- a/packages/playground/windows/playground-win32.sln +++ b/packages/playground/windows/playground-win32.sln @@ -40,6 +40,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Common", "..\..\..\vnext\Co EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Microsoft.ReactNative.Cxx", "..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems", "{DA8B35B3-DA00-4B02-BDE6-6A397B3FD46B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Mso", "..\..\..\vnext\Mso\Mso.vcxitems", "{84E05BFA-CBAF-4F0D-BFB6-4CE85742A57E}" +EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\..\..\vnext\ReactWindowsCore\ReactWindowsCore.vcxitems*{11c084a3-a57c-4296-a679-cac17b603144}*SharedItemsImports = 4 @@ -47,6 +49,7 @@ Global ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 ..\..\..\vnext\Mso\Mso.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 ..\..\..\vnext\Shared\Shared.vcxitems*{2d5d43d9-cffc-4c40-b4cd-02efb4e2742b}*SharedItemsImports = 4 + ..\..\..\vnext\Mso\Mso.vcxitems*{84e05bfa-cbaf-4f0d-bfb6-4ce85742a57e}*SharedItemsImports = 9 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{8b88ffae-4dbc-49a2-afa5-d2477d4ad189}*SharedItemsImports = 4 ..\..\..\vnext\JSI\Shared\JSI.Shared.vcxitems*{a62d504a-16b8-41d2-9f19-e2e86019e5e4}*SharedItemsImports = 4 ..\..\..\vnext\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems*{da8b35b3-da00-4b02-bde6-6a397b3fd46b}*SharedItemsImports = 9 diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.cpp b/vnext/Microsoft.ReactNative/IReactDispatcher.cpp new file mode 100644 index 00000000000..4c394e94825 --- /dev/null +++ b/vnext/Microsoft.ReactNative/IReactDispatcher.cpp @@ -0,0 +1,44 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include "pch.h" +#include "IReactDispatcher.h" +#include "ReactDispatcherHelper.g.cpp" + +using namespace winrt; +using namespace Windows::Foundation; + +namespace winrt::Microsoft::ReactNative { + +ReactDispatcher::ReactDispatcher(Mso::DispatchQueue &&queue) noexcept : m_queue{std::move(queue)} {} + +bool ReactDispatcher::HasThreadAccess() noexcept { + return m_queue.HasThreadAccess(); +} + +void ReactDispatcher::Post(ReactDispatcherCallback const &callback) noexcept { + return m_queue.Post([callback]() noexcept { callback(); }); +} + +/*static*/ Mso::DispatchQueue ReactDispatcher::GetUIDispatchQueue(IReactPropertyBag const &properties) noexcept { + return GetUIDispatcher(properties).as()->m_queue; +} + +/*static*/ IReactDispatcher ReactDispatcher::UIThreadDispatcher() noexcept { + return make(Mso::DispatchQueue::MakeCurrentThreadUIQueue()); +} + +/*static*/ ReactPropertyId ReactDispatcher::UIDispatcherProperty() noexcept { + static ReactPropertyId uiThreadDispatcherProperty{L"ReactNative.Dispatcher", L"UIDispatcher"}; + return uiThreadDispatcherProperty; +} + +/*static*/ IReactDispatcher ReactDispatcher::GetUIDispatcher(IReactPropertyBag const &properties) noexcept { + return ReactPropertyBag{properties}.Get(UIDispatcherProperty()); +} + +/*static*/ void ReactDispatcher::SetUIThreadDispatcher(IReactPropertyBag const &properties) noexcept { + ReactPropertyBag{properties}.Set(UIDispatcherProperty(), UIThreadDispatcher()); +} + +} // namespace winrt::Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.h b/vnext/Microsoft.ReactNative/IReactDispatcher.h new file mode 100644 index 00000000000..e71c0526012 --- /dev/null +++ b/vnext/Microsoft.ReactNative/IReactDispatcher.h @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#pragma once +#include "ReactDispatcherHelper.g.h" +#include +#include +#include + +namespace winrt::Microsoft::ReactNative { + +struct ReactDispatcher : implements { + ReactDispatcher() = default; + ReactDispatcher(Mso::DispatchQueue &&queue) noexcept; + + bool HasThreadAccess() noexcept; + void Post(ReactDispatcherCallback const &callback) noexcept; + + static Mso::DispatchQueue GetUIDispatchQueue(IReactPropertyBag const &properties) noexcept; + + static IReactDispatcher UIThreadDispatcher() noexcept; + static ReactPropertyId UIDispatcherProperty() noexcept; + static IReactDispatcher GetUIDispatcher(IReactPropertyBag const &properties) noexcept; + static void SetUIThreadDispatcher(IReactPropertyBag const &properties) noexcept; + + private: + Mso::DispatchQueue m_queue; +}; + +} // namespace winrt::Microsoft::ReactNative + +namespace winrt::Microsoft::ReactNative::implementation { + +struct ReactDispatcherHelper { + ReactDispatcherHelper() = default; + + static IReactDispatcher UIThreadDispatcher() noexcept { + return ReactDispatcher::UIThreadDispatcher(); + } + + static IReactPropertyName UIDispatcherProperty() noexcept { + return ReactDispatcher::UIDispatcherProperty().Handle(); + } +}; + +} // namespace winrt::Microsoft::ReactNative::implementation + +namespace winrt::Microsoft::ReactNative::factory_implementation { + +struct ReactDispatcherHelper : ReactDispatcherHelperT {}; + +} // namespace winrt::Microsoft::ReactNative::factory_implementation diff --git a/vnext/Microsoft.ReactNative/IReactDispatcher.idl b/vnext/Microsoft.ReactNative/IReactDispatcher.idl new file mode 100644 index 00000000000..e5f3d9b6d5f --- /dev/null +++ b/vnext/Microsoft.ReactNative/IReactDispatcher.idl @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import "IReactPropertyBag.idl"; + +namespace Microsoft.ReactNative { + + // The delegate is used to create property value on-demand. + [webhosthidden] + delegate void ReactDispatcherCallback(); + + [webhosthidden] + interface IReactDispatcher + { + // True if dispatcher uses current thread. + Boolean HasThreadAccess { get; }; + + // Post task for the asynchronous execution. + void Post(ReactDispatcherCallback callback); + } + + // Helper methods for the property bag implementation. + [webhosthidden] + static runtimeclass ReactDispatcherHelper + { + // Get or create IReactDispatcher for the current UI thread. + static IReactDispatcher UIThreadDispatcher{ get; }; + + // Get name of the UIDispatcher property for the IReactPropertyBag. + static IReactPropertyName UIDispatcherProperty(); + } +} // namespace ReactNative diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index 68d21f19ac0..3a981cd2ef6 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -1,4 +1,4 @@ - + @@ -299,6 +299,9 @@ IJSValueWriter.idl + + IReactDispatcher.idl + IReactModuleBuilder.idl @@ -467,6 +470,9 @@ IJSValueWriter.idl + + IReactDispatcher.idl + IReactModuleBuilder.idl @@ -547,6 +553,7 @@ + diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index de2019f2870..49caa030be9 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -265,6 +265,7 @@ + Modules @@ -298,7 +299,6 @@ ReactHost - @@ -599,6 +599,7 @@ Base + Modules @@ -648,7 +649,6 @@ ReactHost - @@ -685,6 +685,7 @@ + diff --git a/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp b/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp index 3f980dcbd45..b3e32f5ca5b 100644 --- a/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp +++ b/vnext/Microsoft.ReactNative/Modules/AppStateData.cpp @@ -11,8 +11,8 @@ using namespace xaml; namespace react::uwp { -AppStateData::AppStateData(Mso::React::IReactContext &reactContext) noexcept - : Super(Mso::DispatchQueue::MainUIQueue()), m_lastState{"active"}, m_reactContext{&reactContext} {} +AppStateData::AppStateData(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept + : Super(uiQueue), m_lastState{"active"}, m_reactContext{&reactContext} {} AppStateData::~AppStateData() = default; @@ -59,8 +59,8 @@ void AppStateData::RaiseEvent(char const *newState) noexcept { "RCTDeviceEventEmitter", "emit", folly::dynamic::array("appStateDidChange", std::move(parameters))); } -AppState2::AppState2(Mso::React::IReactContext &reactContext) noexcept - : m_data{Mso::Make(reactContext)} {} +AppState2::AppState2(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept + : m_data{Mso::Make(reactContext, uiQueue)} {} AppState2::~AppState2() = default; diff --git a/vnext/Microsoft.ReactNative/Modules/AppStateData.h b/vnext/Microsoft.ReactNative/Modules/AppStateData.h index ff400f8af81..48bc55672c5 100644 --- a/vnext/Microsoft.ReactNative/Modules/AppStateData.h +++ b/vnext/Microsoft.ReactNative/Modules/AppStateData.h @@ -15,7 +15,7 @@ namespace react::uwp { struct AppStateData : Mso::ActiveObject<> { using Super = ActiveObjectType; - AppStateData(Mso::React::IReactContext &reactContext) noexcept; + AppStateData(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept; ~AppStateData() override; void Initialize() noexcept override; void Finalize() noexcept override; @@ -36,7 +36,7 @@ struct AppStateData : Mso::ActiveObject<> { // It is a temporary class that we need to keep until we remove ReactUWP class AppState2 : public facebook::react::AppState { public: - AppState2(Mso::React::IReactContext &reactContext) noexcept; + AppState2(Mso::React::IReactContext &reactContext, Mso::DispatchQueue const &uiQueue) noexcept; public: // facebook::react::AppState ~AppState2() override; diff --git a/vnext/Microsoft.ReactNative/ReactApplication.cpp b/vnext/Microsoft.ReactNative/ReactApplication.cpp index a4f2d1d4e93..ee7263ae52f 100644 --- a/vnext/Microsoft.ReactNative/ReactApplication.cpp +++ b/vnext/Microsoft.ReactNative/ReactApplication.cpp @@ -5,6 +5,7 @@ #include "ReactApplication.h" #include "ReactApplication.g.cpp" +#include "IReactDispatcher.h" #include "Modules/LinkingManagerModule.h" #include "ReactNativeHost.h" @@ -48,6 +49,7 @@ ReactApplication::ReactApplication(IInspectable const &outer) noexcept : ReactAp ReactNative::ReactInstanceSettings ReactApplication::InstanceSettings() noexcept { if (!m_instanceSettings) { m_instanceSettings = make(); + ReactDispatcher::SetUIThreadDispatcher(m_instanceSettings.Properties()); } return m_instanceSettings; diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index 0218b9b075c..a700f383c2c 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -20,6 +20,7 @@ #include #include +#include "IReactDispatcher.h" #include "Modules/AppStateData.h" #include "Modules/ClipboardModule.h" #include "Modules/DevSettingsModule.h" @@ -153,7 +154,7 @@ void ReactInstanceWin::Initialize() noexcept { InitUIManager(); Mso::PostFuture( - Mso::DispatchQueue::MainUIQueue(), + m_uiQueue, [weakThis = Mso::WeakPtr{this}]() noexcept { // Objects that must be created on the UI thread if (auto strongThis = weakThis.GetStrongPtr()) { @@ -162,7 +163,8 @@ void ReactInstanceWin::Initialize() noexcept { strongThis->m_appTheme = std::make_shared(legacyInstance, strongThis->m_uiMessageThread.LoadWithLock()); react::uwp::I18nHelper().Instance().setInfo(react::uwp::I18nModule::GetI18nInfo()); - strongThis->m_appearanceListener = Mso::Make(legacyInstance); + strongThis->m_appearanceListener = + Mso::Make(legacyInstance, strongThis->m_uiQueue); } }) .Then(Queue(), [ this, weakThis = Mso::WeakPtr{this} ]() noexcept { @@ -198,7 +200,7 @@ void ReactInstanceWin::Initialize() noexcept { devSettings->debuggerConsoleRedirection = false; // JSHost::ChangeGate::ChakraCoreDebuggerConsoleRedirection(); - m_appState = std::make_shared(*m_reactContext); + m_appState = std::make_shared(*m_reactContext, m_uiQueue); // Acquire default modules and then populate with custom modules std::vector cxxModules = react::uwp::GetCoreModules( @@ -454,8 +456,9 @@ void ReactInstanceWin::InitNativeMessageThread() noexcept { void ReactInstanceWin::InitUIMessageThread() noexcept { // Native queue was already given us in constructor. - m_uiMessageThread.Exchange(std::make_shared( - Mso::DispatchQueue::MainUIQueue(), Mso::MakeWeakMemberFunctor(this, &ReactInstanceWin::OnError))); + m_uiQueue = winrt::Microsoft::ReactNative::ReactDispatcher::GetUIDispatchQueue(m_options.Properties); + m_uiMessageThread.Exchange( + std::make_shared(m_uiQueue, Mso::MakeWeakMemberFunctor(this, &ReactInstanceWin::OnError))); m_batchingUIThread = react::uwp::MakeBatchingQueueThread(m_uiMessageThread.Load()); } @@ -517,7 +520,7 @@ std::shared_ptr ReactInstanceWin::GetRedBoxHandler() noexcept { return m_options.RedBoxHandler; } else if (m_options.DeveloperSettings.IsDevModeEnabled) { auto localWkReactHost = m_weakReactHost; - return CreateDefaultRedBoxHandler(std::move(localWkReactHost)); + return CreateDefaultRedBoxHandler(std::move(localWkReactHost), Mso::Copy(m_uiQueue)); } else { return {}; } diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h index 7a90f7da41b..5f2957f8389 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.h @@ -161,6 +161,7 @@ class ReactInstanceWin final : public Mso::ActiveObject m_appTheme; Mso::CntPtr m_appearanceListener; std::string m_bundleRootPath; + Mso::DispatchQueue m_uiQueue; }; } // namespace Mso::React diff --git a/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl b/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl index f388e735f29..fff4532c2d4 100644 --- a/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl +++ b/vnext/Microsoft.ReactNative/ReactInstanceSettings.idl @@ -30,6 +30,6 @@ namespace Microsoft.ReactNative { String DebugBundlePath { get; set; }; String BundleRootPath { get; set; }; UInt16 DebuggerPort { get; set; }; - Microsoft.ReactNative.IRedBoxHandler RedBoxHandler { get; set; }; + IRedBoxHandler RedBoxHandler { get; set; }; } } diff --git a/vnext/Microsoft.ReactNative/RedBox.cpp b/vnext/Microsoft.ReactNative/RedBox.cpp index 699454c743e..0491a64fc9a 100644 --- a/vnext/Microsoft.ReactNative/RedBox.cpp +++ b/vnext/Microsoft.ReactNative/RedBox.cpp @@ -358,19 +358,19 @@ struct RedBox : public std::enable_shared_from_this { * This class is implemented such that the methods on IRedBoxHandler are thread safe. */ struct DefaultRedBoxHandler : public std::enable_shared_from_this, IRedBoxHandler { - DefaultRedBoxHandler(Mso::WeakPtr &&weakReactHost) noexcept - : m_weakReactHost(std::move(weakReactHost)) {} + DefaultRedBoxHandler(Mso::WeakPtr &&weakReactHost, Mso::DispatchQueue &&uiQueue) noexcept + : m_weakReactHost{std::move(weakReactHost)}, m_uiQueue{std::move(uiQueue)} {} ~DefaultRedBoxHandler() { - // Hide any currently showing redboxes - std::vector> redboxes; + // Hide any currently showing redBoxes + std::vector> redBoxes; { std::scoped_lock lock{m_lockRedBox}; - std::swap(m_redboxes, redboxes); + std::swap(m_redBoxes, redBoxes); } - Mso::DispatchQueue::MainUIQueue().Post([redboxes = std::move(redboxes)]() { - for (const auto redbox : redboxes) { - redbox->Dismiss(); + m_uiQueue.Post([redBoxes = std::move(redBoxes)]() { + for (const auto redBox : redBoxes) { + redBox->Dismiss(); } }); } @@ -386,7 +386,7 @@ struct DefaultRedBoxHandler : public std::enable_shared_from_this redbox; { std::scoped_lock lock{m_lockRedBox}; - for (auto it = m_redboxes.begin(); it != m_redboxes.end(); ++it) { + for (auto it = m_redBoxes.begin(); it != m_redBoxes.end(); ++it) { if ((*it)->GetId() == info.Id) { redbox = *it; break; } } } + if (redbox) { - Mso::DispatchQueue::MainUIQueue().Post([redboxCaptured = std::move(redbox), errorInfo = std::move(info)]() { + m_uiQueue.Post([redboxCaptured = std::move(redbox), errorInfo = std::move(info)]() { redboxCaptured->UpdateError(std::move(errorInfo)); }); } } virtual void dismissRedbox() override { - Mso::DispatchQueue::MainUIQueue().Post([wkthis = std::weak_ptr(shared_from_this())]() { + m_uiQueue.Post([wkthis = std::weak_ptr(shared_from_this())]() { if (auto pthis = wkthis.lock()) { std::scoped_lock lock{pthis->m_lockRedBox}; - if (!pthis->m_redboxes.empty()) - pthis->m_redboxes[0]->Dismiss(); + if (!pthis->m_redBoxes.empty()) + pthis->m_redBoxes[0]->Dismiss(); } }); } private: - // When notified by a redbox that its been dismisssed + // When notified by a redbox that its been dismissed void onDismissedCallback(uint32_t id) noexcept { // Save a local ref, so if we are removing the last redbox which could hold the last ref to the DefaultRedBoxHandler // We ensure that we exit the mutex before the handler is destroyed. @@ -436,10 +437,10 @@ struct DefaultRedBoxHandler : public std::enable_shared_from_thisGetId() == id) { redbox = *it; - it = m_redboxes.erase(it); + it = m_redBoxes.erase(it); break; } } @@ -453,8 +454,8 @@ struct DefaultRedBoxHandler : public std::enable_shared_from_this redbox; { std::scoped_lock lock{m_lockRedBox}; - if (!m_redboxes.empty()) { - redbox = m_redboxes[0]; + if (!m_redBoxes.empty()) { + redbox = m_redBoxes[0]; } } @@ -462,13 +463,14 @@ struct DefaultRedBoxHandler : public std::enable_shared_from_thisShowNewJSError(); }); + m_uiQueue.Post([redboxCaptured = std::move(redbox)]() { redboxCaptured->ShowNewJSError(); }); } - bool m_showingRedBox = false; // Access from UI Thread only + private: + const Mso::DispatchQueue m_uiQueue; + bool m_showingRedBox{false}; // Access from UI Thread only std::mutex m_lockRedBox; - std::vector> m_redboxes; // Protected by m_lockRedBox + std::vector> m_redBoxes; // Protected by m_lockRedBox const Mso::WeakPtr m_weakReactHost; }; @@ -564,8 +566,10 @@ std::shared_ptr CreateRedBoxHandler( return std::make_shared(redBoxHandler); } -std::shared_ptr CreateDefaultRedBoxHandler(Mso::WeakPtr &&weakReactHost) noexcept { - return std::make_shared(std::move(weakReactHost)); +std::shared_ptr CreateDefaultRedBoxHandler( + Mso::WeakPtr &&weakReactHost, + Mso::DispatchQueue &&uiQueue) noexcept { + return std::make_shared(std::move(weakReactHost), std::move(uiQueue)); } } // namespace Mso::React diff --git a/vnext/Microsoft.ReactNative/RedBox.h b/vnext/Microsoft.ReactNative/RedBox.h index 0a14590aaea..8d0672a2a54 100644 --- a/vnext/Microsoft.ReactNative/RedBox.h +++ b/vnext/Microsoft.ReactNative/RedBox.h @@ -10,6 +10,8 @@ namespace Mso::React { std::shared_ptr CreateRedBoxHandler( winrt::Microsoft::ReactNative::IRedBoxHandler const &redBoxHandler) noexcept; -std::shared_ptr CreateDefaultRedBoxHandler(Mso::WeakPtr &&weakReactHost) noexcept; +std::shared_ptr CreateDefaultRedBoxHandler( + Mso::WeakPtr &&weakReactHost, + Mso::DispatchQueue &&uiQueue) noexcept; } // namespace Mso::React diff --git a/vnext/Microsoft.ReactNative/RedBoxHandler.cpp b/vnext/Microsoft.ReactNative/RedBoxHandler.cpp index 945bece5241..a368c5bb21f 100644 --- a/vnext/Microsoft.ReactNative/RedBoxHandler.cpp +++ b/vnext/Microsoft.ReactNative/RedBoxHandler.cpp @@ -7,6 +7,7 @@ #include "RedBoxHelper.g.cpp" #endif +#include #include #include #include @@ -32,7 +33,8 @@ struct DefaultRedBoxHandler : winrt::implements(host); Mso::WeakPtr wkHost(hostImpl->ReactHost()); - m_redBoxHandler = Mso::React::CreateDefaultRedBoxHandler(std::move(wkHost)); + m_redBoxHandler = Mso::React::CreateDefaultRedBoxHandler( + std::move(wkHost), ReactDispatcher::GetUIDispatchQueue(host.InstanceSettings().Properties())); } void ShowNewError(IRedBoxErrorInfo const &info, RedBoxErrorType type) noexcept { diff --git a/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp b/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp index cae018a7568..1dfd9e4f3c5 100644 --- a/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp +++ b/vnext/Microsoft.ReactNative/Threading/MessageQueueThreadFactory.cpp @@ -12,7 +12,8 @@ std::shared_ptr MakeJSQueueThread() noexcep } std::shared_ptr MakeUIQueueThread() noexcept { - return std::make_shared(Mso::DispatchQueue::MainUIQueue(), nullptr, nullptr); + return std::make_shared( + Mso::DispatchQueue::MakeCurrentThreadUIQueue(), nullptr, nullptr); } std::shared_ptr MakeSerialQueueThread() noexcept { diff --git a/vnext/Mso/dispatchQueue/dispatchQueue.h b/vnext/Mso/dispatchQueue/dispatchQueue.h index a7c1c0a4a20..50e2ecbe1e0 100644 --- a/vnext/Mso/dispatchQueue/dispatchQueue.h +++ b/vnext/Mso/dispatchQueue/dispatchQueue.h @@ -113,9 +113,6 @@ struct DispatchQueue { //! demand. static DispatchQueue const &ConcurrentQueue() noexcept; - //! Get DispatchQueue associated with the main UI thread. It is created on demand. - static DispatchQueue const &MainUIQueue() noexcept; - //! Create new serial DispatchQueue on top of platform specific thread pool. static DispatchQueue MakeSerialQueue() noexcept; @@ -158,8 +155,7 @@ struct DispatchQueue { //! True if tasks are invoked in a serial order by the queue. bool IsSerial() const noexcept; - //! True if queue is running on current thread or associated with it. E.g. MainUIQueue always returns true for the - //! main UI thread. + //! True if queue is running on current thread or associated with it. bool HasThreadAccess() const noexcept; //! Check if a long running task should yield. @@ -416,9 +412,6 @@ struct IDispatchQueueStatic : IUnknown { //! demand. virtual DispatchQueue const &ConcurrentQueue() noexcept = 0; - //! Get DispatchQueue associated with the main UI thread. It is created on demand. - virtual DispatchQueue const &MainUIQueue() noexcept = 0; - //! Create new serial DispatchQueue on top of platform specific thread pool. virtual DispatchQueue MakeSerialQueue() noexcept = 0; @@ -545,10 +538,6 @@ inline /*static*/ DispatchQueue const &DispatchQueue::ConcurrentQueue() noexcept return IDispatchQueueStatic::Instance()->ConcurrentQueue(); } -inline /*static*/ DispatchQueue const &DispatchQueue::MainUIQueue() noexcept { - return IDispatchQueueStatic::Instance()->MainUIQueue(); -} - inline /*static*/ DispatchQueue DispatchQueue::MakeSerialQueue() noexcept { return IDispatchQueueStatic::Instance()->MakeSerialQueue(); } diff --git a/vnext/Mso/src/dispatchQueue/queueService.cpp b/vnext/Mso/src/dispatchQueue/queueService.cpp index b560a732173..d30f444bdc0 100644 --- a/vnext/Mso/src/dispatchQueue/queueService.cpp +++ b/vnext/Mso/src/dispatchQueue/queueService.cpp @@ -269,11 +269,6 @@ DispatchQueue const &DispatchQueueStatic::ConcurrentQueue() noexcept { return *static_cast(static_cast(&concurrentQueue)); } -DispatchQueue const &DispatchQueueStatic::MainUIQueue() noexcept { - static auto mainUIQueue{Mso::Make(MakeMainUIScheduler())}; - return *static_cast(static_cast(&mainUIQueue)); -} - DispatchQueue DispatchQueueStatic::MakeSerialQueue() noexcept { return Mso::Make(MakeThreadPoolScheduler(/*maxThreads:*/ 1)); } diff --git a/vnext/Mso/src/dispatchQueue/queueService.h b/vnext/Mso/src/dispatchQueue/queueService.h index 0d1b30a9734..a80f72a669b 100644 --- a/vnext/Mso/src/dispatchQueue/queueService.h +++ b/vnext/Mso/src/dispatchQueue/queueService.h @@ -82,14 +82,12 @@ struct QueueLocalValueEntry { struct DispatchQueueStatic : Mso::UnknownObject { static DispatchQueueStatic *Instance() noexcept; static Mso::CntPtr MakeLooperScheduler() noexcept; - static Mso::CntPtr MakeMainUIScheduler() noexcept; static Mso::CntPtr MakeCurrentThreadUIScheduler() noexcept; static Mso::CntPtr MakeThreadPoolScheduler(uint32_t maxThreads) noexcept; public: // IDispatchQueueStatic DispatchQueue CurrentQueue() noexcept override; DispatchQueue const &ConcurrentQueue() noexcept override; - DispatchQueue const &MainUIQueue() noexcept override; DispatchQueue MakeSerialQueue() noexcept override; DispatchQueue MakeLooperQueue() noexcept override; DispatchQueue MakeCurrentThreadUIQueue() noexcept override; diff --git a/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp b/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp index 1753023d3a1..70981a780a1 100644 --- a/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp +++ b/vnext/Mso/src/dispatchQueue/uiScheduler_winrt.cpp @@ -5,38 +5,36 @@ #include "object/refCountedObject.h" #include "queueService.h" #include "taskQueue.h" -#include "winrt/Windows.ApplicationModel.Core.h" -#include "winrt/Windows.UI.Core.h" +#include "winrt/Windows.System.h" using namespace winrt; -using namespace Windows::ApplicationModel::Core; -using namespace Windows::UI::Core; +using namespace Windows::System; namespace Mso { -struct UISchdulerWinRT; +struct UISchedulerWinRT; -//! TaskDispatchedHandler is a DispatchedHandler delegate that we pass to CoreDispatcher. +//! TaskDispatcherHandler is a DispatcherQueueHandler delegate that we pass to DispatcherQueue. //! We use custom ref counting to avoid extra memory allocations and to handle reference to DispatchTask. -struct TaskDispatchedHandler final : impl::abi_t { - TaskDispatchedHandler(UISchdulerWinRT *scheduler) noexcept; +struct TaskDispatcherHandler final : impl::abi_t { + TaskDispatcherHandler(UISchedulerWinRT *scheduler) noexcept; int32_t __stdcall QueryInterface(guid const &id, void **result) noexcept final; uint32_t __stdcall AddRef() noexcept final; uint32_t __stdcall Release() noexcept final; int32_t __stdcall Invoke() noexcept final; private: - UISchdulerWinRT *m_scheduler; + UISchedulerWinRT *m_scheduler; }; -struct UISchdulerWinRT : Mso::UnknownObject { - UISchdulerWinRT(CoreDispatcher &&coreDispatcher) noexcept; - ~UISchdulerWinRT() noexcept override; +struct UISchedulerWinRT : Mso::UnknownObject { + UISchedulerWinRT(DispatcherQueue &&dispatcher) noexcept; + ~UISchedulerWinRT() noexcept override; uint32_t AddHandlerRef() noexcept; uint32_t ReleaseHandlerRef() noexcept; - DispatchedHandler MakeDispatchedHandler() noexcept; + DispatcherQueueHandler MakeDispatcherQueueHandler() noexcept; bool TryTakeTask(Mso::CntPtr &queue, DispatchTask &task) noexcept; public: // IDispatchQueueScheduler @@ -49,39 +47,40 @@ struct UISchdulerWinRT : Mso::UnknownObject m_queue; - Mso::CntPtr m_self; + Mso::CntPtr m_self; uint32_t m_handlerRefCount{0}; uint32_t m_taskCount{0}; bool m_isShutdown{false}; + std::thread::id m_threadId{std::this_thread::get_id()}; }; //============================================================================= -// TaskDispatchedHandler implementation +// TaskDispatcherHandler implementation //============================================================================= -TaskDispatchedHandler::TaskDispatchedHandler(UISchdulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} +TaskDispatcherHandler::TaskDispatcherHandler(UISchedulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} -int32_t __stdcall TaskDispatchedHandler::QueryInterface(guid const &id, void **result) noexcept { - if (is_guid_of(id) || is_guid_of(id) || +int32_t __stdcall TaskDispatcherHandler::QueryInterface(guid const &id, void **result) noexcept { + if (is_guid_of(id) || is_guid_of(id) || is_guid_of(id)) { - *result = static_cast *>(this); + *result = static_cast *>(this); AddRef(); return impl::error_ok; } @@ -94,15 +93,15 @@ int32_t __stdcall TaskDispatchedHandler::QueryInterface(guid const &id, void **r return impl::error_no_interface; } -uint32_t __stdcall TaskDispatchedHandler::AddRef() noexcept { +uint32_t __stdcall TaskDispatcherHandler::AddRef() noexcept { return m_scheduler->AddHandlerRef(); } -uint32_t __stdcall TaskDispatchedHandler::Release() noexcept { +uint32_t __stdcall TaskDispatcherHandler::Release() noexcept { return m_scheduler->ReleaseHandlerRef(); } -int32_t __stdcall TaskDispatchedHandler::Invoke() noexcept { +int32_t __stdcall TaskDispatcherHandler::Invoke() noexcept { Mso::CntPtr queue; DispatchTask task; if (m_scheduler->TryTakeTask(queue, task)) { @@ -113,23 +112,22 @@ int32_t __stdcall TaskDispatchedHandler::Invoke() noexcept { } //============================================================================= -// UISchdulerWinRT implementation +// UISchedulerWinRT implementation //============================================================================= -UISchdulerWinRT::UISchdulerWinRT(CoreDispatcher &&coreDispatcher) noexcept - : m_coreDispatcher{std::move(coreDispatcher)} {} +UISchedulerWinRT::UISchedulerWinRT(DispatcherQueue &&dispatcher) noexcept : m_dispatcher{std::move(dispatcher)} {} -UISchdulerWinRT::~UISchdulerWinRT() noexcept { +UISchedulerWinRT::~UISchedulerWinRT() noexcept { AwaitTermination(); } -uint32_t UISchdulerWinRT::AddHandlerRef() noexcept { +uint32_t UISchedulerWinRT::AddHandlerRef() noexcept { std::lock_guard lock{m_mutex}; return ++m_handlerRefCount; } -uint32_t UISchdulerWinRT::ReleaseHandlerRef() noexcept { - Mso::CntPtr self; +uint32_t UISchedulerWinRT::ReleaseHandlerRef() noexcept { + Mso::CntPtr self; CleanupContext context{this}; { @@ -147,7 +145,7 @@ uint32_t UISchdulerWinRT::ReleaseHandlerRef() noexcept { } } -bool UISchdulerWinRT::TryTakeTask(Mso::CntPtr &queue, DispatchTask &task) noexcept { +bool UISchedulerWinRT::TryTakeTask(Mso::CntPtr &queue, DispatchTask &task) noexcept { { std::lock_guard lock{m_mutex}; VerifyElseCrashSz(m_taskCount, "Task count cannot be negative"); @@ -161,45 +159,47 @@ bool UISchdulerWinRT::TryTakeTask(Mso::CntPtr &queue, Dis return false; } -DispatchedHandler UISchdulerWinRT::MakeDispatchedHandler() noexcept { +DispatcherQueueHandler UISchedulerWinRT::MakeDispatcherQueueHandler() noexcept { VerifyElseCrash(m_mutex.IsLockedByMe()); if (m_handlerRefCount == 0) { - m_self = this; // Keep reference to self while CoreDispatcher owns DispatchedHandler. + m_self = this; // Keep reference to self while DispatcherQueue owns DispatcherQueueHandler. } ++m_handlerRefCount; - return {static_cast(&m_dispatchedHandler), take_ownership_from_abi}; + return {static_cast(&m_dispatcherHandler), take_ownership_from_abi}; } -void UISchdulerWinRT::IntializeScheduler(Mso::WeakPtr &&queue) noexcept { +void UISchedulerWinRT::IntializeScheduler(Mso::WeakPtr &&queue) noexcept { m_queue = std::move(queue); } -bool UISchdulerWinRT::HasThreadAccess() noexcept { - return m_coreDispatcher.HasThreadAccess(); +bool UISchedulerWinRT::HasThreadAccess() noexcept { + // m_dispatcher.HasThreadAccess() is implemented only in Windows 19H1. + // We must use an alternative implementation. + return m_threadId == std::this_thread::get_id(); } -bool UISchdulerWinRT::IsSerial() noexcept { +bool UISchedulerWinRT::IsSerial() noexcept { return true; } -void UISchdulerWinRT::Post() noexcept { - DispatchedHandler handler; +void UISchedulerWinRT::Post() noexcept { + DispatcherQueueHandler handler; { std::lock_guard lock{m_mutex}; if (!m_isShutdown) { ++m_taskCount; - handler = MakeDispatchedHandler(); + handler = MakeDispatcherQueueHandler(); } } if (handler) { - m_coreDispatcher.RunAsync(CoreDispatcherPriority::Normal, std::move(handler)); + m_dispatcher.TryEnqueue(handler); } } -void UISchdulerWinRT::Shutdown() noexcept { +void UISchedulerWinRT::Shutdown() noexcept { CleanupContext context{this}; { std::lock_guard lock{m_mutex}; @@ -208,18 +208,18 @@ void UISchdulerWinRT::Shutdown() noexcept { } } -void UISchdulerWinRT::AwaitTermination() noexcept { +void UISchedulerWinRT::AwaitTermination() noexcept { Shutdown(); m_terminationEvent.Wait(); } //============================================================================= -// UISchdulerWinRT::CleanupContext implementation +// UISchedulerWinRT::CleanupContext implementation //============================================================================= -UISchdulerWinRT::CleanupContext::CleanupContext(UISchdulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} +UISchedulerWinRT::CleanupContext::CleanupContext(UISchedulerWinRT *scheduler) noexcept : m_scheduler{scheduler} {} -UISchdulerWinRT::CleanupContext::~CleanupContext() noexcept { +UISchedulerWinRT::CleanupContext::~CleanupContext() noexcept { if (m_isTerminated) { m_scheduler->m_terminationEvent.Set(); } @@ -231,8 +231,8 @@ UISchdulerWinRT::CleanupContext::~CleanupContext() noexcept { } } -void UISchdulerWinRT::CleanupContext::CheckShutdown() noexcept { - // See if core dispatcher released all handlers without invoking them. +void UISchedulerWinRT::CleanupContext::CheckShutdown() noexcept { + // See if dispatcher queue released all handlers without invoking them. if (m_scheduler->m_taskCount != 0 && m_scheduler->m_handlerRefCount == 0) { m_isShutdown = true; m_scheduler->m_taskCount = 0; @@ -240,20 +240,16 @@ void UISchdulerWinRT::CleanupContext::CheckShutdown() noexcept { } } -void UISchdulerWinRT::CleanupContext::CheckTermination() noexcept { +void UISchedulerWinRT::CleanupContext::CheckTermination() noexcept { m_isTerminated = m_scheduler->m_isShutdown && (m_scheduler->m_handlerRefCount == 0); } //============================================================================= -// DispatchQueueStatic::MakeThreadPoolScheduler implementation +// DispatchQueueStatic::MakeCurrentThreadUIScheduler implementation //============================================================================= -/*static*/ Mso::CntPtr DispatchQueueStatic::MakeMainUIScheduler() noexcept { - return Mso::Make(CoreApplication::MainView().CoreWindow().Dispatcher()); -} - /*static*/ Mso::CntPtr DispatchQueueStatic::MakeCurrentThreadUIScheduler() noexcept { - return Mso::Make(CoreWindow::GetForCurrentThread().Dispatcher()); + return Mso::Make(DispatcherQueue::GetForCurrentThread()); } } // namespace Mso diff --git a/vnext/ReactUWP/Base/UwpReactInstance.cpp b/vnext/ReactUWP/Base/UwpReactInstance.cpp index ce74a02918b..3f447c313b0 100644 --- a/vnext/ReactUWP/Base/UwpReactInstance.cpp +++ b/vnext/ReactUWP/Base/UwpReactInstance.cpp @@ -115,7 +115,7 @@ void UwpReactInstance::Start(const std::shared_ptr &spThis, cons std::shared_ptr appTheme = std::make_shared(spThis, m_defaultNativeThread); I18nHelper::Instance().setInfo(I18nModule::GetI18nInfo()); - auto appearanceListener = Mso::Make(spThis); + auto appearanceListener = Mso::Make(spThis, Mso::DispatchQueue::MakeCurrentThreadUIQueue()); // TODO: Figure out threading. What thread should this really be on? m_initThread = std::make_unique(); diff --git a/vnext/ReactUWP/Modules/AppearanceModule.cpp b/vnext/ReactUWP/Modules/AppearanceModule.cpp index 8f4bba5fa6e..0405de975a2 100644 --- a/vnext/ReactUWP/Modules/AppearanceModule.cpp +++ b/vnext/ReactUWP/Modules/AppearanceModule.cpp @@ -14,10 +14,12 @@ using Method = facebook::xplat::module::CxxModule::Method; namespace react::uwp { -AppearanceChangeListener::AppearanceChangeListener(std::weak_ptr &&reactInstance) noexcept - : Mso::ActiveObject<>(Mso::DispatchQueue::MainUIQueue()), m_weakReactInstance(std::move(reactInstance)) { +AppearanceChangeListener::AppearanceChangeListener( + std::weak_ptr &&reactInstance, + Mso::DispatchQueue const &uiQueue) noexcept + : Mso::ActiveObject<>(uiQueue), m_weakReactInstance(std::move(reactInstance)) { // Ensure we're constructed on the UI thread - VerifyIsInQueueElseCrash(); + VerifyElseCrash(uiQueue.HasThreadAccess()); m_currentTheme = Application::Current().RequestedTheme(); diff --git a/vnext/ReactUWP/Modules/AppearanceModule.h b/vnext/ReactUWP/Modules/AppearanceModule.h index 275c36a3819..1e3b7c6268e 100644 --- a/vnext/ReactUWP/Modules/AppearanceModule.h +++ b/vnext/ReactUWP/Modules/AppearanceModule.h @@ -17,7 +17,7 @@ class AppearanceChangeListener final : public Mso::ActiveObject<> { using UISettings = winrt::Windows::UI::ViewManagement::UISettings; public: - AppearanceChangeListener(std::weak_ptr &&reactInstance) noexcept; + AppearanceChangeListener(std::weak_ptr &&reactInstance, Mso::DispatchQueue const &uiQueue) noexcept; const char *GetColorScheme() const noexcept; private: diff --git a/vnext/ReactUWP/ReactUWP.vcxproj.filters b/vnext/ReactUWP/ReactUWP.vcxproj.filters index 30f2d7c7f54..cc8de5168af 100644 --- a/vnext/ReactUWP/ReactUWP.vcxproj.filters +++ b/vnext/ReactUWP/ReactUWP.vcxproj.filters @@ -106,6 +106,9 @@ Modules\Animated + + Modules + Modules @@ -166,6 +169,7 @@ Polyester + Threading @@ -322,8 +326,6 @@ Views - - @@ -482,6 +484,9 @@ Modules\Animated + + Modules + Modules @@ -657,7 +662,6 @@ Views -