Skip to content

Commit 2a57f41

Browse files
authored
Merge pull request #624 from rootvector2/csvformat-print-writelock
synchronize private CSVFormat print overload on writeLock
2 parents 3634534 + 1a8403d commit 2a57f41

2 files changed

Lines changed: 52 additions & 15 deletions

File tree

src/main/java/org/apache/commons/csv/CSVFormat.java

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,21 +2264,23 @@ public void print(final Object value, final Appendable out, final boolean newRec
22642264
}
22652265
}
22662266

2267-
private synchronized void print(final Object object, final CharSequence value, final Appendable out, final boolean newRecord) throws IOException {
2268-
final int offset = 0;
2269-
final int len = value.length();
2270-
if (!newRecord) {
2271-
out.append(getDelimiterString());
2272-
}
2273-
if (object == null) {
2274-
out.append(value);
2275-
} else if (isQuoteCharacterSet()) {
2276-
// The original object is needed so can check for Number
2277-
printWithQuotes(object, value, out, newRecord);
2278-
} else if (isEscapeCharacterSet()) {
2279-
printWithEscapes(value, out);
2280-
} else {
2281-
out.append(value, offset, len);
2267+
private void print(final Object object, final CharSequence value, final Appendable out, final boolean newRecord) throws IOException {
2268+
synchronized (writeLock) {
2269+
final int offset = 0;
2270+
final int len = value.length();
2271+
if (!newRecord) {
2272+
out.append(getDelimiterString());
2273+
}
2274+
if (object == null) {
2275+
out.append(value);
2276+
} else if (isQuoteCharacterSet()) {
2277+
// The original object is needed so can check for Number
2278+
printWithQuotes(object, value, out, newRecord);
2279+
} else if (isEscapeCharacterSet()) {
2280+
printWithEscapes(value, out);
2281+
} else {
2282+
out.append(value, offset, len);
2283+
}
22822284
}
22832285
}
22842286

src/test/java/org/apache/commons/csv/CSVFormatTest.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import static org.junit.jupiter.api.Assertions.assertNotSame;
3232
import static org.junit.jupiter.api.Assertions.assertNull;
3333
import static org.junit.jupiter.api.Assertions.assertThrows;
34+
import static org.junit.jupiter.api.Assertions.assertTimeoutPreemptively;
3435
import static org.junit.jupiter.api.Assertions.assertTrue;
3536
import static org.junit.jupiter.api.Assertions.fail;
3637

@@ -45,8 +46,11 @@
4546
import java.lang.reflect.Modifier;
4647
import java.sql.ResultSet;
4748
import java.sql.SQLException;
49+
import java.time.Duration;
4850
import java.util.Arrays;
4951
import java.util.Objects;
52+
import java.util.concurrent.CountDownLatch;
53+
import java.util.concurrent.TimeUnit;
5054

5155
import org.apache.commons.csv.CSVFormat.Builder;
5256
import org.junit.jupiter.api.Test;
@@ -921,6 +925,37 @@ void testPrintRecordEmpty() throws IOException {
921925
assertEquals(format.getRecordSeparator(), out.toString());
922926
}
923927

928+
@Test
929+
void testPrintRecordNotBlockedByInstanceMonitor() throws Exception {
930+
// A CSVFormat is documented as thread-safe. Internal write locking uses a private lock, so external code
931+
// holding the format instance monitor must not be able to block a print call.
932+
final CSVFormat format = CSVFormat.RFC4180;
933+
final CountDownLatch holdingMonitor = new CountDownLatch(1);
934+
final CountDownLatch releaseMonitor = new CountDownLatch(1);
935+
final Thread holder = new Thread(() -> {
936+
synchronized (format) {
937+
holdingMonitor.countDown();
938+
try {
939+
releaseMonitor.await();
940+
} catch (final InterruptedException e) {
941+
Thread.currentThread().interrupt();
942+
}
943+
}
944+
});
945+
holder.start();
946+
try {
947+
assertTrue(holdingMonitor.await(5, TimeUnit.SECONDS));
948+
assertTimeoutPreemptively(Duration.ofSeconds(5), () -> {
949+
final Appendable out = new StringBuilder();
950+
format.printRecord(out, "a", "b", "c");
951+
assertEquals("a,b,c" + format.getRecordSeparator(), out.toString());
952+
});
953+
} finally {
954+
releaseMonitor.countDown();
955+
holder.join();
956+
}
957+
}
958+
924959
@Test
925960
void testPrintWithEscapesEndWithCRLF() throws IOException {
926961
final Reader in = new StringReader("x,y,x\r\na,?b,c\r\n");

0 commit comments

Comments
 (0)