@@ -68,244 +68,6 @@ bool TimerQueue::IsEmpty() const {
6868 return m_timerVector.empty ();
6969}
7070
71- // ==============================================================================
72- // TimingHelper
73- // ==============================================================================
74-
75- /* static*/ void TimingHelper::ThreadpoolTimerCallback (PTP_CALLBACK_INSTANCE , PVOID Parameter, PTP_TIMER ) noexcept {
76- static_cast <TimingHelper *>(Parameter)->OnTimerRaised ();
77- }
78-
79- void TimingHelper::OnTimerRaised () noexcept {
80- if (auto inst = m_wkInstance.lock ()) {
81- if (auto nativeThread = m_nativeThread.lock ()) {
82- // Make sure we execute it on native thread for native modules
83- // Capture weak_ptr "this" because callback will be executed on native
84- // thread even if "this" is destroyed.
85- nativeThread->runOnQueue ([weakThis = std::weak_ptr<TimingHelper>(shared_from_this ())]() {
86- auto strongThis = weakThis.lock ();
87- if (!strongThis) {
88- return ;
89- }
90-
91- if ((!strongThis->m_threadpoolTimer ) || strongThis->m_timerQueue .IsEmpty ()) {
92- return ;
93- }
94-
95- folly::dynamic readyTimers = folly::dynamic::array ();
96- auto now = std::chrono::system_clock::now ();
97- auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
98-
99- // Fire timers which will be expired in 10ms
100- while (!strongThis->m_timerQueue .IsEmpty () && now_ms > strongThis->m_timerQueue .Front ().DueTime - 10ms) {
101- // Pop first timer from the queue and add it to list of timers ready
102- // to fire
103- auto next = strongThis->m_timerQueue .Front ();
104- strongThis->m_timerQueue .Pop ();
105-
106- // VSO:1916882 potential overflow
107- readyTimers.push_back (next.Id );
108-
109- // If timer is repeating push it back onto the queue for the next
110- // repetition 'next.Period' being greater than 10ms is intended to
111- // prevent infinite loops
112- if (next.Repeat )
113- strongThis->m_timerQueue .Push (Timer{next.Id , now_ms + next.Period , next.Period , true });
114- }
115-
116- if (!readyTimers.empty ()) {
117- if (auto instance = strongThis->m_wkInstance .lock ()) {
118- instance->callJSFunction (" JSTimers" , " callTimers" , folly::dynamic::array (std::move (readyTimers)));
119- } else {
120- assert (false && " m_wkInstance.lock failed" );
121- }
122- }
123-
124- if (!strongThis->m_timerQueue .IsEmpty ()) {
125- strongThis->SetKernelTimer (strongThis->m_timerQueue .Front ().DueTime );
126- } else {
127- strongThis->m_dueTime = DateTime::max ();
128- }
129- });
130- } else {
131- assert (false && " m_nativeThread.lock failed" );
132- }
133- }
134- }
135-
136- void TimingHelper::createTimer (
137- std::weak_ptr<facebook::react::Instance> instance,
138- uint64_t id,
139- double duration,
140- double jsSchedulingTime,
141- bool repeat) noexcept {
142- SetInstance (instance);
143- auto now = std::chrono::system_clock::now ();
144- auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
145-
146- // Convert double duration to std::chrono::duration
147- auto period = TimeSpan{(int64_t )duration};
148- // Convert int64_t scheduletime to std::chrono::time_point
149- DateTime scheduledTime = DateTime (TimeSpan ((int64_t )jsSchedulingTime));
150- // Calculate the initial due time -- scheduleTime plus duration
151- auto initialDueTime = scheduledTime + period;
152-
153- if (scheduledTime + period <= now_ms && !repeat) {
154- if (auto inst = m_wkInstance.lock ()) {
155- inst->callJSFunction (" JSTimers" , " callTimers" , folly::dynamic::array (folly::dynamic::array (id)));
156- } else {
157- assert (false && " m_wkInstance.lock failed" );
158- }
159- return ;
160- }
161-
162- // Make sure duration is always larger than 16ms to avoid unnecessary wakeups.
163- period = TimeSpan{duration < 16 ? 16 : (int64_t )duration};
164- m_timerQueue.Push (Timer{id, initialDueTime, period, repeat});
165-
166- TimersChanged ();
167- }
168-
169- void TimingHelper::TimersChanged () noexcept {
170- if (m_timerQueue.IsEmpty ()) {
171- // TimerQueue is empty.
172- // Stop the kernel timer only when it is about to fire
173- if (KernelTimerIsAboutToFire ()) {
174- StopKernelTimer ();
175- }
176- return ;
177- }
178- // If front timer has the same target time as ThreadpoolTimer,
179- // we will keep ThreadpoolTimer unchanged.
180- if (m_timerQueue.Front ().DueTime == m_dueTime) {
181- // do nothing
182- }
183- // If current front timer's due time is earlier than current
184- // ThreadpoolTimer's, we need to reset the ThreadpoolTimer to current front
185- // timer
186- else if (m_timerQueue.Front ().DueTime < m_dueTime) {
187- SetKernelTimer (m_timerQueue.Front ().DueTime );
188- }
189- // If current front timer's due time is later than current kernel timer's,
190- // we will reset kernel timer only when it is about to fire
191- else if (KernelTimerIsAboutToFire ()) {
192- SetKernelTimer (m_timerQueue.Front ().DueTime );
193- }
194- }
195-
196- bool TimingHelper::KernelTimerIsAboutToFire () noexcept {
197- // Here we assume if kernel timer is going to fire within 2 frames (about
198- // 33ms), we return true I am not sure the 2 frames assumption is good enough.
199- // We may need adjustment after performance analysis.
200- auto now = std::chrono::system_clock::now ();
201- auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
202- if (m_dueTime - now_ms <= 33ms)
203- return true ;
204- return false ;
205- }
206-
207- void TimingHelper::SetInstance (std::weak_ptr<facebook::react::Instance> instance) noexcept {
208- if (m_wkInstance.expired ())
209- m_wkInstance = instance;
210- }
211-
212- void TimingHelper::SetKernelTimer (DateTime dueTime) noexcept {
213- m_dueTime = dueTime;
214- FILETIME FileDueTime;
215- ULARGE_INTEGER ulDueTime;
216- auto now = std::chrono::system_clock::now ();
217- auto now_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now);
218- TimeSpan period = dueTime - now_ms;
219- ulDueTime.QuadPart = (ULONGLONG ) - (period.count () * 10000 );
220- FileDueTime.dwHighDateTime = ulDueTime.HighPart ;
221- FileDueTime.dwLowDateTime = ulDueTime.LowPart ;
222-
223- if (!m_threadpoolTimer) {
224- InitializeKernelTimer ();
225- }
226-
227- SetThreadpoolTimer (m_threadpoolTimer, &FileDueTime, 0 , 0 );
228- }
229-
230- void TimingHelper::InitializeKernelTimer () noexcept {
231- // Create ThreadPoolTimer
232- m_threadpoolTimer = CreateThreadpoolTimer (&TimingHelper::ThreadpoolTimerCallback, static_cast <PVOID >(this ), NULL );
233- assert (m_threadpoolTimer && " CreateThreadpoolTimer failed." );
234- }
235-
236- void TimingHelper::deleteTimer (uint64_t id) noexcept {
237- if (m_timerQueue.IsEmpty ())
238- return ;
239- if (m_timerQueue.Remove (id)) {
240- TimersChanged ();
241- }
242- }
243-
244- void TimingHelper::StopKernelTimer () noexcept {
245- // Cancel pending callbacks
246- SetThreadpoolTimer (m_threadpoolTimer, NULL , 0 , 0 );
247- m_dueTime = DateTime::max ();
248- }
249-
250- void TimingHelper::setSendIdleEvents (bool /* sendIdleEvents*/ ) noexcept {
251- // It seems we don't need this API. Leave it empty for now.
252- assert (false && " not implemented" );
253- }
254-
255- TimingHelper::~TimingHelper () {
256- if (m_threadpoolTimer) {
257- StopKernelTimer ();
258- WaitForThreadpoolTimerCallbacks (m_threadpoolTimer, true );
259- CloseThreadpoolTimer (m_threadpoolTimer);
260- }
261- }
262-
263- // ==============================================================================
264- // TimingModule
265- // ==============================================================================
266-
267- TimingModule::TimingModule (std::shared_ptr<TimingHelper> &&timing) : m_timing(std::move(timing)) {}
268-
269- std::string TimingModule::getName () {
270- return " Timing" ;
271- }
272-
273- std::map<std::string, dynamic> TimingModule::getConstants () noexcept {
274- return {};
275- }
276-
277- std::vector<module ::CxxModule::Method> TimingModule::getMethods () noexcept {
278- return {
279- Method (
280- " createTimer" ,
281- [this ](dynamic args) // int64_t id, int64_t duration, double
282- // jsSchedulingTime, bool repeat
283- {
284- m_timing->createTimer (
285- getInstance (),
286- jsArgAsInt (args, 0 ),
287- jsArgAsDouble (args, 1 ),
288- jsArgAsDouble (args, 2 ),
289- jsArgAsBool (args, 3 ));
290- }),
291- Method (
292- " deleteTimer" ,
293- [this ](dynamic args) // int64_t code, const std::string& reason,
294- // int64_t id
295- { m_timing->deleteTimer (jsArgAsInt (args, 0 )); }),
296- Method (
297- " setSendIdleEvents" ,
298- [this ](dynamic args) // const std::string& message, int64_t id
299- { m_timing->setSendIdleEvents (jsArgAsBool (args, 0 )); }),
300- };
301- }
302-
303- std::unique_ptr<facebook::xplat::module ::CxxModule> CreateTimingModule (
304- const std::shared_ptr<facebook::react::MessageQueueThread> &nativeThread) noexcept {
305- auto module = std::make_unique<TimingModule>(std::make_shared<TimingHelper>(nativeThread));
306- return std::move (module );
307- }
308-
30971// ==============================================================================
31072// Timing
31173// ==============================================================================
0 commit comments