Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix[react-devtools]: remove all listeners when Agent is shutdown #31151

Merged

Conversation

hoxyq
Copy link
Contributor

@hoxyq hoxyq commented Oct 8, 2024

Based on #31049, credits to @EdmondChuiHW.

What is happening here:

  1. Once Agent is destroyed, unsubscribe own listeners and bridge listeners.
  2. [Browser extension only] Once Agent is destroyed, unsubscribe listeners from BackendManager.
  3. [Browser extension only] I've discovered that backendManager.js content script can get injected multiple times by the browser. When Frontend is initializing, it will create Store first, and then execute a content script for bootstraping backend manager. If Frontend was destroyed somewhere between these 2 steps, Backend won't be notified, because it is not initialized yet, so it will not unsubscribe listeners correctly. We might end up duplicating listeners, and the next time Frontend is launched, it will report an issues "Cannot add / remove node ...", because same operations are emitted twice.

To reproduce 3 you can do the following:

  1. Click reload-to-profile
  2. Right after when both app and Chrome DevTools panel are reloaded, close Chrome DevTools.
  3. Open Chrome DevTools again, open Profiler panel and observe "Cannot add / remove node ..." error in the UI.

Copy link

vercel bot commented Oct 8, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 8, 2024 11:03am

Copy link
Contributor

@EdmondChuiHW EdmondChuiHW left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the original discussion and credit! Good to see this identified and fixed with a reliable repro

@@ -754,6 +754,9 @@ export default class Agent extends EventEmitter<{
shutdown: () => void = () => {
// Clean up the overlay if visible, and associated events.
this.emit('shutdown');

this._bridge.removeAllListeners();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this safe? What happens if other objects are listening to the shutdown event too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is safe, because Agent is actually notified by Bridge here:

bridge.addListener('shutdown', this.shutdown);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Talked irl. What I was asking was

  1. ProfilerStore subscribes to bridge
  2. Agent subscribes to bridge
  3. Bridge emits shutdown event
  4. Agent remove all listeners here
  5. Would ProfileStore get the shutdown event?

We found that the current impl does account for this (removing all listeners won't prevent existing listeners from getting the currently-processed event):

const clonedListeners = Array.from(listeners);

We should prob still be a good neighbour and only unsubscribe our own listeners tho

@hoxyq hoxyq merged commit 1d8d120 into facebook:main Oct 9, 2024
184 checks passed
@hoxyq hoxyq deleted the react-devtools/remove-listeners-on-agent-shutdown branch October 9, 2024 12:34
hoxyq added a commit that referenced this pull request Oct 15, 2024
Changes in this release:

* Fix React Compiler badging ([poteto](https://github.com/poteto) in
[#31196](#31196))
* fix[react-devtools]: fixed timeline profiler tests
([hoxyq](https://github.com/hoxyq) in
[#31261](#31261))
* fix[react-devtools]: record timeline data only when supported
([hoxyq](https://github.com/hoxyq) in
[#31154](#31154))
* refactor[react-devtools]: flatten reload and profile config
([hoxyq](https://github.com/hoxyq) in
[#31132](#31132))
* fix[react-devtools]: remove all listeners when Agent is shutdown
([hoxyq](https://github.com/hoxyq) in
[#31151](#31151))
* fix[react-devtools]: removed redundant startProfiling call
([hoxyq](https://github.com/hoxyq) in
[#31131](#31131))
* refactor[react-devtools/fiber/renderer]: optimize durations resolution
([hoxyq](https://github.com/hoxyq) in
[#31118](#31118))
* fix[react-devtools]: update profiling status before receiving response
from backend ([hoxyq](https://github.com/hoxyq) in
[#31117](#31117))
* fix[react-devtools]: wrap key string in preformatted text html element
([hoxyq](https://github.com/hoxyq) in
[#31153](#31153))
* chore[react-devtools]: drop legacy context tests
([hoxyq](https://github.com/hoxyq) in
[#31059](#31059))
* chore[react-devtools]: add legacy mode error message to the ignore
list for tests ([hoxyq](https://github.com/hoxyq) in
[#31060](#31060))
* fix[react-devtools]: request hook initialization inside http server
response ([hoxyq](https://github.com/hoxyq) in
[#31102](#31102))
* [Flight] Serialize Server Components Props in DEV
([sebmarkbage](https://github.com/sebmarkbage) in
[#31105](#31105))
* Add: reload to profile for Fusebox
([EdmondChuiHW](https://github.com/EdmondChuiHW) in
[#31021](#31021))
* refactor: allow custom impl of backend realod-to-profile support check
([EdmondChuiHW](https://github.com/EdmondChuiHW) in
[#31048](#31048))
* fix: use public instance in Fiber renderer and expose it from
getInspectorDataForViewAtPoint ([hoxyq](https://github.com/hoxyq) in
[#31068](#31068))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants