From f3adc1ee7ba305bc6df2b96bab7fa4d1857ca5b4 Mon Sep 17 00:00:00 2001 From: Davide Angelocola Date: Sat, 11 Jul 2026 21:59:46 +0200 Subject: [PATCH] fix: close pipe readers before join() to prevent conformance test hang (#259) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit assertMatchesParquetOracle streams the vortex-java CSV export and the parquet oracle through a PipedWriter/PipedReader pair on two virtual threads, compared line-by-line by the main thread. If assertFilesMatch stops reading early (oracle abort, line mismatch), nobody drains the pipe anymore — a producer still writing blocks forever once its 128KB buffer fills, and the unconditional join() right after hangs forever. Never surfaced before #257 because every prior repeated/list column consumer failed fast (VortexException thrown immediately). #257 is the first case where the Java side succeeds and outruns an early-exiting reader (confirmed via jstack: main thread parked on Thread.join() for 18+ minutes, ~4s CPU time). Close both readers before join() so a producer parked on a full buffer gets an IOException instead of blocking; the artificial "Pipe closed" error ranks below the real captured errors in the existing priority order, so behavior is unchanged on every other path. --- .../RaincloudConformanceIntegrationTest.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/integration/src/test/java/io/github/dfa1/vortex/integration/RaincloudConformanceIntegrationTest.java b/integration/src/test/java/io/github/dfa1/vortex/integration/RaincloudConformanceIntegrationTest.java index 18f62862..d349559d 100644 --- a/integration/src/test/java/io/github/dfa1/vortex/integration/RaincloudConformanceIntegrationTest.java +++ b/integration/src/test/java/io/github/dfa1/vortex/integration/RaincloudConformanceIntegrationTest.java @@ -18,6 +18,7 @@ import org.opentest4j.TestAbortedException; import java.io.BufferedReader; +import java.io.Closeable; import java.io.IOException; import java.io.PipedReader; import java.io.PipedWriter; @@ -122,6 +123,14 @@ private static void assertMatchesParquetOracle(Path vortex, Path parquet) throws assertFilesMatch(vortexReader, oracleReader); } catch (Throwable t) { mainError = t; + } finally { + // assertFilesMatch may stop reading early (oracle abort, line mismatch) while a + // producer is still writing — closing the read end here, rather than relying on + // the outer try-with-resources (which only runs after join() below), unblocks a + // producer parked on a full PipedWriter buffer with a "Pipe closed" IOException + // instead of hanging join() forever (#259). + closeQuietly(vortexReader); + closeQuietly(oracleReader); } try { @@ -173,6 +182,18 @@ private static void assertMatchesParquetOracle(Path vortex, Path parquet) throws } } + /// Closes a stream ignoring the outcome, used only to unblock a producer thread + /// parked writing to the other end of a pipe before joining it. + /// + /// @param closeable the stream to close + private static void closeQuietly(Closeable closeable) { + try { + closeable.close(); + } catch (IOException ignored) { + // best effort: only used to unblock a producer thread before join() + } + } + /// Runs the full conformance check but reports the outcome as an aborted test /// either way: only triaged matrix entries may count as green or red. The abort /// message says which way to flip the entry.