-
Notifications
You must be signed in to change notification settings - Fork 30.9k
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
Ability to replace current Node process with another #21664
Comments
@isiahmeadows - great explanation, thank you. How do you compare this with:
|
@gireeshpunathil In reality, it's supposed to use/emulate POSIX's If you look at Liftoff's readme, that should explain best what this primarily targets: loader scripts that need zero state of their own after the process is respawned with the new arguments. If you want a cohesive idea of what using this would look like for a relaunch script, check out Babel's babel-node script, which is such a relaunch script that reloads the sibling For a concrete example, this entire subsection would collapse to just this. |
makes sense to me! but would love to hear from @nodejs/child_process |
Just to talk about Windows for a minute, Note though that Windows doesn't support Documentation for the https://msdn.microsoft.com/en-us/library/431x4c1w.aspx and you will find documentation for HTH |
@HPaulS But |
Process B starts, process A immediately quits, same difference. It's the best you can do on Windows. Simulating 'it' would be hard. I would avoid it unless there is some vital reason to do so, and I don't see one here. Of course, I have to qualify all this by saying that I don't know what a node.js process actually is. I assume, from what you say, that it's a true process, in the sense that the underlying OS understands it. If so (and even if not, I guess) I can see some value in having a way to say to it 'reset everything to your initial, default state'. Exactly how you might do that is another issue. Killing the process off and starting a new one is probably easiest since it would clean up all the resources currently in use and get rid of any saved state. And on Unix, yes, there's a handy 'do all that in place' system call, lucky Unix, but that, in and of itself, is not important. I don't know enough about node.js to comment further, I feel like I'm weeding in your back garden here Isiah. PS: Starting a new process on Windows (and then immediately exiting the old one, by whatever means) will not keep the same process ID. Is this an issue? If it is then you will indeed need to find an alternative solution which will have to be some sort of 'reset everything' call into node.js itself. |
A Node.js process is just that, a true, heavy OS-level process, with all the baggage that surrounds one. The goal of this request is to provide a way to basically kill off this process and delegate as much as possible to that replacement process. The standard way to do it on Linux/Unix is via Hopefully this explains what I'm going for. |
To me, the only important question here is whether the spawned process has to have the same process ID as the parent process. Does it? Everything else is easily doable via Note: the |
Fair enough, so that part could be left out. (I didn't know that.) But anyways, the main goal of this feature request is to replace this mess with something that leverages native and runtime support to be way more memory-efficient. |
OK, thanks. I myself had to check that. So I saw this: https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback But we seem to need another version that never returns, implementation details at the discretion of the node.js team. Don't you think? [Edit] Pesky malfunctioning T key :( |
@gireeshpunathil So, have you gotten feedback from any of them yet? |
Why is retaining the process ID critical, other than that it imitates the behavior of
|
Basically, I'd like to see Liftoff and friends be able to use core functionality to do their thing most efficiently, instead of relying on either a native extension or creating a second heavy process when it's not always necessary.
Retaining the process ID is not critical - it's just part of the Basically, what I'm wanting is the ability to "tail call" into another process, with a little bit of runtime assistance to optimize that better when it can (like in Unix). Does that help? |
Similarly, I also would love to see something similar to I'd like to add that retaining the process ID is critical for these uses - shells calling |
Compare |
How about throwing if there are worker threads? |
This feature would prove useful for nodejs/corepack |
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. |
Non-automated comment. |
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. |
If no one is going to work on it (seems likely since it's been open since 2018), it's better to just let it die off peacefully. Bump comments just end up spamming people's inboxes. The project still accepts pull requests, even if the issue is closed. |
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. |
It's been almost six years without movement so I'm putting this one out to pasture. |
Edit: If someone can come up with a better shim for
execve
for Windows, that'd be far better. The form below is very expensive and very horrible.Edit 2: Linked relevant SO question.
Edit 3: Clarify FS changes
Edit 4: Here's the text from that SO question as of July 6, 2018 (so you don't have to search for it), where I asked about how to do the Windows part.
Click to show (warning: lots of text)
So, in a feature request I filed against Node.js, I was looking for a way to replace the current Node process with another. In Linux and friends (really, any POSIX-compliant system), this is easy: use
execve
and friends and call it a day. But obviously, that won't work on Windows, since it only hasCreateProcess
(whichexecve
and friends delegate to, complete with async behavior). And it's not like people haven't wanted to do similar, leading to numerous duplicate questions on this site. (This isn't a duplicate because it's explicitly seeking a workaround given certain constraints, not just asking for direct replacement.)Process replacement has several facets that have to addressed:
And for my particular case, there are a few constraints:
malloc
calls, handles, thread manipulation, or process manipulation to track and free them all, since DLL rewriting isn't exactly practical.call
s andpush
es is far from practical, and would just be all-around slow for obvious reasons.So, here's the gist of what I was thinking: use something similar to a pseudo-trampoline.
MAX_PATH + 1
chars for the application path +'\0'
.MAX_PATH + 1
chars for the current working directory path +'\0'
.'\0'
.'\0'
.The idea here is to use a process-based trampoline and drop the current process size to an absolute minimum while the newly created one is started.
But where I'm not very familiar with Windows, I probably made quite a few mistakes here. Also, the above seems extremely inefficient and to an extent it just feels horribly wrong for something a kernel could just release a few memory pages, deallocate a bunch of memory handles, and move some memory around for the next process.
So, to summarize, what's the ideal way to emulate process replacement on Windows with the fewest limitations?
I would like a means to "replace" the current Node process with another, keeping the same process ID. It would be something morally similar to this function, but it wouldn't return. This would be most useful for conditionally replacing Node flags in a startup script - for example, if someone wants to enable modules and your behavior needs to change non-trivially in the presence of them (like if you need to install a default loader), you'll want to respawn the process with
--experimental-modules --loader <file>
so you can install the loader.This is also for scenarios when you want to run a module as a
main
module. If you want to do logic after the process ends, you should be usingchild_process.spawn
regardless - you shouldn't be attempting to "replace" it in any capacity.Here's what I propose:
child_process.replaceSpawn(command [ , args] [ , options ])
command
is the path to the new command.args
is the args to replace the arguments with. This defaults to the empty array.options
is for the various options for replacing the process. This defaults to an empty object.options.cwd
is the new cwd to use. (Default:process.cwd()
)options.env
is the new environment to use. (Default:process.env
)options.argv0
is the binary to spawn as. (Default:command
)child_process.replaceFork(mainPath [ , args] [ , options ])
works similarly to above.mainPath
is the path to the newrequire.main
.options.execPath
is the new binary to spawn as. (Default:process.execPath
)options.execArgv
are the new Node flags to spawn with. (Default:process.execArgv
)options.argv0
is the binary to spawn as. (Default:process.argv0
)Add a
napi_terminating
member fornapi_status
to representtry_catch.HasTerminated()
and the result of each call after replacement termination.Add a
napi_set_terminate_hook(napi_env env, void (*fun)(void*), void* data)
function to register a callback called on termination, to make it easier to clean up resources.Internally, there are two cases you need to cover, and the simulated part for Windows is where it gets really hairy due to all the edge cases. Here's pseudocode for the basic algorithm (I'm not really familiar with Node internals, so take this as a rough guideline):
execve
or equivalent with the new process path, arguments, and environment.v8::V8::TerminateExecution()
. All N-API callbacks should returnnapi_terminated
during this step.In addition, file system requests will have to generally create each file descriptor with
O_CLOEXEC
.As for precedent where this could be used immediately:
kexec
where available, which is a POSIX-only module that replaces the process literally. Absent that, it falls back to its own implementation that works like the other two examples.The text was updated successfully, but these errors were encountered: