/*
-* Copyright (C) 2008-2015, International Business Machines
+* Copyright (C) 2008-2016, International Business Machines
* Corporation and others. All Rights Reserved.
*/
*
*
* </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
*/
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;
* 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;
* @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();
* @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)
{
* @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)
* this date interval formatter.
* @stable ICU 4.0
*/
- public DateFormat getDateFormat()
+ public synchronized DateFormat getDateFormat()
{
return (DateFormat)fDateFormat.clone();
}
/*
*******************************************************************************
- * Copyright (C) 2001-2015, International Business Machines Corporation and *
+ * Copyright (C) 2001-2016, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*/
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;
}
}
}
+
+ // 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);
+ }
+ }
+ }
+
+
}