Skip to content

Commit 0b69fe9

Browse files
committed
fix: close pipe readers before join() to prevent conformance test hang (#259)
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.
1 parent 776843a commit 0b69fe9

1 file changed

Lines changed: 21 additions & 0 deletions

File tree

integration/src/test/java/io/github/dfa1/vortex/integration/RaincloudConformanceIntegrationTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.opentest4j.TestAbortedException;
1919

2020
import java.io.BufferedReader;
21+
import java.io.Closeable;
2122
import java.io.IOException;
2223
import java.io.PipedReader;
2324
import java.io.PipedWriter;
@@ -122,6 +123,14 @@ private static void assertMatchesParquetOracle(Path vortex, Path parquet) throws
122123
assertFilesMatch(vortexReader, oracleReader);
123124
} catch (Throwable t) {
124125
mainError = t;
126+
} finally {
127+
// assertFilesMatch may stop reading early (oracle abort, line mismatch) while a
128+
// producer is still writing — closing the read end here, rather than relying on
129+
// the outer try-with-resources (which only runs after join() below), unblocks a
130+
// producer parked on a full PipedWriter buffer with a "Pipe closed" IOException
131+
// instead of hanging join() forever (#259).
132+
closeQuietly(vortexReader);
133+
closeQuietly(oracleReader);
125134
}
126135

127136
try {
@@ -173,6 +182,18 @@ private static void assertMatchesParquetOracle(Path vortex, Path parquet) throws
173182
}
174183
}
175184

185+
/// Closes a stream ignoring the outcome, used only to unblock a producer thread
186+
/// parked writing to the other end of a pipe before joining it.
187+
///
188+
/// @param closeable the stream to close
189+
private static void closeQuietly(Closeable closeable) {
190+
try {
191+
closeable.close();
192+
} catch (IOException ignored) {
193+
// best effort: only used to unblock a producer thread before join()
194+
}
195+
}
196+
176197
/// Runs the full conformance check but reports the outcome as an aborted test
177198
/// either way: only triaged matrix entries may count as green or red. The abort
178199
/// message says which way to flip the entry.

0 commit comments

Comments
 (0)