-
Notifications
You must be signed in to change notification settings - Fork 340
fix: Servers terminating even with 'detached: true' #1603
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
base: main
Are you sure you want to change the base?
Conversation
@microsoft-github-policy-service agree company="Amazon" |
// channel (e.g. IPC) disconnects, the child terminates. | ||
process.on('disconnect', () => {}); | ||
|
||
// Keeps the server alive since node may terminate this process if |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would not hardcode this here. Could we pass that as a argument to the --detach param.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I've added an optional value for --detached NNNN
to set the timeout in milliseconds.
9fd7ee3
to
30aab27
Compare
I've added some more information under the |
Hey @dbaeumer would you be able to give this another look |
We have a couple places we parse cli args that have a value, so a general utility will help deduplicate code Signed-off-by: nkomonen-amazon <[email protected]>
Background Info: - In Unix-like environments, the child process would sometimes not exit even when the parent did. - The exit/watchdog timers were created due to address this behavior. See the docstring of setupExitTimer(). - The 'detached' flag would not be honored in Unix-like environments, and looks to stay open regardless in some cases. Problem: If a language server was set as detached in the ServerOptions, and we wanted it to not exit on parent exit, it would still exit due to the setupExitTimer() Solution: - A server will know that it is detached by the cli process argument: '--detached' - The client side code to inject this is in a following commit - The exit/watchdog timers will not run if the detached flag is true - When the input stream to the server ends due to parent terminating, the server would also. This PR changes it to skip this if detached. Signed-off-by: nkomonen-amazon <[email protected]>
- IMPORTANT: We need the setInterval for detached to work, so that the server does not auto-terminate when the parent does. See the code comment which explains why. - By default if the user specifies for the language server to be detached, it will never quit after the parent/client terminates. But if a number (milliseconds) is provided when launching the server it will timeout after that amount of time, after the client terminates. Signed-off-by: nkomonen-amazon <[email protected]>
All different methods that can support detached (i.e the server will not terminate when the parent does) will now do so. - A new field has been added in for the ForkOptions so that forked servers can now be detached. - Only MessageTransport and StreamInfo will remain the same. I have not looked in to how those would support properly detaching. Signed-off-by: nkomonen-amazon <[email protected]>
Signed-off-by: nkomonen-amazon <[email protected]>
- Allows the client to configure a ServerOptions setting that sets the max lifetime of the detached language server after the parent/client has terminated. - Additionally, this exposes some utils from the language server so that they can be reused in tests. Otherwise we'd have to duplicate code and reduce code coverage Signed-off-by: nkomonen-amazon <[email protected]>
1c57b6a
to
6adb005
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work. Only small things.
@@ -57,10 +57,18 @@ namespace Transport { | |||
} | |||
} | |||
|
|||
export interface ExecutableOptions { | |||
export interface DetachedOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would do this differently. We should define detached as detached: boolean | number
. If it is a boolean
then we should use a default timeout. If it is a number we use it and -1
represent indefinite. This makes it more in line with other timeout settings.
* This function assumes the parent process has been properly configured with | ||
* necessary settings (e.g., using `child_process.unref()`). | ||
*/ | ||
function isDetached() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small things: declare as return a boolean
// the parent terminates + this process has nothing in the event loop. | ||
setInterval(() => { | ||
// Check if the detached server has reached the user-specified lifetime | ||
if (_protocolConnectionEndTime && timeoutMillis && Date.now() - _protocolConnectionEndTime >= timeoutMillis) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although not happening here I usually try to make undefined checks explicit especially if the variable as a or type that can falsify.
_protocolConnectionEndTime !== undefined
@@ -24,6 +24,10 @@ | |||
"./browser": { | |||
"types": "./lib/browser/main.d.ts", | |||
"browser": "./lib/browser/main.js" | |||
}, | |||
"./utils": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this. It will export this from the npm package and will force use to keep it stable as API.
Background Info
Problem
If a language server was set as
detached
in the ServerOptions, and we wanted it to not exit on parent exit, it wouldstill exit.
Solution
Commits in this PR have been split up in to smaller concerns, see their comments for specific changes.
Summary is that when the detached flag is set in the ServerOptions, it will now ensure that the Server actually stays alive when the parent exits.
Testing
console.log('Client PID: ${process.pid}')
detached
flag in theServerOptions
detached: true
, otherwise it should not exist ifdetached: false
Manually tested on:
Behaviors
detached: false
detached: true
&detachedTimeout
is set THEN the server will timeout afterdetachedTimeout
milliseconds have passed since the parent (client) have terminateddetached: true
, a graceful shutdown of the VS Code application results in the Language Server still runningcontext.subscriptions
it will be cleaned up regardless ofdetached
Resolves #1595
Signed-off-by: nkomonen-amazon [email protected]