From 407c6e93e041b7ec87ba6146084139ccc7a3cf52 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Mon, 7 Sep 2026 12:16:38 +0100 Subject: [PATCH] Close the javac pipes and join the drain threads in externalCompile CodeGenUtil.externalCompile never closed any of the three pipes it opened for the compiler process. proc.getOutputStream() (stdin, which javac never reads) was left open, and the BufferedReaders wrapping stdout and stderr in copy() were never closed, so three file descriptors per invocation were held until the Process was collected. The drain threads were also never joined: proc.waitFor() returns as soon as the process exits, which can be before the readers have finished appending, so the compiler diagnostics printed just below could be truncated or empty. The "out" and "err" locals were assigned and then unused. Close stdin up front, join both readers after waitFor(), destroy the process in a finally, and let copy() close its reader when the pipe is drained. Co-Authored-By: Claude Opus 5 (1M context) --- .../xmlbeans/impl/tool/CodeGenUtil.java | 31 ++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/xmlbeans/impl/tool/CodeGenUtil.java b/src/main/java/org/apache/xmlbeans/impl/tool/CodeGenUtil.java index c3db6e08e..80c2264b4 100644 --- a/src/main/java/org/apache/xmlbeans/impl/tool/CodeGenUtil.java +++ b/src/main/java/org/apache/xmlbeans/impl/tool/CodeGenUtil.java @@ -242,10 +242,23 @@ public static boolean externalCompile(List srcFiles, File outdir, File[] c StringBuilder errorBuffer = new StringBuilder(); StringBuilder outputBuffer = new StringBuilder(); - Thread out = copy(proc.getInputStream(), outputBuffer); - Thread err = copy(proc.getErrorStream(), errorBuffer); - - proc.waitFor(); + try { + // the compiler reads nothing from stdin - leaving the pipe open just + // holds a file descriptor until the Process is collected + proc.getOutputStream().close(); + + Thread out = copy(proc.getInputStream(), outputBuffer); + Thread err = copy(proc.getErrorStream(), errorBuffer); + + proc.waitFor(); + + // the readers can still be draining the pipes after the process exits, + // so join before reporting what they collected + out.join(); + err.join(); + } finally { + proc.destroy(); + } if (verbose || proc.exitValue() != 0) { if (outputBuffer.length() > 0) { @@ -347,9 +360,13 @@ private static File findJavaTool(String tool) { */ private static Thread copy(InputStream stream, final StringBuilder output) { final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset())); - Thread readerThread = new Thread(() -> - reader.lines().forEach(s -> output.append(s).append('\n')) - ); + Thread readerThread = new Thread(() -> { + try (BufferedReader r = reader) { + r.lines().forEach(s -> output.append(s).append('\n')); + } catch (IOException e) { + // nothing useful to do with a failure to drain the pipe + } + }); readerThread.start(); return readerThread; }