LSP
LSP copied to clipboard
omnisharp server spawns zombie mono processes
The omnisharp server is able to spawn zombie 'mono' processes. I have caught it doing this twice already after noticing my fan was running hard and discovered multiple hanging mono processes still running. Quitting Sublime Text didn't close the processes. I suspect that LSP is not terminating the language server properly which is causing it to not clean up child processes.
It's possible that omnisharp itself is also the culprit for not handling shutdown system signals properly although LSP needs to take special care that it's terminating gracefully as well.
To Reproduce
I'm not sure how to reproduce the bug right now but I will update the issue when I figure this out.
Screenshots

Environment (please complete the following information):
- OS: macOS 10.15
- Sublime Text version: 4094
- LSP version: 1.2.7
- Language servers used: omnisharp (see the version below in the json config)
Additional context
I configure the server by hijacking from VSCode.
"omnisharp":
{
"command":
[
"/Users/ryanjoseph/.vscode/extensions/ms-dotnettools.csharp-1.23.8/.omnisharp/1.37.5/run",
"-lsp"
],
"env": {
"FrameworkPathOverride": "/Users/ryanjoseph/.vscode/extensions/ms-dotnettools.csharp-1.23.8/.omnisharp/1.37.5/omnisharp/.msbuild/Current"
},
"selector": "source.cs"
}
The problem seems to be restarting the server, i.e. it leaves zombie processes some which use 100% CPU doing something. Can you reproduce this on your system?
Also simply quitting Sublime Text while the omnisharp server is open will leave a zombie mono process running. Either LSP is not cleaning up properly or omnisharp is.
Maybe not a real solution, but can you instead try running the OmniSharp.exe file under mono?
"command": ["mono", "/Users/ryanjoseph/.vscode/extensions/ms-dotnettools.csharp-1.23.8/.omnisharp/1.37.5/path/to/OmniSharp.exe", "-lsp"]
Although, I don't know if a global mono
would work.
Node-based servers (or Microsoft's LSP library rather) have a code that force-kills the server if the parent process dies. So that covers the case when ST is closed and the plugin doesn't get a chance to handle graceful exit. Not that it's very relevant here but just mentioning it as apparently it's a case that was deemed worth handling.
I wonder if using preexec_fn=os.setsid
for the subprocess call would make any difference... Sublime build system uses that and I have it has something to do with parent-child process relationship.
FWIW, there was this conversation on discord related to this. I tried to extract some of the messages that might be useful for this.
https://discord.com/channels/280102180189634562/280157067396775936/732264197429264574
Q: How to kill a process and all processes that that process has created? A: I believe it is different per OS I think on Mac/Linux you need to pass os.setsid to preexec_fn
https://discord.com/channels/280102180189634562/280157067396775936/732281138256543774
...btw subprocess.Popen(args, ..., start_new_session=True) also kills the child processes
https://discord.com/channels/280102180189634562/280157067396775936/732290169176195082
Just be aware speaking of signals doesn’t translate to Windows
Ok so the solution was indeed to use mono with the .exe. I have no idea what the "run" executable is for or why it was leaving zombie processes. I'm not very experienced with C# so I didn't know the .exe (which is normally a Windows executable and won't run on macOS) should be run from mono. What happens now is I get a mono-sgen64
process instead of mono
.
Here is my full command working on macOS 10.15. Maybe update the documentation for macOS? It would be nice to document the need for FrameworkPathOverride but I think this is only needed because of the Visual Studio project which Unity created requires a version of .NET which is not supported (this question address it https://stackoverflow.com/questions/63579846/macos-visualstudiocode-omnisharp-missing-net-framework-4-6-1).
"omnisharp":
{
"command":
[
"mono",
"/Users/ryanjoseph/.vscode/extensions/ms-dotnettools.csharp-1.23.8/.omnisharp/1.37.5/omnisharp/OmniSharp.exe",
"-lsp"
],
"env": {
"FrameworkPathOverride": "/Users/ryanjoseph/.vscode/extensions/ms-dotnettools.csharp-1.23.8/.omnisharp/1.37.5/omnisharp/.msbuild/Current"
},
"selector": "source.cs"
},
Do you want me to close this now? It was basically a user error but there could be something to be added to the documentation.
@rwols already added clearifacation for the omni language server with https://github.com/sublimelsp/LSP/pull/1551🙂
The bug is that we don't kill child processes in all possible cases IMO. The fact that running omnisharp under mono works around this issue is... a workaround :)
I've been dealing with similar problem in typescript-language-server
recently. Quitting ST was leaving zombie tssserver
sub-process of typescript-language-server
running.
Based on what I've learned there, I don't think it's ST's job to enumerate and kill all sub-processes of the language server process (the process that ST is starting itself). It's the job of the language server child processes to handle sudden death (SIGKILL, for example) of the parent process.
In Node.js, whether the processes are communicating with stdio
or node ipc
, there is always a way for the child process to tell the parent process is gone and shut itself down so I'm assuming that translates to any runtime and language.
Closing based on my previous comment.