@samitouri / QOS-React-2 / commits / 183d1f42ed

Fix: Measure expiration times relative to module initialization (#15357)

We use bitwise operations to compute expiration times, which means they need to be smaller than 31 bits. So we measure times relative to module initialization, similar to `performance.now`. This was already working in the old fiber scheduler, but we didn't have a test for it.

Andrew Clark committed Apr 8, 2019 at 19:44 UTC 183d1f42edecb9e303c05d1a4303d17ecffb0cc0
2 files changed +30 -2
packages/react-reconciler/src/ReactFiberScheduler.new.js
+3 -2
@@ -228,12 +228,13 @@ let interruptedBy: Fiber | null = null;
228 // In other words, because expiration times determine how updates are batched,
229 // we want all updates of like priority that occur within the same event to
230 // receive the same expiration time. Otherwise we get tearing.
231 +let initialTimeMs: number = now();
232 let currentEventTime: ExpirationTime = NoWork;
233
234 export function requestCurrentTime() {
235 if (workPhase === RenderPhase || workPhase === CommitPhase) {
236 // We're inside React, so it's fine to read the actual time.
236 - return msToExpirationTime(now());
237 + return msToExpirationTime(now() - initialTimeMs);
238 }
239 // We're not inside React, so we may be in the middle of a browser event.
240 if (currentEventTime !== NoWork) {
@@ -241,7 +242,7 @@ export function requestCurrentTime() {
242 return currentEventTime;
243 }
244 // This is the first update since React yielded. Compute a new start time.
244 - currentEventTime = msToExpirationTime(now());
245 + currentEventTime = msToExpirationTime(now() - initialTimeMs);
246 return currentEventTime;
247 }
248
packages/react-reconciler/src/__tests__/ReactExpiration-test.internal.js
+27
@@ -245,4 +245,31 @@ describe('ReactExpiration', () => {
245 '1 [D] [render]',
246 ]);
247 });
248 +
249 + it('should measure expiration times relative to module initialization', () => {
250 + // Tests an implementation detail where expiration times are computed using
251 + // bitwise operations.
252 +
253 + jest.resetModules();
254 + Scheduler = require('scheduler');
255 + // Before importing the renderer, advance the current time by a number
256 + // larger than the maximum allowed for bitwise operations.
257 + const maxSigned31BitInt = 1073741823;
258 + Scheduler.advanceTime(maxSigned31BitInt * 100);
259 +
260 + // Now import the renderer. On module initialization, it will read the
261 + // current time.
262 + ReactNoop = require('react-noop-renderer');
263 +
264 + ReactNoop.render('Hi');
265 +
266 + // The update should not have expired yet.
267 + expect(Scheduler).toFlushExpired([]);
268 + expect(ReactNoop).toMatchRenderedOutput(null);
269 +
270 + // Advance the time some more to expire the update.
271 + Scheduler.advanceTime(10000);
272 + expect(Scheduler).toFlushExpired([]);
273 + expect(ReactNoop).toMatchRenderedOutput('Hi');
274 + });
275 });