Commit Graph

3306 Commits

Author SHA1 Message Date
Andrew Clark 48740429b4 Expiration: Do nothing except disable time slicing (#21345)
We have a feature called "expiration" whose purpose is to prevent
a concurrent update from being starved by higher priority events.
If a lane is CPU-bound for too long, we finish the rest of the work
synchronously without allowing further interruptions.

In the current implementation, we do this in sort of a roundabout way:
once a lane is determined to have expired, we entangle it with SyncLane
and switch to the synchronous work loop.

There are a few flaws with the approach. One is that SyncLane has a
particular semantic meaning besides its non-yieldiness. For example,
`flushSync` will force remaining Sync work to finish; currently, that
also includes expired work, which isn't an intended behavior, but rather
an artifact of the implementation.

An event worse example is that passive effects triggered by a Sync
update are flushed synchronously, before paint, so that its result
is guaranteed to be observed by the next discrete event. But expired
work has no such requirement: we're flushing expired effects before
paint unnecessarily.

Aside from the behaviorial implications, the current implementation has
proven to be fragile: more than once, we've accidentally regressed
performance due to a subtle change in how expiration is handled.

This PR aims to radically simplify how we model starvation protection by
scaling back the implementation as much as possible. In this new model,
if a lane is expired, we disable time slicing. That's it. We don't
entangle it with SyncLane. The only thing we do is skip the call to
`shouldYield` in between each time slice. This is identical to how we
model synchronous-by-default updates in React 18.
2021-04-24 16:32:48 -07:00
Andrew Clark 0f5ebf366e Delete unreferenced type (#21343)
Had already deleted all the uses but didn't remove the type itself.
2021-04-23 15:41:53 -07:00
Sebastian Markbåge 9cd52b27fe Restore context after an error happens (#21341)
Typically we don't need to restore the context here because we assume that
we'll terminate the rest of the subtree so we don't need the correct
context since we're not rendering any siblings.

However, after a nested suspense boundary we need to restore the context.
The boundary could do this but since we're already doing this in the
suspense branch of renderNode, we might as well do it in the error case
which isn't very perf sensitive anyway.
2021-04-23 12:24:10 -07:00
Sebastian Markbåge ad091759a9 Revert "Emit reactroot attribute on the first element we discover (#21154)" (#21340)
This reverts commit 266c26ad45.
2021-04-23 10:28:44 -07:00
Sebastian Markbåge 709f948412 [Fizz] Add FB specific streaming API and build (#21337)
Add FB specific streaming API and build
2021-04-22 16:54:29 -07:00
Sebastian Markbåge af5037a7a8 [Fizz] Wire up the Fixture (#21273)
* Wire up fizz to fixture

* Fixed typo conditional
2021-04-22 15:00:35 -07:00
Andrew Clark e8cdce40d6 Don't flush sync at end of discreteUpdates (#21327)
All it should do is change the priority. The updates will be flushed
by the microtask.
2021-04-22 15:28:05 -05:00
Andrew Clark a155860018 Fix: Don't flush discrete at end of batchedUpdates (#21229)
The outermost `batchedUpdates` call flushes pending sync updates at the
end. This was intended for legacy sync mode, but it also happens to
flush discrete updates in concurrent mode.

Instead, we should only flush sync updates at the end of
`batchedUpdates` for legacy roots. Discrete sync updates can wait to
flush in the microtask.

`discreteUpdates` has the same issue, which is how I originally noticed
this, but I'll change that one in a separate commit since it requires
updating a few (no longer relevant) internal tests.
2021-04-21 09:32:09 -07:00
Andrew Clark 89847bf6e6 Continuous updates should interrupt transitions (#21323)
Even when updates are sync by default.

Discovered this quirk while working on #21322. Previously, when sync
default updates are enabled, continuous updates are treated like
default updates. We implemented this by assigning DefaultLane to
continous updates. However, an unintended consequence of that approach
is that continuous updates would no longer interrupt transitions,
because default updates are not supposed to interrupt transitions.

To fix this, I changed the implementation to always assign separate
lanes for default and continuous updates. Then I entangle the
lanes together.
2021-04-21 08:51:04 -07:00
Andrew Clark ef37d55b68 Use performConcurrentWorkOnRoot for "sync default" (#21322)
Instead of `performSyncWorkOnRoot`.

The conceptual model is that the only difference between sync default
updates (in React 18) and concurrent default updates (in a future major
release) is time slicing. All other behavior should be the same
(i.e. the stuff in `finishConcurrentRender`).

Given this, I think it makes more sense to model the implementation this
way, too. This exposed a quirk in the previous implementation where
non-sync work was sometimes mistaken for sync work and flushed too
early. In the new implementation, `performSyncWorkOnRoot` is only used
for truly synchronous renders (i.e. `SyncLane`), which should make these
mistakes less common.

Fixes most of the tests marked with TODOs from #21072.
2021-04-21 08:29:31 -07:00
Ricky a632f7de3b Flip tuple order of useTransition (#20976) 2021-04-20 12:21:44 -04:00
Andrew Clark bd7f4a013b Fix sloppy factoring in performSyncWorkOnRoot (#21246)
* Warn if `finishedLanes` is empty in commit phase

See #21233 for context.

* Fix sloppy factoring when assigning finishedLanes

`finishedLanes` is assigned in `performSyncWorkOnRoot` and
`performSyncWorkOnRoot`. It's meant to represent whichever lanes we
used to render, but because of some sloppy factoring, it can sometimes
equal `NoLanes`.

The fixes are:
- Always check if the lanes are not `NoLanes` before entering the work
loop. There was a branch where this wasn't always true.
- In `performSyncWorkOnRoot`, don't assume the next lanes are sync; the
priority may have changed, or they may have been flushed by a
previous task.
- Don't re-assign the `lanes` variable (the one that gets assigned to
`finishedLanes` until right before we enter the work loop, so that it
is always corresponds to the newest complete root.
2021-04-20 09:14:55 -07:00
Andrew Clark 78120032d4 Remove flushDiscreteUpdates from end of event (#21223)
We don't need this anymore because we flush in a microtask.

This should allow us to remove the logic in the event system that
tracks nested event dispatches.

I added a test to confirm that nested event dispatches don't triggger
a synchronous flush, like they would if we wrapped them `flushSync`. It
already passed; I added it to prevent a regression.
2021-04-20 08:25:31 -07:00
Ricky a3a7adb83e Turn off enableSyncDefaultUpdates in test renderer (#21319) 2021-04-19 22:20:48 -07:00
Brian Vaughn cdb6b4c554 Only hide outermost host nodes when Offscreen is hidden (#21250) 2021-04-19 21:33:42 -04:00
Brian Vaughn 7becb2ff1b DevTools version bump 4.12.3 -> 4.12.4 2021-04-19 21:23:01 -04:00
Brian Vaughn 83bdc565f9 Remove @octokit/rest dependency from DevTools (#21317) 2021-04-19 21:20:34 -04:00
Brian Vaughn b9c6a2b30e Remove LayoutStatic check from commit phase (#21249) 2021-04-19 15:16:24 -04:00
Brian Vaughn 8f202a7c8d DevTools version bump 4.12.2 -> 4.12.3 2021-04-19 13:09:07 -04:00
Brian Vaughn 4def1ceee2 Update DevTools Error strings to support GitHub fuzzy search (#21314) 2021-04-19 13:05:28 -04:00
Andrew Clark af1a4cbf7a Revert expiration for retry lanes (#21300)
Retries should be allowed to expire if they are CPU bound for too long,
but when I made this change it caused a spike in browser crashes. There
must be some other underlying bug; not super urgent but ideally should
figure out why and fix it. Unfortunately we don't have a repro for the
crashes, only detected via production metrics.
2021-04-16 16:45:09 -05:00
Brian Vaughn d14b6a4bdd DevTools version bump 4.12.1 -> 4.12.2 2021-04-16 12:35:29 -04:00
Brian Vaughn 5027eb4650 DevTools fork console patching logic (#21301)
React has its own component stack generation code that DevTools embeds a fork of, but both of them use a shared helper for disabling console logs. This shared helper is DEV only though, because it was intended for use with React DEV-only warnings and we didn't want to unnecessarily add bytes to production builds.

But DevTools itself always ships as a production build– even when it's used to debug DEV bundles of product apps (with third party DEV-only warnings). That means this helper was always a noop.

The resolveCurrentDispatcher method was changed recently to replace the thrown error with a call to console.error. This newly logged error ended up slipping through and being user visible because of the above issue.

This PR updates DevTools to also fork the console patching logic (to remove the DEV-only guard).

Note that I didn't spot this earlier because my test harness (react-devtools-shell) always runs in DEV mode. 🤡
2021-04-16 12:01:47 -04:00
Brian Vaughn c1a53ad2b2 DevTools pre-filter GH issues by repo (#21292) 2021-04-15 19:56:41 -04:00
Sebastian Markbåge cc4b431dab Mark boundary as client rendered even if aborting fallback (#21294) 2021-04-15 19:16:40 -04:00
Ricky f7cdc89361 Also turn off enableSyncDefaultUpdates in RN test renderer (#21293) 2021-04-15 16:31:46 -04:00
Ricky 4c9eb2af1e Add dynamic flags to React Native (#21291)
* Add dynamic flags to React Native

* Hardcode the setting to false instead
2021-04-15 15:30:09 -04:00
Brian Vaughn f3337aa544 DevTools error boundary: Search for pre-existing GH issues (#21279) 2021-04-15 13:34:54 -04:00
Sebastian Markbåge 9eddfbf5af [Fizz] Two More Fixes (#21288)
* Emit value of option tags

* Mask the legacy context passed to classes
2021-04-15 10:26:49 -07:00
Sebastian Markbåge 11b07597ee Fix classes (#21283) 2021-04-15 08:06:31 -07:00
Sebastian Markbåge 96d00b9bba [Fizz] Random Fixes (#21277) 2021-04-14 23:29:30 -04:00
Sebastian Markbåge 0e100ed00f Gate a test (#21275)
Rebase error
2021-04-14 16:37:44 -07:00
Sebastian Markbåge 81ef539535 Always insert a dummy node with an ID into fallbacks (#21272) 2021-04-14 15:39:51 -07:00
Sebastian Markbåge 266c26ad45 Emit reactroot attribute on the first element we discover (#21154)
This may not be the first root element if the root is a fragment and the
second one unsuspends first. But this tag doesn't work well for root
fragments anyway.
2021-04-14 15:09:22 -07:00
Sebastian Markbåge a4a940d7a1 [Fizz] Add unsupported Portal/Scope components (#21261)
* Update Portal error message

* Add Scope Component
2021-04-14 14:35:36 -07:00
Sebastian Markbåge f4d7a0f1ea Implement useOpaqueIdentifier (#21260)
The format of this ID is specific to the format.
2021-04-14 14:25:42 -07:00
Sebastian Markbåge dde875dfb1 [Fizz] Implement Hooks (#21257)
* Implement Fizz Hooks

This is pretty much just a copy of the partial renderer Hooks.

* Implement forward ref and memo
2021-04-14 14:16:41 -07:00
Sebastian Markbåge a597c2f5dc [Fizz] Fix reentrancy bug (#21270)
* Fix reentrancy bug

* Fix another reentrancy bug

There's also an issue if we try to schedule something to be client
rendered if its fallback hasn't rendered yet. So we don't do it
in that case.
2021-04-14 13:49:14 -07:00
Brian Vaughn 68097787f6 DevTools should use reconciler version (rather than renderer version)
when available (#21269)
2021-04-14 14:48:05 -04:00
Brian Vaughn 15e779d921 Reconciler should inject its own version into DevTools hook (#21268) 2021-04-14 14:47:36 -04:00
Sebastian Markbåge 4f76a28c93 [Fizz] Implement New Context (#21255)
* Add NewContext module

This implements a reverse linked list tree containing the previous
contexts.

* Implement recursive algorithm

This algorithm pops the contexts back to a shared ancestor on the way down
the stack and then pushes new contexts in reverse order up the stack.

* Move isPrimaryRenderer to ServerFormatConfig

This is primarily intended to be used to support renderToString with a
separate build than the main one. This allows them to be nested.

* Wire up more element type matchers

* Wire up Context Provider type

* Wire up Context Consumer

* Test

* Implement reader in class

* Update error codez
2021-04-14 11:45:42 -07:00
Brian Vaughn 6b3d86a2e9 Fixed bug in react-detools-inline frontend init method (#21265) 2021-04-14 13:27:21 -04:00
Sebastian Markbåge dbadfa2c36 [Fizz] Classes Follow Up (#21253)
* Port Classes from Fiber to Fizz

* Test
2021-04-13 13:57:36 -07:00
Brian Vaughn 84c06fef81 Add createBridge and createStore exports to react-devtools-inline (for Replay integration) (#21032) 2021-04-12 17:07:14 -04:00
Joshua Gross 686b635b71 Prevent reading canonical property of null (#21242)
* Prevent reading canonical property of null

* prettier
2021-04-12 11:10:51 -07:00
Brian Vaughn 9d48779b36 Fixed broken feature flag import for DT inline package (#21237) 2021-04-11 14:59:52 -04:00
Andrew Clark bb88ce95a8 Bugfix: Don't rely on finishedLanes for passive effects (#21233)
I recently started using `pendingPassiveEffectsLanes` to check if there were any pending
passive effects (530027a). `pendingPassiveEffectsLanes` is the value of
`root.finishedLanes` at the beginning of the commit phase. When there
are pending passive effects, it should always be a non-zero value,
because it represents the lanes used to render the effects.

But it turns out that `root.finishedLanes` isn't always correct.
Sometimes it's `NoLanes` even when there's a new commit.

I found this while investigating an internal bug report. The only repro
I could get was via a headless e2e test runner; I couldn't get one in an
actual browser, or other interactive environment. I used the e2e test to
bisect and confirm the fix. But I don't know yet know how to write a
regression test for the precise underlying scenario. I can probably
reverse engineer one by studying the code; after a quick glance
at `performConcurrentWorkOnRoot` and `performSyncWorkOnRoot`, it's not
hard to see how this might happen.

In the meantime, I'll revert the recent change that exposed the bug.

I was surprised that this had never come up before, since the code that
assigns `root.finishedLanes` is in an extremely hot path, and it hasn't
changed in a while. The reason is that, before 530027a,
`root.finishedLanes` was only used by the DevTools profiler, which is
probably why we had never noticed any issues. In addition to fixing the
inconsistency, we might also consider making `finishedLanes` a
profiling-only field.
2021-04-11 02:09:41 -07:00
Sebastian Markbåge 343710c923 [Fizz] Fragments and Iterable support (#21228) 2021-04-10 15:50:42 -04:00
Ricky 933880b454 Make time-slicing opt-in (#21072)
* Add enableSyncDefaultUpdates feature flag

* Add enableSyncDefaultUpdates implementation

* Fix tests

* Switch feature flag to true by default

* Finish concurrent render whenever for non-sync lanes

* Also return DefaultLane with eventLane

* Gate interruption test

* Add continuout native event test

* Fix tests from rebasing main

* Hardcode lanes, remove added export

* Sync forks
2021-04-09 19:50:09 -04:00
Sebastian Markbåge b0407b55ff Support more empty types (#21225)
Undefined errors as a direct return value.

This changes semantics for "true" and functions to mirror the client.
2021-04-09 16:23:37 -07:00