node icon indicating copy to clipboard operation
node copied to clipboard

node-api: use c-based api for libnode embedding

Open vmoroz opened this issue 5 months ago • 4 comments

Note: this is an active work in progress and there are still a lot of code churning. You are welcome to comment on the code and share your thoughts, but please be aware that the code is not final yet.

This is a temporary spin off from the PR #43542. This separate PR is created to simplify merging and rebasing with the latest code while we discuss the new API design. When the code is ready it should be merged back to PR #43542.

The goal of the original PR is to enable C API and the Node-API for the embedded scenarios. The C API allows using the shared libnode from runtimes that do not interop with C++ such as WASM, C#, Java, etc. This PR works towards the same goal with some changes to the original code.

This is the related issue #23265.

The API design principles

  • Follow the best practices of the Node-API design and provide a way to interop with it.
  • Prefix the new API constructs with node_embedding_.
  • Design the API for ABI safety and being future proof for new requirements.
    • Follow the Builder pattern for the API design.
    • The typical use is to create an object, configure it, initialize it based on the configuration, use it, and then delete it. The configuration changes are prohibited after the object is initialized.
    • What if the initialization sequence must be customized? It means that we add a new configuration function and insert a customization hook into the initialization sequence. Thus, we can evolve the API by adding new configuration functions, and occasionally deprecating the old functions.
    • All behavior changes must be associated with a new API version number.

The API usage

  • To use the C embedding API, we must create, configure, and initialize the global node_embedding_platform. It initializes Node and V8 JS engine once per process and parses the CLI arguments.
  • Then, we create, configure, and initialize one or more node_embedding_runtimes. A runtime is responsible for running JavaScript code.
  • The runtime CLI arguments are initialized by default with the args and exec_args from the result of the platform initialization. They can be overridden while configuring the runtime.
  • A runtime can run in its own thread, several runtimes can share the same thread, or the same runtime can be run from multiple threads.
  • The runtime event loop APIs provide control over the runtime execution. These functions can be called many times because they do not destroy the runtime in the end.
  • The runtime offers to specify version of Node-API and to retrieve the associated napi_api instance. Any Node-API code that uses the napi_env must be run in the runtime scope controlled by node_embedding_runtime_open_scope and node_embedding_runtime_close_scope functions.

The API overview

Based on the use scenarios, the API can be split up into six groups.

Error handling API

  • node_embedding_on_error sets the global error handling hook.

Global platform API

  • node_embedding_set_api_version
  • node_embedding_run_main
  • node_embedding_create_platform
  • node_embedding_delete_platform
  • node_embedding_platform_set_flags
  • node_embedding_platform_get_parsed_args

Runtime API

  • node_embedding_run_runtime
  • node_embedding_create_runtime
  • node_embedding_delete_runtime
  • node_embedding_runtime_set_flags
  • node_embedding_runtime_set_args
  • node_embedding_runtime_on_preload
  • node_embedding_runtime_on_start_execution
  • node_embedding_runtime_add_module
  • [ ] add API to handle unhandled exceptions

Runtime API to run event loops

  • node_embedding_on_wake_up_event_loop
  • node_embedding_run_event_loop
  • node_embedding_complete_event_loop
  • [ ] add API for emitting beforeExit event
  • [ ] add API for emitting exit event
  • [ ] add API to stop the event loop

Runtime API to interop with Node-API

  • node_embedding_run_node_api
  • node_embedding_open_node_api_scope
  • node_embedding_close_node_api_scope

Documentation

  • The new C embedding API is added to the existing embedding.md file after the C++ embedding API description.
  • The index.md is changed to indicate that the embedding.md has docs for C++ and C APIs.
  • [ ] TODO: complete the examples section.

Tests

  • The new C embedding API tests pass the same scenarios as the C++ embedding API tests.
  • The embedtest executable can be run in several modes controlled by the first CLI argument. It effectively contains several main functions for different test scenarios.
  • The JS test code is changed to provide the test mode argument based on the scenario.
  • Added several new test scenarios:
    • run several Node.js runtimes each in its own thread;
    • run several Node.js runtimes all in the same thread;
    • run Node.js runtime from different threads.
    • test that preload callback is called for the main and worker threads.

The PR status

The code is not 100% complete yet. There are still a few TODO items, but I would like to start a discussion with the Node-API team about the new API.

  • [ ] Address outstanding TODOs
    • [ ] Allow running Node.js uv_loop from UI loop. Follow the Electron implementation. - Complete implementation for non-Windows.
    • [ ] Can we use some kind of waiter concept instead of the observer thread?
    • [ ] Generate the main script based on the runtime settings.
    • [ ] Set the global Inspector for he main runtime.
    • [ ] Start workers from C++.
    • [ ] Worker to inherit parent Inspector.
    • [ ] Cancel pending event loop tasks on runtime deletion.
    • [ ] Can we initialize platform again if it returns early?
    • [ ] Test passing the V8 thread pool size.
    • [ ] Add a way to terminate the runtime.
    • [ ] Allow to provide custom thread pool from the app.
    • [ ] Consider adding a v-table for the API functions to simplify binding with other languages.
    • [ ] We must not exit the process on node::Environment errors.
    • [ ] Be explicit about the recoverable errors.
    • [ ] Store IsolateScope in TLS.
  • [ ] Review the API design
  • [ ] Write docs

vmoroz avatar Aug 30 '24 14:08 vmoroz