}
// 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
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
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;
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);
+ }
+ }
}