Merge 449a6f1386002a38c34422098002e950928f706a into 9969dc60c5278340b6b9a4d7facdde620e99d1f5

This commit is contained in:
Burning_TNT 2025-08-02 23:00:10 +08:00 committed by GitHub
commit 33b54b39bb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 60 additions and 5 deletions

View File

@ -25,6 +25,7 @@ import org.jackhuang.hmcl.util.Log4jLevel;
import org.jackhuang.hmcl.util.StringUtils; import org.jackhuang.hmcl.util.StringUtils;
import org.jackhuang.hmcl.util.platform.ManagedProcess; import org.jackhuang.hmcl.util.platform.ManagedProcess;
import org.jackhuang.hmcl.util.platform.OperatingSystem; import org.jackhuang.hmcl.util.platform.OperatingSystem;
import org.jackhuang.hmcl.util.platform.SystemUtils;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
@ -54,7 +55,7 @@ final class ExitWaiter implements Runnable {
@Override @Override
public void run() { public void run() {
try { try {
int exitCode = process.getProcess().waitFor(); int exitCode = SystemUtils.waitFor(process.getProcess());
for (Thread thread : joins) for (Thread thread : joins)
thread.join(); thread.join();

View File

@ -205,7 +205,7 @@ public enum Architecture {
if (CURRENT_ARCH == X86_64) { if (CURRENT_ARCH == X86_64) {
try { try {
Process process = Runtime.getRuntime().exec(new String[]{"/usr/sbin/sysctl", "-n", "sysctl.proc_translated"}); Process process = Runtime.getRuntime().exec(new String[]{"/usr/sbin/sysctl", "-n", "sysctl.proc_translated"});
if (process.waitFor(3, TimeUnit.SECONDS) && process.exitValue() == 0 if (SystemUtils.waitFor(process, 3, TimeUnit.SECONDS) && process.exitValue() == 0
&& "1".equals(IOUtils.readFullyAsString(process.getInputStream(), OperatingSystem.NATIVE_CHARSET).trim())) { && "1".equals(IOUtils.readFullyAsString(process.getInputStream(), OperatingSystem.NATIVE_CHARSET).trim())) {
sysArch = ARM64; sysArch = ARM64;
} }
@ -221,7 +221,7 @@ public enum Architecture {
if (new File(uname).exists()) { if (new File(uname).exists()) {
try { try {
Process process = Runtime.getRuntime().exec(new String[]{uname, "-m"}); Process process = Runtime.getRuntime().exec(new String[]{uname, "-m"});
if (process.waitFor(3, TimeUnit.SECONDS) && process.exitValue() == 0) { if (SystemUtils.waitFor(process, 3, TimeUnit.SECONDS) && process.exitValue() == 0) {
sysArch = parseArchName(IOUtils.readFullyAsString(process.getInputStream(), OperatingSystem.NATIVE_CHARSET).trim()); sysArch = parseArchName(IOUtils.readFullyAsString(process.getInputStream(), OperatingSystem.NATIVE_CHARSET).trim());
} }
} catch (Throwable e) { } catch (Throwable e) {

View File

@ -72,7 +72,7 @@ public final class SystemUtils {
ManagedProcess managedProcess = new ManagedProcess(processBuilder); ManagedProcess managedProcess = new ManagedProcess(processBuilder);
managedProcess.pumpInputStream(SystemUtils::onLogLine); managedProcess.pumpInputStream(SystemUtils::onLogLine);
managedProcess.pumpErrorStream(SystemUtils::onLogLine); managedProcess.pumpErrorStream(SystemUtils::onLogLine);
return managedProcess.getProcess().waitFor(); return waitFor(managedProcess.getProcess());
} }
public static String run(String... command) throws Exception { public static String run(String... command) throws Exception {
@ -95,7 +95,7 @@ public final class SystemUtils {
Lang.wrap(() -> convert.apply(inputStream)), Lang.wrap(() -> convert.apply(inputStream)),
Schedulers.io()); Schedulers.io());
if (!process.waitFor(15, TimeUnit.SECONDS)) if (!SystemUtils.waitFor(process, 15, TimeUnit.SECONDS))
throw new TimeoutException(); throw new TimeoutException();
if (process.exitValue() != 0) if (process.exitValue() != 0)
@ -115,4 +115,58 @@ public final class SystemUtils {
private static void onLogLine(String log) { private static void onLogLine(String log) {
LOG.info(log); LOG.info(log);
} }
/**
* <p>A low performance implementation of {@link Process#waitFor()}</p>
*
* <p>
* On Windows, JVM invoke
* <a href="https://github.com/openjdk/jdk/blob/b77bd5fd6a6f7ddbed90300fba790da4fb683275/src/java.base/windows/native/libjava/ProcessImpl_md.c#L428-L459">WaitForMultipleObjects</a>
* to wait the specific process and VM thread interrupt flag at the same time.
* However, this API might throw unexpected exception, causing a IOException on Java level.
* </p>
*
* <p>
* The following codes replace native implementation with software ones.
* </p>
*/
public static int waitFor(Process process) throws InterruptedException {
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS) {
return process.waitFor();
}
while (true) {
try {
return process.exitValue();
} catch (IllegalThreadStateException e2) {
// noinspection BusyWait
Thread.sleep(1000);
}
}
}
/**
* See {@link #waitFor(Process)}
*/
public static boolean waitFor(Process process, long timeout, TimeUnit unit) throws InterruptedException {
if (OperatingSystem.CURRENT_OS != OperatingSystem.WINDOWS) {
return process.waitFor(timeout, unit);
}
long startTime = System.nanoTime();
long rem = unit.toNanos(timeout);
do {
try {
process.exitValue();
return true;
} catch (IllegalThreadStateException ex) {
if (rem > 0)
Thread.sleep(
Math.min(TimeUnit.NANOSECONDS.toMillis(rem) + 1, 100));
}
rem = unit.toNanos(timeout) - (System.nanoTime() - startTime);
} while (rem > 0);
return false;
}
} }