fix(java): keep a language server JDK that ran usable while it is unchanged (#2679)

A JDK whose java -version succeeded was probed again every 10 seconds; on a busy machine one probe could time out and the working JDK was reported as "cannot run on this machine". A successful probe now stays valid while the executable's size, modification time and permissions are unchanged; failed probes are still retried after 10 seconds, and a java that hangs on its first probe is still rejected.

Fixes #2470
This commit is contained in:
l0ng-ai
2026-11-11 15:50:10 +08:00
committed by GitHub
parent 4a0efbae25
commit dc4e8403de
+31 -1
View File
@@ -64,7 +64,10 @@ pub(crate) fn java_runs(java: &Path) -> bool {
&& probe.modified == modified
&& probe.len == metadata.len()
&& probe.permissions == metadata.permissions()
&& probe.at.elapsed() < RESCAN_AFTER
// An executable that ran keeps running while it is unchanged; probing it again
// on a busy machine (jdtls indexing, an antivirus scan) can time out and turn
// a working JDK into "cannot run" until the next probe.
&& (probe.runnable || probe.at.elapsed() < RESCAN_AFTER)
{
return probe.runnable;
}
@@ -923,6 +926,33 @@ mod tests {
assert_eq!(cfg.launch_error.as_deref(), Some("invalid selected JDK /outside/scan"));
}
#[cfg(unix)]
#[test]
fn a_java_that_ran_is_not_probed_again_while_unchanged() {
let tmp = tempfile::tempdir().unwrap();
let jdk = test_jdk(tmp.path(), 25, "busy25");
let java = java_executable(&jdk.home);
let busy = tmp.path().join("busy");
let probes = tmp.path().join("probes");
// Times out once `busy` exists, as `java -version` can on a loaded machine.
std::fs::write(
&java,
format!(
"#!/bin/sh\necho >> '{}'\n[ -f '{}' ] && exec /bin/sleep 10\nexit 0\n",
probes.display(),
busy.display()
),
)
.unwrap();
assert!(java_runs(&java));
std::fs::write(&busy, "").unwrap();
let long_ago = Instant::now().checked_sub(RESCAN_AFTER * 2).unwrap();
JAVA_PROBES.lock().unwrap().get_mut(&java).unwrap().at = long_ago;
assert!(java_runs(&java), "a JDK that ran stays usable");
assert!(java_execution_error(&java).is_none());
assert_eq!(std::fs::read_to_string(&probes).unwrap().lines().count(), 1, "probed once");
}
#[cfg(unix)]
#[test]
fn slow_java_probe_does_not_hold_the_cache_lock() {