RFR: 8366736: Closed System.out causes child process to hang on Windows
Roger Riggs
rriggs at openjdk.org
Tue Jan 13 21:47:01 UTC 2026
On Tue, 13 Jan 2026 16:52:36 GMT, Ioi Lam <iklam at openjdk.org> wrote:
> The bug is here on line 121:
>
> https://github.com/openjdk/jdk/blob/586846b84a38d285c5905437e903cfc57f609410/src/java.base/windows/classes/java/lang/ProcessImpl.java#L118-L121
>
> If `System.out` has been closed, `fdAccess.getHandle()` will return -1. This causes `stdHandles[1]` to have the same value as if the child process's stdout was redirected with `Redirect.PIPE`. This will cause a Pipe to be created here for the child process's STDOUT on line 168:
>
> https://github.com/openjdk/jdk/blob/586846b84a38d285c5905437e903cfc57f609410/src/java.base/windows/native/libjava/ProcessImpl_md.c#L158-L184
>
> However, the caller of the `ProcessBuilder` is not aware of this and will not drain this pipe. This causes the child process to get stuck when writing to its stdout when the pipe 's buffer is filled up.
>
> The fix is to treat the redirection as `Redirect.DISCARD` when `System.out` and/or `System.err` have been closed.
src/java.base/windows/classes/java/lang/ProcessImpl.java line 118:
> 116: }
> 117:
> 118: Redirect file1 = null;
Remove
src/java.base/windows/classes/java/lang/ProcessImpl.java line 125:
> 123: if (stdHandles[1] == -1L) {
> 124: // FileDescriptor.out has been closed.
> 125: file1 = Redirect.DISCARD;
Suggestion:
// FileDescriptor.out has been closed.
f1 = newFileOutputStream(Redirect.DISCARD.file(),
Redirect.DISCARD.append());
stdHandles[1] = fdAccess.getHandle(f1.getFD());
Inline the change and avoid extra local state variable
src/java.base/windows/classes/java/lang/ProcessImpl.java line 137:
> 135: if (file1 != null) {
> 136: f1 = newFileOutputStream(file1.file(),
> 137: file1.append());
Revert to original.
src/java.base/windows/classes/java/lang/ProcessImpl.java line 141:
> 139: }
> 140:
> 141: Redirect file2 = null;
Remove
src/java.base/windows/classes/java/lang/ProcessImpl.java line 148:
> 146: if (stdHandles[2] == -1L) {
> 147: // FileDescriptor.err has been closed.
> 148: file2 = Redirect.DISCARD;
Suggestion:
// FileDescriptor.err has been closed.
f2 = newFileOutputStream(Redirect.DISCARD.file(),
Redirect.DISCARD.append());
stdHandles[2] = fdAccess.getHandle(f2.getFD());
Inline the change to discard stderr.
src/java.base/windows/classes/java/lang/ProcessImpl.java line 157:
> 155: if (file2 != null) {
> 156: f2 = newFileOutputStream(file2.file(),
> 157: file2.append());
Revert to original.
test/jdk/java/lang/ProcessBuilder/InheritIOClosed.java line 52:
> 50: "1234567890" +
> 51: "1234567890" +
> 52: "1234567890";
Suggestion:
private final static Path JAVA_EXE =
Path.of(System.getProperty("java.home"), "bin", "java");
private static final String s = "1234567890".repeat(10);
Path is more compact.
test/jdk/java/lang/ProcessBuilder/InheritIOClosed.java line 65:
> 63:
> 64: ProcessBuilder pb = new ProcessBuilder().inheritIO()
> 65: .command(JAVA_EXE,
Suggestion:
.command(JAVA_EXE.toString(),
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687961657
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687960154
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687961222
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687965150
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687964194
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687964792
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687982258
PR Review Comment: https://git.openjdk.org/jdk/pull/29198#discussion_r2687984357
More information about the core-libs-dev
mailing list