fix(kotlin): Kill Gradle daemons on KLS shutdown#1082
fix(kotlin): Kill Gradle daemons on KLS shutdown#1082mareurs wants to merge 2 commits intooraios:mainfrom
Conversation
KLS triggers Gradle during project indexing, which spawns persistent daemon processes (~500MB RSS each, 3h idle timeout). These daemons detach from KLS (reparented to init) and are invisible to normal process-tree cleanup via psutil.Process.children(recursive=True). Override stop() to find and terminate Gradle daemons matching this KLS instance's JAVA_HOME after normal shutdown. Identification is done by matching the java binary in the daemon's cmdline against JAVA_HOME/bin/java. Closes oraios#1081 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| if java_home: | ||
| self._kill_gradle_daemons(java_home) | ||
|
|
||
| def _get_java_home_from_dependency_provider(self) -> str | None: |
There was a problem hiding this comment.
neither this nor the None check for java_home above seem necessary
There was a problem hiding this comment.
Agreed. On main, _dependency_provider is always DependencyProvider and _java_home_path is always set after _get_or_install_core_dependency(). Will simplify — access the attribute directly without the isinstance/None guards.
| the JAVA_HOME used by this KLS instance. | ||
| """ | ||
| java_bin = os.path.join(java_home, "bin", "java") | ||
| try: |
There was a problem hiding this comment.
this also seems too defensive. When could realpath go wrong?
There was a problem hiding this comment.
Correct, os.path.realpath() doesn't raise — it returns the input unchanged if resolution fails. Will remove both try/except blocks.
Isn't this completely f*d up? |
| def stop(self, shutdown_timeout: float = 2.0) -> None: | ||
| java_home = self._get_java_home_from_dependency_provider() | ||
| super().stop(shutdown_timeout=shutdown_timeout) | ||
| if java_home: |
There was a problem hiding this comment.
We can't do that in general once (if) we allow users to use their system java. The only safe way to kill the gradle daemons is to know they correspond to Serena's own java. So we will need to wait for #1079 to be merged and then to distinguish if we're using Serena's java or another one. In the advanced config docs we can then tell users that they might need to clean zombies by themselves if they enable using system java.
Meanwhile, I will raise an issue in the KLS repo and ask what's up with all of this
There was a problem hiding this comment.
We should probably close #1079 instead. Being able to reliably terminate the LS is more important than reusing a system JRE. I can't think of a good reason why using the system JRE would be important.
There was a problem hiding this comment.
Valid concern. With the bundled JRE, the JAVA_HOME/bin/java path is unique to this KLS instance, so matching is safe. With a system JRE, we'd potentially match unrelated Gradle daemons — that's dangerous.
For now this PR only targets the current behavior (bundled JRE). If #1079 is merged in the future and system JRE becomes possible, we'd need to either skip daemon cleanup for system JRE or use additional matching criteria (e.g. the project directory from /proc/PID/environ).
Separately, our investigation confirmed that the Gradle Tooling API (used by JetBrains KLS internally) always spawns a daemon — there's no --no-daemon equivalent. So post-hoc cleanup is the only option.
Yes — it's a fundamental constraint of the Gradle Tooling API. The API mandates daemon usage; there's no |
- Remove _get_java_home_from_dependency_provider() indirection; use assert + direct access instead (dependency_provider and java_home_path are always set for KLS) - Remove try/except OSError around os.path.realpath() calls — realpath never raises, it returns the path unchanged on failure Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
psutil.Process.children(recursive=True)cleanupstop()inKotlinLanguageServerto find and terminate matching Gradle daemons after normal shutdowncmdline[0]againstJAVA_HOME/bin/javafrom this KLS instanceRoot cause investigation
Investigation performed in #1079 (discussion thread) confirmed:
JAVA_HOMEpoints to~/.serena/language_servers/static/KotlinLanguageServer/...)/proc/PID/environand controlled before/after test runsTest results
Full test suite run with
pytest -n auto(32 workers):Gradle daemon before/after verification
pytest -m kotlinCloses #1081
🤖 Generated with Claude Code