ICU-13606 Fixing race condition in MeasureFormat.

X-SVN-Rev: 41025
This commit is contained in:
Shane Carr 2018-03-01 00:58:47 +00:00
parent c589ea8b5d
commit 33709da06a
3 changed files with 69 additions and 1 deletions

View File

@ -1062,9 +1062,13 @@ UnicodeString &MeasureFormat::formatNumeric(
}
// Format time. draft becomes something like '5:30:45'
// #13606: DateFormat is not thread-safe, but MeasureFormat advertises itself as thread-safe.
FieldPosition smallestFieldPosition(smallestField);
UnicodeString draft;
static UMutex dateFmtMutex = U_MUTEX_INITIALIZER;
umtx_lock(&dateFmtMutex);
dateFmt.format(date, draft, smallestFieldPosition, status);
umtx_unlock(&dateFmtMutex);
// If we find field for smallest amount replace it with the formatted
// smallest amount from above taking care to replace the integer part

View File

@ -933,10 +933,15 @@ public class MeasureFormat extends UFormat {
if (intFieldPosition.getBeginIndex() == 0 && intFieldPosition.getEndIndex() == 0) {
throw new IllegalStateException();
}
// Format our duration as a date, but keep track of where the smallest field is
// so that we can use it to replace the integer portion of the smallest value.
// #13606: DateFormat is not thread-safe, but MeasureFormat advertises itself as thread-safe.
FieldPosition smallestFieldPosition = new FieldPosition(smallestField);
String draft = formatter.format(duration, new StringBuffer(), smallestFieldPosition).toString();
String draft;
synchronized (formatter) {
draft = formatter.format(duration, new StringBuffer(), smallestFieldPosition).toString();
}
try {
// If we find the smallest field

View File

@ -8,7 +8,10 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import com.ibm.icu.dev.test.TestFmwk;
import com.ibm.icu.impl.DontCareFieldPosition;
import com.ibm.icu.text.MeasureFormat;
import com.ibm.icu.util.Currency;
import com.ibm.icu.util.Measure;
import com.ibm.icu.util.MeasureUnit;
import com.ibm.icu.util.ULocale;
@ -30,5 +33,61 @@ public class MeasureUnitThreadTest extends TestFmwk {
Currency.getInstance(ULocale.ENGLISH);
try {thread.join();} catch(InterruptedException e) {};
}
static class NumericMeasureThread extends Thread {
final MeasureFormat mf;
final Measure[] arg;
final String expected;
volatile boolean running = true;
AssertionError error;
NumericMeasureThread(Measure[] arg, String expected) {
this.mf = MeasureFormat.getInstance(ULocale.ENGLISH, MeasureFormat.FormatWidth.NUMERIC);
this.arg = arg;
this.expected = expected;
this.error = null;
}
@Override
public void run() {
while (running) {
try {
StringBuilder sb = new StringBuilder();
mf.formatMeasures(sb, DontCareFieldPosition.INSTANCE, arg);
org.junit.Assert.assertEquals(expected, sb.toString());
} catch (AssertionError e) {
error = e;
break;
}
}
}
}
// Race in formatMeasures with width NUMERIC:
// http://bugs.icu-project.org/trac/ticket/13606
@Test
public void NumericRaceTest() throws InterruptedException {
NumericMeasureThread t1 = new NumericMeasureThread(new Measure[] {
new Measure(3, MeasureUnit.MINUTE),
new Measure(4, MeasureUnit.SECOND)
}, "3:04");
NumericMeasureThread t2 = new NumericMeasureThread(new Measure[] {
new Measure(5, MeasureUnit.MINUTE),
new Measure(6, MeasureUnit.SECOND)
}, "5:06");
t1.start();
t2.start();
Thread.sleep(5);
t1.running = false;
t2.running = false;
t1.join();
t2.join();
if (t1.error != null) {
throw new AssertionError("Failure in thread 1", t1.error);
}
if (t2.error != null) {
throw new AssertionError("Failure in thread 2", t2.error);
}
}
}