From 1adbf9665372047eafaec7986681d831b464631e Mon Sep 17 00:00:00 2001 From: Vladimir Morozov Date: Thu, 21 May 2020 13:04:06 -0700 Subject: [PATCH] Implemented support for native module std::weak_ptr --- ...indows-2020-05-21-13-01-25-WeakModule.json | 8 +++ .../SampleLibraryCPP/SampleModuleCPP.h | 49 ++++++++++++- .../Microsoft.ReactNative.Cxx/NativeModules.h | 69 ++++++++++++++++--- 3 files changed, 114 insertions(+), 12 deletions(-) create mode 100644 change/react-native-windows-2020-05-21-13-01-25-WeakModule.json diff --git a/change/react-native-windows-2020-05-21-13-01-25-WeakModule.json b/change/react-native-windows-2020-05-21-13-01-25-WeakModule.json new file mode 100644 index 00000000000..c39f097a0f9 --- /dev/null +++ b/change/react-native-windows-2020-05-21-13-01-25-WeakModule.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Implemented support for native module std::weak_ptr", + "packageName": "react-native-windows", + "email": "vmorozov@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-05-21T20:01:24.822Z" +} diff --git a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h index fd6a18f6d83..08bfdcdd6e7 100644 --- a/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h +++ b/packages/microsoft-reactnative-sampleapps/windows/SampleLibraryCPP/SampleModuleCPP.h @@ -10,7 +10,7 @@ #include "DebugHelpers.h" #include "NativeModules.h" -#define DEBUG_OUTPUT(...) DebugWriteLine("SampleModuleCppImpl", ##__VA_ARGS__); +#define DEBUG_OUTPUT(...) DebugWriteLine("SampleLibraryCpp", ##__VA_ARGS__); namespace SampleLibraryCpp { @@ -42,6 +42,7 @@ struct SampleModuleCppImpl { const ReactNotificationId cppTimerNotification{L"SampleModuleCppImpl", L"TimerNotification"}; const ReactNotificationId csTimerNotification{L"SampleModuleCS", L"TimerNotification"}; + // Note that all notification subscriptions are removed automatically when React instance is unloaded. reactContext.Notifications().Subscribe(csTimerNotification, [ ](winrt::Windows::Foundation::IInspectable const &, ReactNotificationArgs const &args) noexcept { DEBUG_OUTPUT("C++ module, C# timer:", *args.Data()); @@ -256,4 +257,50 @@ struct SampleModuleCppImpl { static constexpr std::chrono::milliseconds TimedEventInterval{5000}; }; +// SampleSharedCppModule shows how to inherited native modules from std::enable_shared_from_this +// to use weak_from_this() in event handlers. In this example we use notifications instead +// of events just to show case the std::weak_ptr use. +REACT_MODULE(SampleSharedCppModule); +struct SampleSharedCppModule : std::enable_shared_from_this { + using IInspectable = winrt::Windows::Foundation::IInspectable; + + // The Initialize method is called when React instance loaded JavaScript and the module is ready to use. + REACT_INIT(Initialize) + void Initialize(React::ReactContext const &reactContext) noexcept { + const ReactNotificationId cppTimerNotification{L"SampleModuleCppImpl", L"TimerNotification"}; + const ReactNotificationId csTimerNotification{L"SampleModuleCS", L"TimerNotification"}; + + // Note that all notification subscriptions are removed automatically when React instance is unloaded. + reactContext.Notifications().Subscribe( + csTimerNotification, + [weakSelf = weak_from_this()](IInspectable const &, ReactNotificationArgs const &args) noexcept { + if (auto strongSelf = weakSelf.lock()) { + strongSelf->ReportCSTimer(*args.Data()); + } + }); + + reactContext.Notifications().Subscribe( + csTimerNotification, + [weakSelf = weak_from_this()](IInspectable const &, ReactNotificationArgs const &args) noexcept { + if (auto strongSelf = weakSelf.lock()) { + strongSelf->ReportCppTimer(*args.Data()); + } + }); + } + + void ReportCSTimer(int timerCount) noexcept { + DEBUG_OUTPUT("SampleSharedCppModule, C# timer:", timerCount); + } + + void ReportCppTimer(int timerCount) noexcept { + DEBUG_OUTPUT("SampleSharedCppModule, C++ timer:", timerCount); + } + + REACT_METHOD(SayHello) + std::string SayHello() noexcept { + // This method is currently unused + return "Hello"; + } +}; + } // namespace SampleLibraryCpp diff --git a/vnext/Microsoft.ReactNative.Cxx/NativeModules.h b/vnext/Microsoft.ReactNative.Cxx/NativeModules.h index 6d76e18f4ca..1123df4dbf6 100644 --- a/vnext/Microsoft.ReactNative.Cxx/NativeModules.h +++ b/vnext/Microsoft.ReactNative.Cxx/NativeModules.h @@ -1108,29 +1108,76 @@ struct TurboModuleSpec { } }; +// The default factory for the TModule. +// It wraps up the TModule into a ReactNonAbiValue to be passed through the ABI boundary. +template +inline std::tuple MakeDefaultReactModuleWrapper() noexcept { + ReactNonAbiValue moduleWrapper{std::in_place}; + TModule *module = moduleWrapper.GetPtr(); + return std::tuple{std::move(moduleWrapper), module}; +} + +// The default factory for TModule inherited from enable_shared_from_this. +// It wraps up the TModule into an std::shared_ptr before giving it to ReactNonAbiValue. +template +inline std::tuple MakeDefaultSharedPtrReactModuleWrapper() noexcept { + ReactNonAbiValue> moduleWrapper{std::in_place, std::make_shared()}; + TModule *module = moduleWrapper.GetPtr()->get(); + return std::tuple{std::move(moduleWrapper), module}; +} + +namespace Internal { +// Internal functions to test if type is inherited from std::enable_shared_from_this. +template +std::true_type IsBaseOfTemplateTest(std::enable_shared_from_this *); +std::false_type IsBaseOfTemplateTest(...); +} // namespace Internal + +// Check if type T is inherited from std::enable_shared_form_this. +// We support the scenario when the T and U are different types. +template +using IsEnabledSharedFromThisT = decltype(Internal::IsBaseOfTemplateTest((T *)nullptr)); +template +inline constexpr bool IsEnabledSharedFromThisV = IsEnabledSharedFromThisT::value; + +// Default implementation of factory getter for a TModule type **not** inherited from std::enable_shared_form_this. +// For the custom implementation define GetReactModuleFactory with the last parameter to be 'int' (not 'int *'). +template , int> = 0> +inline constexpr auto GetReactModuleFactory(TModule * /*moduleNullPtr*/, int * /*useDefault*/) noexcept { + return &MakeDefaultReactModuleWrapper; +} + +// Default implementation of factory getter for a TModule type inherited from std::enable_shared_form_this. +// For the custom implementation define GetReactModuleFactory with the last parameter to be 'int' (not 'int *'). +template , int> = 0> +inline constexpr auto GetReactModuleFactory(TModule * /*moduleNullPtr*/, int * /*useDefault*/) noexcept { + return &MakeDefaultSharedPtrReactModuleWrapper; +} + +// Type traits for TModule. It defines a factory to create the module and its ABI-safe wrapper. +template +struct ReactModuleTraits { + using FactoryType = std::tuple() noexcept; + static constexpr FactoryType *Factory = GetReactModuleFactory((TModule *)nullptr, 0); +}; + +// Create a module provider for TModule type. template inline ReactModuleProvider MakeModuleProvider() noexcept { return [](IReactModuleBuilder const &moduleBuilder) noexcept { - ReactNonAbiValue moduleObject{std::in_place}; - TModule *module = moduleObject.GetPtr(); + auto [moduleWrapper, module] = ReactModuleTraits::Factory(); ReactModuleBuilder builder{module, moduleBuilder}; GetReactModuleInfo(module, builder); builder.CompleteRegistration(); - return moduleObject; + return moduleWrapper; }; } +// Create a module provider for TModule type that satisfies the TModuleSpec. template inline ReactModuleProvider MakeTurboModuleProvider() noexcept { TModuleSpec::template ValidateModule(); - return [](IReactModuleBuilder const &moduleBuilder) noexcept { - ReactNonAbiValue moduleObject{std::in_place}; - TModule *module = moduleObject.GetPtr(); - ReactModuleBuilder builder{module, moduleBuilder}; - GetReactModuleInfo(module, builder); - builder.CompleteRegistration(); - return moduleObject; - }; + return MakeModuleProvider(); } } // namespace winrt::Microsoft::ReactNative