-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
N-API: An api for embedding Node in applications #23265
Comments
A big part of that is that they haven’t ever been designed as a coherent API (or designed at all, really), and we would likely need to iterate on them a bit more before they are stable – which is probably also the point where we can start to talk about enabling N-API versions of them. If you want to work on this, good starting points might be #21653 (comment), or splitting |
Thanks for helping point where to start! I also noticed some relevant TODOs in 'node_worker' that would get resolved by more stable apis for this. |
@rubys another person we should loop into discussions/team about use cases/testing/api for using Node.js as a shared library. |
First observation: we should plan to move to having Second, I would suggest that one of the goals be to allow electron to be built using exclusively NAPI interfaces. See electron/atom/app/node_main.cc. This means that in addition to Create and Destroy environments, there would need to be an interface to execute a script in an environment, and to evaluate an expression in that environment. |
Like - making the |
@empyrical today if you do the following on Mac or Linux:
You end up with But again, we would either need to include these libraries in the existing releases or have separate releases. |
Oh - I misunderstood. I thought you meant only building |
@empyrical that's actually what
|
Just two quick things to note:
|
Curious for some thoughts with regards to And should the "main thread" only be allowed to be made in the process' main thread? JS code that checks Maybe there should be a NAPI function for creating a "main" env, and then a different one for subsequent ones? Basically: // Any more than one invocation per process would result in an error napi_status
NAPI_EXTERN napi_status napi_create_main_env(int* argc, const char** argv, napi_env* env);
// Parent env should also show up as parentPort on worker_threads
NAPI_EXTERN napi_status napi_create_env(napi_env parent_env, napi_env* env); |
Why not? |
IMO:
|
I'm clearly not understanding the downside. How is a 4M executable better than a 9k executable plus a 4M libnode? Alternatives:
|
downsides are mostly on unforeseen consumability issues at the end-user: for example user needing to explicitly set |
@gireeshpunathil others seem to manage without these problems; but in any case, what alternative would you suggest? |
I don't know. In most of my interactions with embedded users in nodejs/help repo, I see they build from source - not because they don't have a libnode, but because each one of them wanted to embed node at different levels of abstractions - 2 node::Init, 3 node::Start, create re-use Once we have normalized these into one or two or three discrete entry points, we could expose (only) those that leads to improved consumption of libnode; and that should help us take an easy decision. One obvious route is to release regular (exe) and libnode separately, against a specified version. |
I agree, that is probably the best way forward. Dynamic linking can be pretty painful when copying executable files around (which even our own test suite does on a regular basis). |
I've created a demo of how this could work. |
This discussion related to nodejs/Release#341 as well. If we had a Development kit and a Deployment kit (or equivalent) then we could add a shared library in addition to the existing exe without concern over the additional size. |
I think that the shared library stuff is worth an issue of its own, imo! sadly some questions i had about what the n-api could look like got buried by this talk. (I can edit in a link to the top level issue if one exists) edit: link to the issue: #24028 |
I think people value the "single file" node binary approach. Being able to move the node executable around by itself has benefit (IMO), and for a lot of use-cases disk space is cheap, so that's less of an issue.
Sounds like a win-win to me (hopefully not too much extra pain for CI / build). |
Going to close this for now and remake this issue when I've got time to try and implement the N-API stuff. I made a new discussion for the shared library stuff here for those interested: #24028 |
ping @nodejs/n-api, would you consider adding this to your backlog? |
@joyeecheung One of the first design decisions of MetaCall was to avoid technical debt as much as possible. So I could figure out how to solve the problem without patching any line of NodeJS code. I tried this in 8.x and 10.x and it works like a charm. I did not tried 12.x (if you did any change it would be interesting to check it out).
With this methodology it is possible to access the internals that are aren't exposed from embedding API, without touching any single code of NodeJS. This model turns out the N-API from extension into embedding API, inverting the model and allowing to call single functions in asynchronous manner (the event loop is also inverted). Although it seems hacky, it has been tested on high performance FaaS and it works properly. It may encounter limitations over the fork model but they are also mitigated as I explained before. |
Consider the case that the node.js is built as a shared library (using clang-cl or msvc), and use it in msvc/gcc, eg., in node.js + Cef. Because of the ABI incompatibility, one has to use the N-API instead of NAN/v8 to add global variables into the isolate. Since the program is not an extension, but an embedder, the program needs a napi_env to access the environment of the node.js. Apparently, the current N-API has no way to achieve this goal, but we can just add a function named napi_create_env, just as @empyrical said.
In this way it is the embedder's responsibility to make sure there exists one isolate in this thread. This is possible in CEF since there exists one callback with signature |
If those contributing to the discussion wanted to put together:
That might be a good way to move the conversation forward. |
I compel you to fully open source Node.js occur rather when every dependency requires it.
"...more requests/cases where it[ otherwise] blocks adoption." - I say this exclusive nature of adoption and abstraction (and dependencies) has on the other hand opened node.js "internal" contributors to retribution for certain damages. Much less is the impetus to extend
I wonder if the node.js tool was to create an industry of serverless salaries instead of add utility (1/hour). |
I have #43542 which has a completely independent partial implementation of this feature. One big thing that is missing is the ability to drain the pending async callbacks and then to keep using the created environment - your But I consider it out of scope for the moment. Also I see an API call for creating an environment out of a |
Another thing that may be possible without the API is switching the thread that calls V8 - for those using it with fibers/green threads - but this can be added later if it is deemed necessary. What is important at the moment is that nothing is missing from |
@empyrical @rubys @viferga @darabi @kohillyang https://github.com/mmomtchev/libnode |
Wow that's really cool. Would that make it easier to embed nodejs in a larger Android/iOS application? |
@CMCDragonkai It should make it easier to use from any C/C++ application - if you are willing to try building it on a mobile device, I will be glad to hear from you - normally nothing of this PR should be platform-dependant, but at the moment only Linux (only Ubuntu to be more precise) has been thoroughly tested and used. |
From my experimentation mentioned in #43542 (comment) and the earlier suggestion that we should put together:
The first use cases would be:
|
@mmomtchev Wouldn't you be interested in joining forces? I have solved most of the problems you have without having embedding API, MetaCall supports now from NodeJS v10 to v18 (we used to support v8.x too but I have decided to drop the support in favor of safety and losing a bit of performance). What @mhdawson mentioned is already supported by MetaCall too. And respect to what @CMCDragonkai said, MetaCall has also been tested in iOS and Android but the current build binaries are not published for those platforms yet (although it has been tested there). Here's an old example (now there's no need for Python2.7 anymore in order to build Node, and Debian is distributing NodeJS with libnode as compiled library, so it does not require building NodeJS as shared library, but the rest should work): https://github.com/metacall/embedding-nodejs-example Another good thing of MetaCall is that I do not touch a single line of NodeJS code, it should work as it is. It was one of the main design decisions because I do not want to maintain a port of NodeJS for embedding. We support invoking functions and async functions, and we are implementing support for creating classes and objects from C/C++ side too: metacall/core#343 It also has extra features that improve embedding capabilities which you will hit eventually if you embed NodeJS at some point, related to the threading model etc. |
@viferga Your objectives are very different than mine - in fact MetaCall should be a layer above NAPI embedding. My objectives for NAPI embedding were:
The problem with the |
@mmomtchev most of the objectives are the same..., the only difference is that we offer a simpler API and a library on top of it. We have achieved to properly embed node only with Debian libnode and N-API. It has been very costly but it works, that's what I mean. You can check our implementation if you want to know how we did it. Also it is explained in this post the approach we followed to properly embed it with the current limitations of node. |
There has been no activity on this feature request for 5 months and it is unlikely to be implemented. It will be closed 6 months after the last non-automated comment. For more information on how the project manages feature requests, please consult the feature request management document. |
The issue is not stale and the PR is up-to-date |
In the last few weeks I have made some progress on addressing this issue. There are still a few TODOs listed in the PR's description, but I would like to ask participants of this discussion for the early feedback. While nitpicking is welcome, I am the most interested in your scenarios. So far the design is being actively discussed with @jasongin, one of the original creators of Node-API, and many TODOs are based on his feedback. One of our core scenarios is to use the new API from the node-api-dotnet. It must enable use of libnode in .Net based server or client apps. The PR has a relatively long description, all new APIs are documented, and there are several unit tests that exercise the APIs. |
Is your feature request related to a problem? Please describe.
Right now there isn't a documented/stable way to use Node as a shared library inside of an application. Were one to be made using N-API, this would open up using Chakra in addition to V8 in an application.
Describe the solution you'd like
I would like for there to be stable APIs in
node_api.h
for creating/managing a Node environment.A function that does this could hypothetically look like:
The embedder could get this environment's libuv loop using
napi_get_uv_event_loop
. But I would also like to have open the possibility of providing my own libuv loop that I have control over to help integrate with other event loops (e.g. Qt's event loop). This could look like:Keeping the event loop going (using
uv_run
on the env's loop) would then be the embedder's responsibility.Also, right now methods like
node::CreateEnvironment
seem to always jump into a REPL, unless you provide a string to evaluate or a file to run. Tweaks to help make this nicer to use for embedding will have to be made.These APIs are just hypothetical, and will probably change when an actual attempt to implement them is made.
I am up to trying to implement this, but I would like to see what kind of discussion happens first and what other ideas people have before I start.
Implementation Progress
napi_run_script
)worker_threads
.Describe alternatives you've considered
I've tried using the unstable APIs, and they aren't fun to keep up with 😅
For discussions on how the shared library can be distributed, see this issue: #24028
The text was updated successfully, but these errors were encountered: