Date to String conversion code in NativeDate class is not thread-safe

RESOLVED FIXED

Status

Rhino
Core
RESOLVED FIXED
11 years ago
11 years ago

People

(Reporter: Gary Xue, Unassigned)

Tracking

Details

(Reporter)

Description

11 years ago
User-Agent:       Mozilla/5.0 (Windows; U; Windows NT 5.2; en-US; rv:1.8.0.11) Gecko/20070312 Firefox/1.5.0.11
Build Identifier: 1.6R1

Class org.mozilla.javascript.NativeDate class uses static java.text.DateFormat instances (members: timeZoneFormatter,localeDateTimeFormatter,localeDateFormatter, localeTimeFormatter) to convert date/time values to string. However DateFormat instances are not thread-safe (check JDK documentation). This results in occasional runtime exceptions when expressions containing date->string conversion are evaluated concurrently.

See Steps to Reproduce for sample code that demonstrates this problem. This code evaluates an expression 
   "Date:" + date_var
in 20 concurrent threads. The test code occasionally gives an ArrayIndexOutOfBoundsException.

Reproducible: Sometimes

Steps to Reproduce:
1.Run the following test code, preferrably on a multi-CPU machine. I've reliably reproduced this on a dual-Intel P4 2.8GHZ machine running Windows 2003 Server, against JRE version 1.5.0_07

2. The program occasionally dumps an exception. See Actual Results for stack. This happens about 1 out of 5 runs in my test. 

---- Test Code  ----

import java.util.Random;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;

public class DateConversionTest
{
	
	public static void main(String[] args) throws Exception
	{
		// Run 10 threads
		for ( int i = 0; i < 20; i++ )
		{
			Thread t = new TestThread();
			t.start();
		}
	}
	
	static class TestThread extends Thread
	{
        public void run() 
        {
        	Context cx;
        	Scriptable scope;
    		cx = Context.enter();
    		scope = cx.initStandardObjects();

    		Random rand = new Random();

    		for (int i = 0; i < 1000; i++)
    		{
        		int year = rand.nextInt(10) + 1990;
        		int month = rand.nextInt(12) + 1;
        		int day = rand.nextInt(28) + 1;
        		int hour = rand.nextInt(24);
        		int minute = rand.nextInt(60);
        		int sec = rand.nextInt(60);
        		
            	String expr = "\"Date: \" + new Date( " + 
            		year + "," + month + "," + day + "," + hour + "," + minute + "," + sec + ")"; 
        		cx.evaluateString( scope, expr, "", 1, null );
    		}
    		
    		Context.exit();
        }		
	}
	
}

Actual Results:  
Exception: 

Exception in thread "Thread-14" java.lang.ArrayIndexOutOfBoundsException: 22
        at sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate(Unknown Source)
        at java.util.GregorianCalendar.computeFields(Unknown Source)
        at java.util.GregorianCalendar.computeFields(Unknown Source)
        at java.util.Calendar.setTimeInMillis(Unknown Source)
        at java.util.Calendar.setTime(Unknown Source)
        at java.text.SimpleDateFormat.format(Unknown Source)
        at java.text.SimpleDateFormat.format(Unknown Source)
        at java.text.DateFormat.format(Unknown Source)
        at org.mozilla.javascript.NativeDate.date_format(NativeDate.java:1054)
        at org.mozilla.javascript.NativeDate.execIdCall(NativeDate.java:201)
        at org.mozilla.javascript.IdFunctionObject.call(IdFunctionObject.java:121)
        at org.mozilla.javascript.ScriptableObject.getDefaultValue(ScriptableObject.java:577)
        at org.mozilla.javascript.NativeDate.getDefaultValue(NativeDate.java:84)
        at org.mozilla.javascript.ScriptRuntime.add(ScriptRuntime.java:2290)
        at org.mozilla.javascript.gen.c2708._c0(:1)
        at org.mozilla.javascript.gen.c2708.call()
        at org.mozilla.javascript.ContextFactory.doTopCall(ContextFactory.java:304)
        at org.mozilla.javascript.ScriptRuntime.doTopCall(ScriptRuntime.java:2769)
        at org.mozilla.javascript.gen.c2708.call()
        at org.mozilla.javascript.gen.c2708.exec()
        at org.mozilla.javascript.Context.evaluateString(Context.java:1220)
        at DateConversionTest$TestThread.run(DateConversionTest.java:61)


Expected Results:  
No exception

Use an instance of DateFormat per thread. Or synchronize code that accesses the static DateFormat instances.

Comment 1

11 years ago
Fixed: 

Checking in NativeDate.java;
/cvsroot/mozilla/js/rhino/src/org/mozilla/javascript/NativeDate.java,v  <--  NativeDate.java
new revision: 1.65.2.1; previous revision: 1.65
done

I just synchronized access to the static DateFormat instances. 
Status: UNCONFIRMED → RESOLVED
Last Resolved: 11 years ago
Resolution: --- → FIXED
(Reporter)

Comment 2

11 years ago
Norris - thanks for the fix. Did this fix make it into the 1.6R6 release? Thanks, Gary
You need to log in before you can comment on or make changes to this bug.