Skip to content
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

[Bug] 构建版在启动器退出时桌宠也异常退出的调查报告 #73

Open
isHarryh opened this issue Aug 22, 2024 · 2 comments
Open
Labels
Bug Something isn't working

Comments

@isHarryh
Copy link
Owner

isHarryh commented Aug 22, 2024

背景

@half-nothing 试图为 ArkPets 的构建添加 Actions 时发现,Actions 远端构建的 EXE 存在一个问题:当启动器退出时,由该启动器启动的正在运行的桌宠本体也会一起终止运行(预期表现应该是桌宠本体继续运行)。值得注意的是,我们本地构建的 EXE 没有发生此问题。

与此 Actions 相关的 ArkPets Commit 如下:
83e4f66#diff-5c3fa597431eda03ac3339ae6bf7f05e1a50d6fc7333679ec38e21b337cb6721

调查过程

@half-nothing 于 2024 年 4 月发现,出现问题的 ArkPets 进程被附加了 KILL_ON_JOB_CLOSE 工作限制:
kill_on_job_close

@isHarryh 于 2024 年 8 月确认,该问题是由于 JDK 17.0.10 针对 jpackage 的关于 8301247 的修复引起的。与此问题相关的 JDK Commit 如下:
openjdk/jdk17u-dev@179c60f#diff-7ab0168a7e08937033bbddd525ffc98601753348e290579ececf6c0717118c4eR166-R168

解决建议

为了解决这一问题,建议所有构建环境的 JDK 版本不得超过 17.0.9。有条件的话,可以研究可以避免 KILL_ON_JOB_CLOSE 工作限制触发的方法。

有关 JDK 的更新日志,参见 Consolidated JDK 17 Release Notes

@isHarryh isHarryh added the Bug Something isn't working label Aug 22, 2024
@half-nothing
Copy link
Contributor

half-nothing commented Aug 30, 2024

源码研究

经过对openjdk17关于jpackage windows平台源码的详细研究后

...
const tstring launcherPath = SysInfo::getProcessModulePath();
const tstring appImageRoot = FileUtils::dirname(launcherPath);
const tstring appDirPath = FileUtils::mkpath() << appImageRoot << _T("app");
const AppLauncher appLauncher = AppLauncher().setImageRoot(appImageRoot)
    .addJvmLibName(_T("bin\\jli.dll"))
    .setAppDir(appDirPath)
    .setLibEnvVariableName(_T("PATH"))
    .setDefaultRuntimePath(FileUtils::mkpath() << appImageRoot << _T("runtime"));
const bool restart = !appLauncher.libEnvVariableContainsAppDir();
std::unique_ptr<Jvm> jvm(appLauncher.createJvmLauncher());
if (restart) {
    ...
    jobInfo.BasicLimitInformation.LimitFlags =
            JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE | JOB_OBJECT_LIMIT_SILENT_BREAKAWAY_OK;
    ...
}
...

可以发现,控制进程是否被附加 KILL_ON_JOB_CLOSE 工作限制的是一个bool类型的变量
他的值为appLauncher.libEnvVariableContainsAppDir()的返回值取反
libEnvVariableContainsAppDir()函数定义如下

bool AppLauncher::libEnvVariableContainsAppDir() const {
    tstring value = SysInfo::getEnvVariable(std::nothrow,
            libEnvVarName, tstring());
#ifdef _WIN32
    value = tstrings::toLower(value);
#endif
    const tstring_array tokens = tstrings::split(value,
            tstring(1, FileUtils::pathSeparator));
    return tokens.end() != std::find(tokens.begin(), tokens.end(),
#ifdef _WIN32
        tstrings::toLower(appDirPath)
#else
        appDirPath
#endif
    );
}

可以看到,这个函数的功能实际上是判断给定的环境变量libEnvVarName里面有没有appDirPath
libEnvVarName通过.setLibEnvVariableName(_T("PATH"))调用设置为PATH,即环境变量PATH
appDirPath则是app文件夹所在的目录
此外,在AppLauncher::createJvmLauncher()函数中,发现JVM启动时本身也会将appDirPath添加到libEnvVarName末尾

Jvm* AppLauncher::createJvmLauncher() const {
    ...
    if (!libEnvVariableContainsAppDir()) {
        (*jvm).addEnvVariable(libEnvVarName, SysInfo::getEnvVariable(
                std::nothrow, libEnvVarName)
                + FileUtils::pathSeparator
                + appDirPath);
    }
    ...
}

总结

绕过触发工作限制的方法为:
JVM启动以前,向PATH环境变量添加appDirPath,从而使jpackage绕过添加限制的分支

实现

因为检查环境变量以及设置工作限制都发生在JVM启动以前,所以无法用JAVA语言自身实现jar包的常规手段解决,可以考虑两种解决方法:

  1. 在exe安装的时候自动添加环境变量
  2. 使用别的语言外挂完成
    2. 使用额外的jar包完成这一工作
    3. jar包设置完成后自动重启

现在暂时在windows平台下,使用方案一进行验证,效果良好,在openjdk version "17.0.10" 2024-01-16 LTS下编译,验证该bug已被修复,更多java版本需要进一步验证

2024.10.19 更新

由于JVM本身启动的时候会将appDirPath添加到libEnvVarName末尾,即JVM运行时,libEnvVarName总会存在appDirPath
也就是说明,jar包无法靠自己判断libEnvVarName中存在的appDirPath是JVM添加的,还是我们手动添加的
再广泛一点,即我们无法使用JAVA或者任何依赖于JVM运行的编程语言完成这种操作

@isHarryh
Copy link
Owner Author

isHarryh commented Oct 7, 2024

#74 采用“在exe安装的时候自动添加环境变量”这一解决方法,确实解决了 EXE 构建版的问题。但是,ZIP 构建版仍会存在此问题。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants