ICU-11669 Thread safety of DateIntervalFormat::format()

X-SVN-Rev: 38157
This commit is contained in:
Andy Heninger 2016-01-07 21:15:19 +00:00
parent 0183cad1d7
commit 09819eab73
2 changed files with 90 additions and 7 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2008-2015, International Business Machines
* Copyright (C) 2008-2016, International Business Machines
* Corporation and others. All Rights Reserved.
*/
@ -247,6 +247,12 @@ import com.ibm.icu.util.ULocale.Category;
*
*
* </pre>
* <h4>Synchronization</h4>
*
* The format methods of DateIntervalFormat may be used concurrently from multiple threads.
* Functions that alter the state of a DateIntervalFormat object (setters)
* may not be used concurrently with any other functions.
*
* @stable ICU 4.0
*/
@ -296,7 +302,9 @@ public class DateIntervalFormat extends UFormat {
private DateIntervalInfo fInfo;
/*
* The DateFormat object used to format single pattern
* The DateFormat object used to format single pattern.
* Because fDateFormat is modified during format operations, all
* access to it from logically const, thread safe functions must be synchronized.
*/
private SimpleDateFormat fDateFormat;
@ -304,6 +312,8 @@ public class DateIntervalFormat extends UFormat {
* The 2 calendars with the from and to date.
* could re-use the calendar in fDateFormat,
* but keeping 2 calendars make it clear and clean.
* Because these Calendars are modified during format operations, all
* access to them from logically const, thread safe functions must be synchronized.
*/
private Calendar fFromCalendar;
private Calendar fToCalendar;
@ -559,7 +569,7 @@ public class DateIntervalFormat extends UFormat {
* @return A copy of the object.
* @stable ICU 4.0
*/
public Object clone()
public synchronized Object clone()
{
DateIntervalFormat other = (DateIntervalFormat) super.clone();
other.fDateFormat = (SimpleDateFormat) fDateFormat.clone();
@ -618,7 +628,7 @@ public class DateIntervalFormat extends UFormat {
* @return Reference to 'appendTo' parameter.
* @stable ICU 4.0
*/
public final StringBuffer format(DateInterval dtInterval,
public final synchronized StringBuffer format(DateInterval dtInterval,
StringBuffer appendTo,
FieldPosition fieldPosition)
{
@ -686,7 +696,7 @@ public class DateIntervalFormat extends UFormat {
* @throws IllegalArgumentException if the two calendars are not equivalent.
* @stable ICU 4.0
*/
public final StringBuffer format(Calendar fromCalendar,
public final synchronized StringBuffer format(Calendar fromCalendar,
Calendar toCalendar,
StringBuffer appendTo,
FieldPosition pos)
@ -1008,7 +1018,7 @@ public class DateIntervalFormat extends UFormat {
* this date interval formatter.
* @stable ICU 4.0
*/
public DateFormat getDateFormat()
public synchronized DateFormat getDateFormat()
{
return (DateFormat)fDateFormat.clone();
}

View file

@ -1,6 +1,6 @@
/*
*******************************************************************************
* Copyright (C) 2001-2015, International Business Machines Corporation and *
* Copyright (C) 2001-2016, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
@ -14,7 +14,10 @@ package com.ibm.icu.dev.test.format;
import java.text.FieldPosition;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import com.ibm.icu.impl.Utility;
@ -1714,4 +1717,74 @@ public class DateIntervalFormatTest extends com.ibm.icu.dev.test.TestFmwk {
}
}
}
// TestTicket11669 - Check the thread safety of DateIntervalFormat.format().
// This test failed with ICU 56.
public void TestTicket11669 () {
// These final variables are accessed directly by the concurrent threads.
final DateIntervalFormat formatter = DateIntervalFormat.getInstance(DateFormat.YEAR_MONTH_DAY, ULocale.US);
final ArrayList<DateInterval> testIntervals = new ArrayList<DateInterval>();
final ArrayList<String>expectedResults = new ArrayList<String>();
// Create and save the input test data.
TimeZone tz = TimeZone.getTimeZone("Americal/Los_Angeles");
Calendar intervalStart = Calendar.getInstance(tz, ULocale.US);
Calendar intervalEnd = Calendar.getInstance(tz, ULocale.US);
intervalStart.set(2009, 6, 1);
intervalEnd.set(2009, 6, 2);
testIntervals.add(new DateInterval(intervalStart.getTimeInMillis(), intervalEnd.getTimeInMillis()));
intervalStart.set(2015, 2, 27);
intervalEnd.set(2015, 3, 1);
testIntervals.add(new DateInterval(intervalStart.getTimeInMillis(), intervalEnd.getTimeInMillis()));
// Run the formatter single-threaded to create and save the expected results.
for (DateInterval interval: testIntervals) {
FieldPosition pos = new FieldPosition(0);
StringBuffer result = new StringBuffer();
formatter.format(interval, result, pos);
expectedResults.add(result.toString());
}
class TestThread extends Thread {
public String errorMessage;
public void run() {
for (int loop=0; loop < 2000; ++loop) {
ListIterator<String> expectedItr = expectedResults.listIterator();
for (DateInterval interval: testIntervals) {
String expected = expectedItr.next();
FieldPosition pos = new FieldPosition(0);
StringBuffer result = new StringBuffer();
formatter.format(interval, result, pos);
if (!expected.equals(result.toString())) {
// Note: The ICU test framework doesn't support reporting failures from within a sub-thread.
// Save the failure for the main thread to pick up later.
errorMessage = String.format("Expected \"%s\", actual \"%s\"", expected, result);
return;
}
}
}
}
}
List<TestThread> threads = new ArrayList<TestThread>();
for (int i=0; i<4; ++i) {
threads.add(new TestThread());
}
for (Thread t: threads) {
t.start();
}
for (TestThread t: threads) {
try {
t.join();
} catch (InterruptedException e) {
fail("Unexpected exception: " + e.toString());
}
if (t.errorMessage != null) {
fail(t.errorMessage);
}
}
}
}