Browse Source

#63291 CellFormat global cache isn't thread-safe

move date format synchronization down to where the problem instance is held. 

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1856647 13f79535-47bb-0310-9956-ffa450edef68
tags/REL_4_1_0
Greg Woolsey 5 years ago
parent
commit
0d22749477

+ 2
- 0
.settings/com.vaadin.designer.prefs View File

applicationTheme=Project structure is not supported.
eclipse.preferences.version=1

+ 1
- 1
src/java/org/apache/poi/ss/format/CellDateFormatter.java View File

} }


/** {@inheritDoc} */ /** {@inheritDoc} */
public void formatValue(StringBuffer toAppendTo, Object value) {
public synchronized void formatValue(StringBuffer toAppendTo, Object value) {
if (value == null) if (value == null)
value = 0.0; value = 0.0;
if (value instanceof Number) { if (value instanceof Number) {

+ 10
- 0
src/java/org/apache/poi/ss/format/CellFormatter.java View File



/** /**
* Format a value according the format string. * Format a value according the format string.
* <p/>
* NOTE: this method must be thread safe! In particular, if it uses a
* Format instance that is not thread safe, i.e. DateFormat, this method
* must be synchronized, either on the method, if the format is a final
* property, or on the format instance itself.
* *
* @param toAppendTo The buffer to append to. * @param toAppendTo The buffer to append to.
* @param value The value to format. * @param value The value to format.


/** /**
* Format a value according to the type, in the most basic way. * Format a value according to the type, in the most basic way.
* <p/>
* NOTE: this method must be thread safe! In particular, if it uses a
* Format instance that is not thread safe, i.e. DateFormat, this method
* must be synchronized, either on the method, if the format is a final
* property, or on the format instance itself.
* *
* @param toAppendTo The buffer to append to. * @param toAppendTo The buffer to append to.
* @param value The value to format. * @param value The value to format.

+ 1
- 1
src/java/org/apache/poi/ss/usermodel/DataFormatter.java View File

return getFormat(cell.getNumericCellValue(), formatIndex, formatStr); return getFormat(cell.getNumericCellValue(), formatIndex, formatStr);
} }


private synchronized Format getFormat(double cellValue, int formatIndex, String formatStrIn) {
private Format getFormat(double cellValue, int formatIndex, String formatStrIn) {
localeChangedObservable.checkForLocaleChange(); localeChangedObservable.checkForLocaleChange();


// Might be better to separate out the n p and z formats, falling back to p when n and z are not set. // Might be better to separate out the n p and z formats, falling back to p when n and z are not set.

+ 14
- 6
src/testcases/org/apache/poi/ss/usermodel/TestDataFormatter.java View File



@Test @Test
public void testConcurrentCellFormat() throws Exception { public void testConcurrentCellFormat() throws Exception {
DataFormatter formatter = new DataFormatter();
doFormatTestSequential(formatter);
doFormatTestConcurrent(formatter);
DataFormatter formatter1 = new DataFormatter();
DataFormatter formatter2 = new DataFormatter();
doFormatTestSequential(formatter1);
doFormatTestConcurrent(formatter1, formatter2);
} }


private void doFormatTestSequential(DataFormatter formatter) { private void doFormatTestSequential(DataFormatter formatter) {
} }
} }


private void doFormatTestConcurrent(DataFormatter formatter) throws Exception {
private void doFormatTestConcurrent(DataFormatter formatter1, DataFormatter formatter2) throws Exception {
ArrayList<CompletableFuture<Boolean>> futures = new ArrayList<>(); ArrayList<CompletableFuture<Boolean>> futures = new ArrayList<>();
for (int i = 0; i < 1_000; i++) { for (int i = 0; i < 1_000; i++) {
final int iteration = i; final int iteration = i;
CompletableFuture<Boolean> future = CompletableFuture.supplyAsync( CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(
() -> { () -> {
boolean r1 = doFormatTest(formatter, 43551.50990171296, "3/27/19 12:14:15 PM", iteration);
boolean r2 = doFormatTest(formatter, 36104.424780092595, "11/5/98 10:11:41 AM", iteration);
boolean r1 = doFormatTest(formatter1, 43551.50990171296, "3/27/19 12:14:15 PM", iteration);
boolean r2 = doFormatTest(formatter1, 36104.424780092595, "11/5/98 10:11:41 AM", iteration);
return r1 && r2;
});
futures.add(future);
future = CompletableFuture.supplyAsync(
() -> {
boolean r1 = doFormatTest(formatter2, 43551.50990171296, "3/27/19 12:14:15 PM", iteration);
boolean r2 = doFormatTest(formatter2, 36104.424780092595, "11/5/98 10:11:41 AM", iteration);
return r1 && r2; return r1 && r2;
}); });
futures.add(future); futures.add(future);

Loading…
Cancel
Save