This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-csv.git
The following commit(s) were added to refs/heads/master by this push:
new 410175c3 [CSV-318] CSVPrinter.printRecord(Stream) hangs if given a
parallel stream
410175c3 is described below
commit 410175c3d01403ad575cb9d23263dfc6f32f7c3f
Author: Gary Gregory <[email protected]>
AuthorDate: Tue May 6 17:46:14 2025 -0400
[CSV-318] CSVPrinter.printRecord(Stream) hangs if given a parallel
stream
---
src/changes/changes.xml | 1 +
src/main/java/org/apache/commons/csv/CSVPrinter.java | 14 +++++++++++---
.../java/org/apache/commons/csv/JiraCsv318Test.java | 20 --------------------
3 files changed, 12 insertions(+), 23 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 59a8d096..a2d02ac4 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -42,6 +42,7 @@
<body>
<release version="1.14.1" date="YYYY-MM-DD" description="This is a feature
and maintenance release. Java 8 or later is required.">
<!-- FIX -->
+ <action type="fix" issue="CSV-318" dev="ggregory" due-to="Joseph
Shraibman, Gary Gregory">CSVPrinter.printRecord(Stream) hangs if given a
parallel stream.</action>
<!-- ADD -->
<!-- UPDATE -->
<action type="update" dev="ggregory" due-to="Gary Gregory">Bump
commons-io:commons-io from 2.18.0 to 2.19.0.</action>
diff --git a/src/main/java/org/apache/commons/csv/CSVPrinter.java
b/src/main/java/org/apache/commons/csv/CSVPrinter.java
index 3b9efa7c..80cab060 100644
--- a/src/main/java/org/apache/commons/csv/CSVPrinter.java
+++ b/src/main/java/org/apache/commons/csv/CSVPrinter.java
@@ -35,6 +35,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.Objects;
+import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Stream;
import org.apache.commons.io.function.IOStream;
@@ -87,6 +88,8 @@ public final class CSVPrinter implements Flushable, Closeable
{
private boolean newRecord = true;
private long recordCount;
+
+ private ReentrantLock lock = new ReentrantLock();
/**
* Creates a printer that will print values to the given stream following
the CSVFormat.
@@ -326,9 +329,14 @@ public final class CSVPrinter implements Flushable,
Closeable {
* @since 1.10.0
*/
@SuppressWarnings("resource") // caller closes.
- public synchronized void printRecord(final Stream<?> values) throws
IOException {
- IOStream.adapt(values).forEachOrdered(this::print);
- endOfRecord();
+ public void printRecord(final Stream<?> values) throws IOException {
+ lock.lock();
+ try {
+ IOStream.adapt(values).forEachOrdered(this::print);
+ endOfRecord();
+ } finally {
+ lock.unlock();
+ }
}
private void printRecordObject(final Object value) throws IOException {
diff --git a/src/test/java/org/apache/commons/csv/JiraCsv318Test.java
b/src/test/java/org/apache/commons/csv/JiraCsv318Test.java
index d16f790f..1424809c 100644
--- a/src/test/java/org/apache/commons/csv/JiraCsv318Test.java
+++ b/src/test/java/org/apache/commons/csv/JiraCsv318Test.java
@@ -31,7 +31,6 @@ import java.util.stream.Stream;
import org.apache.commons.io.function.IOConsumer;
import org.apache.commons.io.function.IOStream;
import org.apache.commons.lang3.ArrayUtils;
-import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
/**
@@ -67,10 +66,6 @@ public class JiraCsv318Test {
return Stream.of("col a", "col b", "col c");
}
- public synchronized void printRecord(final Stream<?> values) throws
IOException {
- // IOStream.adapt(values).forEachOrdered(this::print);
- }
-
@Test
void testDefaultStream() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
@@ -91,20 +86,6 @@ public class JiraCsv318Test {
checkOutput(baos);
}
- @SuppressWarnings("resource")
- @Test
- @Disabled("Deadlock because CSVPrinter.print(Object) is synchronized")
- void testParallelIOStreamSynchronizedPrinter() throws IOException {
- final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- try (CSVPrinter printer = newPrinter(baos)) {
- synchronized (printer) {
-
IOStream.adapt(newParallelStream()).forEachOrdered(printer::print);
- }
- }
- // No EOR marker in this test intentionally, so checkOutput will trim.
- checkOutput(baos);
- }
-
@SuppressWarnings("resource")
@Test
void testParallelIOStreamSynchronizedPrinterNotUsed() throws IOException {
@@ -125,7 +106,6 @@ public class JiraCsv318Test {
}
@Test
- @Disabled("Deadlock because CSVPrinter.print(Object) is synchronized")
void testParallelStream() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (CSVPrinter printer = newPrinter(baos)) {