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

[Discussion] Requiring Native Modules in the Renderer Process to be NAPI or Context Aware #18397

Open
MarshallOfSound opened this issue May 22, 2019 · 8 comments

Comments

@MarshallOfSound
Copy link
Member

commented May 22, 2019

Native modules in Electron

Loading native modules in Electron has always been tricky. It's a pain to rebuild, to link to the right node.lib/dll, and to ensure your NODE_MODULE_VERSION is correct. And there's another problem behind the scenes that most users have never had to deal with: Node doesn't let you load multiple instances of a native module in the same process, even in different v8 contexts. Imagine this scenario:

  1. Load https://google.com in BrowserWindow
    • Process is launched.
    • Preload script runs and loads native module X
  2. Load https://google.com/foo in BrowserWindow
    • Chromium decides to reuse the process
    • Preload script runs and loads native module X
    • FAILURE - native module X loaded twice in the same process

We address this in Electron by patching Chromium to create a new process for every navigation. This mostly works but still has a few edge cases (e.g. #4025, #12045, #17576) and a few side effects:

  • We lose all the performance gains from the Chromium plz-navigate changes 😢
  • We have strange behavior with window.open and window.opener as we force child windows into separate processes sometimes. 😢
  • Our patches occasionally have regressions because this isn't a process model that Chromium accepts / tests. 😢

We've always thought "wouldn't it be cool if we could change this". Now, with Node 12's Worker Threads, we see a way to do that.

Worker Threads

Node ran into the same issue when they added worker threads: native modules could not be loaded in multiple workers. Node solved this by introducing the concept of "Context Aware" native modules. This is how native modules tell Node that they are safe to be loaded in multiple v8::Contexts. Nan has a handy helper (NAN_MODULE_WORKER_ENABLED) for doing this.

Context Aware modules and NAPI modules can be instantated multiple times in Node even without our patched process model. As they become more common, our patches will bring less benefit and be more redundant.

What does this mean for Electron?

Right now, nothing. However, at some point in the future -- probably in a few major versions -- we intend to remove our process model patches. At that point, any native Node modules loaded in the renderer process must be either NAPI or Context Aware.

This won't happen tomorrow. It probably won't happen this year. But it will happen, so if you're using or maintaining a native module, you should start looking at the work to mark it as Context Aware. For lots of modules it's as easy as replacing NODE_MODULE with NAN_MODULE_WORKER_ENABLED. For modules that require cleanup, it can be more involved.

You may be wondering why we're changing something that (mostly) works? The main reasons are:

  • Performance. If we don't restart the renderer for every navigation, certain same-site navigations will be much faster than before. 😄
  • Security. When we follow Chromium's process model, we benefit from all the the optimizations and decisions they have made around Site Isolation and related initiatives. 😄
  • Maintenance. Our patches are not easy to keep working, especially as Chromium makes more and more process model performance improvements and optimizations. Removing the patches removes a large maintenance burden. 😄

What does this look like for Native Module X?

The amount of work to become "Context Aware" will be different for every module. For native modules that act as proxies for native getters and setters -- i.e. they just expose JS apis to acccess native APIs -- it's normally as easy as adding a NAN_MODULE_WORKER_ENABLED declaration. You can find an example of that in this pull request.

Some modules will need to do cleanup when the context is destroyed to ensure the module doesn't leaking memory or to cause crashes. You can add hooks to clean up after yourself with node::AddEnvironmentCleanupHook A minimal example of this can be found in this pull request.

Timeline

  • Land the app.allowRendererProcessReuse option in Electron 6
  • Have the first deprecation warning land in Electron 7 for non-context aware native modules
  • Have a deprecation warning land in Electron 8 for the default value of app.allowRendererProcessReuse to switch
  • Switch the default value of app.allowRendererProcessReuse to true in Electron 9
  • Deprecate the ability to change app.allowRendererProcessReuse in Electron 10
  • Remove the ability to change app.allowRendererProcessReuse in Electron 11
@orange4glace

This comment has been minimized.

Copy link

commented May 23, 2019

This is cool. I just encountered such situation that needs multiple native module instances, while it is little sad that it only supports node with V8, not N-API.

@bughit

This comment has been minimized.

Copy link
Contributor

commented May 30, 2019

Are native modules the only reason for the "new process for every navigation"? The following explanation from @zcbenz seems to suggest that node can't fully tear down its one js environment per process, so a new one can take its place.

This is because Node is currently designed to have only one instance in one process, and it doesn't cleanup all the resources one JavaScript environment is destroyed.

@MarshallOfSound

This comment has been minimized.

Copy link
Member Author

commented May 30, 2019

@bughit That situation has changed in recent times, as mentioned in the original issue text with the introduction of worker threads node now supports multiple instances of node per-process along with proper teardown support (this is what making your addon context aware supports). We already teardown node environments when the nodeIntegrationInSubFrames flag is enabled

@bughit

This comment has been minimized.

Copy link
Contributor

commented May 30, 2019

Would you consider adding an experimental option that would deactivate the "process for every navigation" mode even before you completely drop you process model patches?

Assuming that's less work than the full job.

@MarshallOfSound

This comment has been minimized.

Copy link
Member Author

commented May 30, 2019

@bughit See the PR linked --> #18396 in particular the timeline outlined in that PR, what you suggest is already what's being done 😄

@shiftkey shiftkey referenced this issue May 31, 2019
@AnalyzerTech

This comment was marked as off-topic.

Copy link

commented Jun 19, 2019

I don't have all night for this shit. how can I opt out completely. legit code ignored and hidden. I'm probably black Listed hear.

@ghost

This comment was marked as off-topic.

Copy link

commented Jun 29, 2019

Yes

@cynx

This comment has been minimized.

Copy link

commented Sep 1, 2019

Can the decision to remove affinity be reviewed ? I currently don't see a way to open multiple BrowserWindows quickly enough without affinity. This would impact many developers as discussed here - #16319

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants
You can’t perform that action at this time.