Skip to content

Commit 34c5cd8

Browse files
authored
Enable custom timer factory to be provided (#12856)
* Enable custom timer factory to be provided * Change files
1 parent c2a2bd6 commit 34c5cd8

12 files changed

+176
-13
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"type": "prerelease",
3+
"comment": "Enable custom timer factory to be provided",
4+
"packageName": "react-native-windows",
5+
"email": "[email protected]",
6+
"dependentChangeType": "patch"
7+
}

vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,10 @@
310310
<DependentUpon>ReactNativeHost.idl</DependentUpon>
311311
<SubType>Code</SubType>
312312
</ClInclude>
313+
<ClInclude Include="Timer.h">
314+
<DependentUpon>Timer.idl</DependentUpon>
315+
<SubType>Code</SubType>
316+
</ClInclude>
313317
<ClInclude Include="Utils\AccessibilityUtils.h" />
314318
<ClInclude Include="Utils\Helpers.h" />
315319
<ClInclude Include="Utils\KeyboardUtils.h" />

vnext/Microsoft.ReactNative/Modules/Timing.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,10 @@ bool TimerQueue::IsEmpty() {
7373
}
7474

7575
std::unique_ptr<TimerRegistry> TimerRegistry::CreateTimerRegistry(
76-
const winrt::Microsoft::ReactNative::IReactDispatcher &uiDispatcher) noexcept {
76+
const winrt::Microsoft::ReactNative::IReactPropertyBag &properties) noexcept {
7777
auto registry = std::make_unique<TimerRegistry>();
7878
registry->m_timingModule = std::make_shared<Timing>();
79-
registry->m_timingModule->InitializeBridgeless(registry.get(), uiDispatcher);
79+
registry->m_timingModule->InitializeBridgeless(registry.get(), properties);
8080
return registry;
8181
}
8282

@@ -123,16 +123,19 @@ void TimerRegistry::setTimerManager(std::weak_ptr<facebook::react::TimerManager>
123123

124124
void Timing::Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept {
125125
m_context = reactContext;
126+
m_properties = reactContext.Properties().Handle();
126127
m_usePostForRendering = !xaml::TryGetCurrentApplication();
127128
m_uiDispatcher = m_context.UIDispatcher().Handle();
128129
}
129130

130131
void Timing::InitializeBridgeless(
131132
TimerRegistry *timerRegistry,
132-
const winrt::Microsoft::ReactNative::IReactDispatcher &uiDispatcher) noexcept {
133+
const winrt::Microsoft::ReactNative::IReactPropertyBag &properties) noexcept {
133134
m_timerRegistry = timerRegistry;
135+
m_properties = properties;
134136
m_usePostForRendering = !xaml::TryGetCurrentApplication();
135-
m_uiDispatcher = {uiDispatcher};
137+
m_uiDispatcher = {properties.Get(winrt::Microsoft::ReactNative::ReactDispatcherHelper::UIDispatcherProperty())
138+
.try_as<winrt::Microsoft::ReactNative::IReactDispatcher>()};
136139
}
137140

138141
void Timing::DetachBridgeless() {
@@ -183,10 +186,9 @@ void Timing::OnTick() {
183186
}
184187
}
185188

186-
winrt::dispatching::DispatcherQueueTimer Timing::EnsureDispatcherTimer() {
189+
winrt::Microsoft::ReactNative::ITimer Timing::EnsureDispatcherTimer() {
187190
if (!m_dispatcherQueueTimer) {
188-
const auto queue = winrt::dispatching::DispatcherQueue::GetForCurrentThread();
189-
m_dispatcherQueueTimer = queue.CreateTimer();
191+
m_dispatcherQueueTimer = winrt::Microsoft::ReactNative::Timer::Create(m_properties);
190192
m_dispatcherQueueTimer.Tick([wkThis = std::weak_ptr(this->shared_from_this())](auto &&...) {
191193
if (auto pThis = wkThis.lock()) {
192194
pThis->OnTick();

vnext/Microsoft.ReactNative/Modules/Timing.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ class TimerQueue {
5757

5858
struct TimerRegistry : public facebook::react::PlatformTimerRegistry {
5959
static std::unique_ptr<TimerRegistry> CreateTimerRegistry(
60-
const winrt::Microsoft::ReactNative::IReactDispatcher &uiDispatcher) noexcept;
60+
const winrt::Microsoft::ReactNative::IReactPropertyBag &properties) noexcept;
6161

6262
~TimerRegistry();
6363

@@ -86,7 +86,7 @@ struct Timing : public std::enable_shared_from_this<Timing> {
8686

8787
void InitializeBridgeless(
8888
TimerRegistry *timerRegistry,
89-
const winrt::Microsoft::ReactNative::IReactDispatcher &uiDispatcher) noexcept;
89+
const winrt::Microsoft::ReactNative::IReactPropertyBag &properties) noexcept;
9090
void DetachBridgeless();
9191

9292
REACT_METHOD(createTimer)
@@ -101,17 +101,18 @@ struct Timing : public std::enable_shared_from_this<Timing> {
101101
void createTimerOnQueue(uint32_t id, double duration, double jsSchedulingTime, bool repeat) noexcept;
102102
void deleteTimerOnQueue(uint32_t id) noexcept;
103103
void OnTick();
104-
winrt::dispatching::DispatcherQueueTimer EnsureDispatcherTimer();
104+
winrt::Microsoft::ReactNative::ITimer EnsureDispatcherTimer();
105105
void StartRendering();
106106
void PostRenderFrame() noexcept;
107107
void StartDispatcherTimer();
108108
void StopTicks();
109109

110110
winrt::Microsoft::ReactNative::ReactContext m_context; // !bridgeless
111111
TimerRegistry *m_timerRegistry{nullptr}; // bridgeless
112+
winrt::Microsoft::ReactNative::IReactPropertyBag m_properties{nullptr};
112113
TimerQueue m_timerQueue;
113114
xaml::Media::CompositionTarget::Rendering_revoker m_rendering;
114-
winrt::dispatching::DispatcherQueueTimer m_dispatcherQueueTimer{nullptr};
115+
winrt::Microsoft::ReactNative::ITimer m_dispatcherQueueTimer{nullptr};
115116
winrt::weak_ref<winrt::Microsoft::ReactNative::IReactDispatcher> m_uiDispatcher;
116117
bool m_usingRendering{false};
117118
bool m_usePostForRendering{false};

vnext/Microsoft.ReactNative/ReactCoreInjection.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,21 @@ void ReactCoreInjection::SetTopLevelWindowId(const IReactPropertyBag &properties
109109
ReactPropertyBag(properties).Set(TopLevelWindowIdPropertyId(), windowId);
110110
}
111111

112+
static const ReactPropertyId<TimerFactory> &TimerFactoryPropertyId() noexcept {
113+
static const ReactPropertyId<TimerFactory> prop{L"ReactNative.Injection", L"TimerFactory"};
114+
return prop;
115+
}
116+
117+
void ReactCoreInjection::SetTimerFactory(
118+
const IReactPropertyBag &properties,
119+
const TimerFactory &timerFactory) noexcept {
120+
ReactPropertyBag(properties).Set(TimerFactoryPropertyId(), timerFactory);
121+
}
122+
123+
TimerFactory ReactCoreInjection::GetTimerFactory(const IReactPropertyBag &properties) noexcept {
124+
return ReactPropertyBag(properties).Get(TimerFactoryPropertyId()).value_or(nullptr);
125+
}
126+
112127
ReactViewHost::ReactViewHost(
113128
const ReactNative::ReactNativeHost &host,
114129
Mso::React::IReactViewHost &viewHost,

vnext/Microsoft.ReactNative/ReactCoreInjection.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ struct ReactCoreInjection : ReactCoreInjectionT<ReactCoreInjection> {
5252

5353
static uint64_t GetTopLevelWindowId(const IReactPropertyBag &properties) noexcept;
5454
static void SetTopLevelWindowId(const IReactPropertyBag &properties, uint64_t windowId) noexcept;
55+
56+
static ITimer CreateTimer(const IReactPropertyBag &properties);
57+
static TimerFactory GetTimerFactory(const IReactPropertyBag &properties) noexcept;
58+
static void SetTimerFactory(const IReactPropertyBag &properties, const TimerFactory &timerFactory) noexcept;
5559
};
5660

5761
struct ReactViewHost : public winrt::implements<ReactViewHost, IReactViewHost> {

vnext/Microsoft.ReactNative/ReactCoreInjection.idl

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import "IReactContext.idl";
55
import "ReactNativeHost.idl";
66
import "IJSValueReader.idl";
7+
import "Timer.idl";
78

89
#include "DocString.h"
910

@@ -27,6 +28,10 @@ DOC_STRING("Settings per each IReactViewHost associated with an IReactHost insta
2728
};
2829
};
2930

31+
[experimental]
32+
[webhosthidden]
33+
delegate ITimer TimerFactory();
34+
3035
[experimental]
3136
[webhosthidden]
3237
DOC_STRING(
@@ -102,6 +107,9 @@ DOC_STRING("Settings per each IReactViewHost associated with an IReactHost insta
102107
"This must be manually provided to the @ReactInstanceSettings object when using ReactNativeWindows"
103108
"without XAML for certain APIs work correctly.")
104109
static void SetTopLevelWindowId(IReactPropertyBag properties, UInt64 windowId);
110+
111+
DOC_STRING("Sets a factory method for creating custom timers, in environments where system dispatch timers should not be used.")
112+
static void SetTimerFactory(IReactPropertyBag properties, TimerFactory timerFactory);
105113
}
106114

107115

vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -599,8 +599,7 @@ void ReactInstanceWin::InitializeBridgeless() noexcept {
599599

600600
m_jsMessageThread.Load()->runOnQueueSync([&]() {
601601
::SetThreadDescription(GetCurrentThread(), L"React-Native JavaScript Thread");
602-
auto timerRegistry = ::Microsoft::ReactNative::TimerRegistry::CreateTimerRegistry(
603-
m_options.Properties.Get(ReactDispatcherHelper::UIDispatcherProperty()).try_as<IReactDispatcher>());
602+
auto timerRegistry = ::Microsoft::ReactNative::TimerRegistry::CreateTimerRegistry(m_options.Properties);
604603
auto timerRegistryRaw = timerRegistry.get();
605604

606605
auto timerManager = std::make_shared<facebook::react::TimerManager>(std::move(timerRegistry));

vnext/Microsoft.ReactNative/Timer.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#include "pch.h"
5+
#include "Timer.h"
6+
#include "Timer.g.cpp"
7+
8+
#include <CppWinRTIncludes.h>
9+
#include "ReactCoreInjection.h"
10+
11+
namespace winrt::Microsoft::ReactNative::implementation {
12+
13+
// Implementation of ITimer based on Windows' DispatcherQueue
14+
struct ReactTimer : winrt::implements<ReactTimer, ITimer> {
15+
ReactTimer(const IReactPropertyBag &properties) {
16+
const auto queue = winrt::dispatching::DispatcherQueue::GetForCurrentThread();
17+
m_dispatcherQueueTimer = queue.CreateTimer();
18+
}
19+
20+
winrt::Windows::Foundation::TimeSpan Interval() noexcept {
21+
return m_dispatcherQueueTimer.Interval();
22+
}
23+
24+
void Interval(winrt::Windows::Foundation::TimeSpan value) noexcept {
25+
m_dispatcherQueueTimer.Interval(value);
26+
}
27+
28+
void Start() noexcept {
29+
return m_dispatcherQueueTimer.Start();
30+
}
31+
32+
void Stop() noexcept {
33+
return m_dispatcherQueueTimer.Stop();
34+
}
35+
36+
winrt::event_token Tick(const winrt::Windows::Foundation::EventHandler<IInspectable> &handler) {
37+
return m_dispatcherQueueTimer.Tick([handler](auto sender, auto args) { handler(sender, args); });
38+
}
39+
40+
void Tick(winrt::event_token const &token) {
41+
m_dispatcherQueueTimer.Tick(token);
42+
}
43+
44+
private:
45+
winrt::dispatching::DispatcherQueueTimer m_dispatcherQueueTimer{nullptr};
46+
};
47+
48+
Timer::Timer() noexcept {}
49+
50+
ITimer Timer::Create(const IReactPropertyBag &properties) {
51+
auto dispatcher = properties.Get(ReactDispatcherHelper::UIDispatcherProperty()).try_as<IReactDispatcher>();
52+
if (!dispatcher.HasThreadAccess()) {
53+
throw winrt::hresult_wrong_thread();
54+
}
55+
56+
if (auto customTimerFactory = implementation::ReactCoreInjection::GetTimerFactory(properties)) {
57+
return customTimerFactory();
58+
} else {
59+
return winrt::make<ReactTimer>(properties);
60+
}
61+
}
62+
63+
} // namespace winrt::Microsoft::ReactNative::implementation

vnext/Microsoft.ReactNative/Timer.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
#pragma once
5+
6+
#include "Timer.g.h"
7+
8+
#include "winrt/Microsoft.ReactNative.h"
9+
10+
namespace winrt::Microsoft::ReactNative::implementation {
11+
12+
struct Timer : TimerT<Timer> {
13+
Timer() noexcept;
14+
15+
static ITimer Create(const IReactPropertyBag &properties);
16+
};
17+
18+
} // namespace winrt::Microsoft::ReactNative::implementation
19+
20+
namespace winrt::Microsoft::ReactNative::factory_implementation {
21+
struct Timer : TimerT<Timer, implementation::Timer> {};
22+
} // namespace winrt::Microsoft::ReactNative::factory_implementation

0 commit comments

Comments
 (0)