Details
-
Bug
-
Resolution: Fixed
-
P3
-
6
-
b63
-
x86
-
windows_vista
Description
FULL PRODUCT VERSION :
A DESCRIPTION OF THE PROBLEM :
When the notification is scheduled to fire too close to the current time, sometimes it is not sent. Debugging the problem showed that the java.util.Timer, used in the jmx Timer, has no tasks left on the TaskQueue, but the jmx Timer has notifications left in its timerTable. See the attached code to reproduce the effect each time. Same test on Java SE 5.0 works as expected, it seems the bug is not present there.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.timer.Timer;
/**
* @author Nikolay Tsankov
*/
public class TimerTest
{
static int TASK_COUNT = 10000;
static class NotifListener implements NotificationListener
{
int count;
public synchronized void handleNotification(Notification notification, Object handback)
{
count++ ;
}
synchronized int getCount()
{
return count;
}
}
public static void main(String[] args)
throws Exception
{
final Timer timer = new Timer();
timer.start();
NotifListener listener = new NotifListener();
timer.addNotificationListener(listener, null, null);
ExecutorService executor = Executors.newFixedThreadPool(30);
final Random rand = new Random();
for (int i = 0; i < TASK_COUNT; i++ )
{
executor.execute(new Runnable()
{
public void run()
{
long fixedDelay = 0;// anything bigger than 100 and no alarms remain unfired
long dateMillis = System.currentTimeMillis() + fixedDelay + rand.nextInt(2000);
Date date = new Date(dateMillis);
timer.addNotification("type", "msg", "userData", date);
}
});
}
executor.shutdown();
executor.awaitTermination(20, TimeUnit.SECONDS);
waitForNotificationsToEnd(listener);
if (listener.count < TASK_COUNT)
{
System.err.println("Not fired: " + (TASK_COUNT - listener.count));
Integer size = getUtilTimerTaskQueueSize(timer);
System.err.println("The number of TimerTask objects on the java.util.Timer's TaskQueue : " + size);
System.err.println("The number of notifications on javax.management.timer.Timer : " + timer.getNbNotifications());
}
else
{
System.out.println("All notifications handled OK");
}
timer.stop();
}
private static int getUtilTimerTaskQueueSize(Timer timer)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
Field timerField = timer.getClass().getDeclaredField("timer");
timerField.setAccessible(true);
java.util.Timer utilTimer = (java.util.Timer)timerField.get(timer);
Field queueField = utilTimer.getClass().getDeclaredField("queue");
queueField.setAccessible(true);
Object queue = queueField.get(utilTimer);
Method sizeMethod = queue.getClass().getDeclaredMethod("size", new Class[]{});
sizeMethod.setAccessible(true);
Integer size = (Integer)sizeMethod.invoke(queue, new Object[]{});
return size.intValue();
}
/**
* Will return when all notifications are handled or after 10sec. of no new notifications
*
* @param listener
* @throws InterruptedException
*/
private static void waitForNotificationsToEnd(NotifListener listener)
throws InterruptedException
{
int oldCout = listener.getCount();
int noChangeCounter = 1;
while (listener.getCount() < TASK_COUNT)
{
Thread.sleep(1000);
System.out.print('.');
if (oldCout == listener.getCount())//no change
{
if (++noChangeCounter > 10)
{
break;
}
}
else
{
noChangeCounter = 1;
}
oldCout = listener.getCount();
}
System.out.println();
}
}
---------- END SOURCE ----------
Release Regression From : 5.0
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.
A DESCRIPTION OF THE PROBLEM :
When the notification is scheduled to fire too close to the current time, sometimes it is not sent. Debugging the problem showed that the java.util.Timer, used in the jmx Timer, has no tasks left on the TaskQueue, but the jmx Timer has notifications left in its timerTable. See the attached code to reproduce the effect each time. Same test on Java SE 5.0 works as expected, it seems the bug is not present there.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import javax.management.Notification;
import javax.management.NotificationListener;
import javax.management.timer.Timer;
/**
* @author Nikolay Tsankov
*/
public class TimerTest
{
static int TASK_COUNT = 10000;
static class NotifListener implements NotificationListener
{
int count;
public synchronized void handleNotification(Notification notification, Object handback)
{
count++ ;
}
synchronized int getCount()
{
return count;
}
}
public static void main(String[] args)
throws Exception
{
final Timer timer = new Timer();
timer.start();
NotifListener listener = new NotifListener();
timer.addNotificationListener(listener, null, null);
ExecutorService executor = Executors.newFixedThreadPool(30);
final Random rand = new Random();
for (int i = 0; i < TASK_COUNT; i++ )
{
executor.execute(new Runnable()
{
public void run()
{
long fixedDelay = 0;// anything bigger than 100 and no alarms remain unfired
long dateMillis = System.currentTimeMillis() + fixedDelay + rand.nextInt(2000);
Date date = new Date(dateMillis);
timer.addNotification("type", "msg", "userData", date);
}
});
}
executor.shutdown();
executor.awaitTermination(20, TimeUnit.SECONDS);
waitForNotificationsToEnd(listener);
if (listener.count < TASK_COUNT)
{
System.err.println("Not fired: " + (TASK_COUNT - listener.count));
Integer size = getUtilTimerTaskQueueSize(timer);
System.err.println("The number of TimerTask objects on the java.util.Timer's TaskQueue : " + size);
System.err.println("The number of notifications on javax.management.timer.Timer : " + timer.getNbNotifications());
}
else
{
System.out.println("All notifications handled OK");
}
timer.stop();
}
private static int getUtilTimerTaskQueueSize(Timer timer)
throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
{
Field timerField = timer.getClass().getDeclaredField("timer");
timerField.setAccessible(true);
java.util.Timer utilTimer = (java.util.Timer)timerField.get(timer);
Field queueField = utilTimer.getClass().getDeclaredField("queue");
queueField.setAccessible(true);
Object queue = queueField.get(utilTimer);
Method sizeMethod = queue.getClass().getDeclaredMethod("size", new Class[]{});
sizeMethod.setAccessible(true);
Integer size = (Integer)sizeMethod.invoke(queue, new Object[]{});
return size.intValue();
}
/**
* Will return when all notifications are handled or after 10sec. of no new notifications
*
* @param listener
* @throws InterruptedException
*/
private static void waitForNotificationsToEnd(NotifListener listener)
throws InterruptedException
{
int oldCout = listener.getCount();
int noChangeCounter = 1;
while (listener.getCount() < TASK_COUNT)
{
Thread.sleep(1000);
System.out.print('.');
if (oldCout == listener.getCount())//no change
{
if (++noChangeCounter > 10)
{
break;
}
}
else
{
noChangeCounter = 1;
}
oldCout = listener.getCount();
}
System.out.println();
}
}
---------- END SOURCE ----------
Release Regression From : 5.0
The above release value was the last known release where this
bug was not reproducible. Since then there has been a regression.