394215 - Scheduled tasks throwing exceptions kill java.util.Timer thread.

Wrapping the the task run() method into a try/catch to avoid that the Timer thread dies.
This commit is contained in:
Simone Bordet 2012-11-13 19:29:58 +01:00
parent a37f9c813d
commit c07bab8a05
2 changed files with 78 additions and 36 deletions

View File

@ -24,9 +24,13 @@ import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public class TimerScheduler extends AbstractLifeCycle implements Scheduler public class TimerScheduler extends AbstractLifeCycle implements Scheduler
{ {
private static final Logger LOG = Log.getLogger(TimerScheduler.class);
/* /*
* This class uses the Timer class rather than an ScheduledExecutionService because * This class uses the Timer class rather than an ScheduledExecutionService because
* it uses the same algorithm internally and the signature is cheaper to use as there are no * it uses the same algorithm internally and the signature is cheaper to use as there are no
@ -85,7 +89,14 @@ public class TimerScheduler extends AbstractLifeCycle implements Scheduler
@Override @Override
public void run() public void run()
{ {
_task.run(); try
{
_task.run();
}
catch (Throwable x)
{
LOG.debug("Exception while executing task "+_task,x);
}
} }
@Override @Override

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.util.thread;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -45,7 +46,7 @@ public class SchedulerTest
{ {
private static final BenchmarkHelper benchmark = new BenchmarkHelper(); private static final BenchmarkHelper benchmark = new BenchmarkHelper();
private static final Executor executor = Executors.newFixedThreadPool(256); private static final Executor executor = Executors.newFixedThreadPool(256);
@Parameterized.Parameters @Parameterized.Parameters
public static Collection<Object[]> data() public static Collection<Object[]> data()
{ {
@ -58,27 +59,27 @@ public class SchedulerTest
}; };
return Arrays.asList(data); return Arrays.asList(data);
} }
// Scheduler _scheduler=new SimpleScheduler(); // Scheduler _scheduler=new SimpleScheduler();
Scheduler _scheduler; Scheduler _scheduler;
public SchedulerTest(Scheduler scheduler) public SchedulerTest(Scheduler scheduler)
{ {
_scheduler=scheduler; _scheduler=scheduler;
} }
@Before @Before
public void before() throws Exception public void before() throws Exception
{ {
_scheduler.start(); _scheduler.start();
} }
@After @After
public void after() throws Exception public void after() throws Exception
{ {
_scheduler.stop(); _scheduler.stop();
} }
@Test @Test
public void testExecution() throws Exception public void testExecution() throws Exception
{ {
@ -92,14 +93,14 @@ public class SchedulerTest
executed.set(System.currentTimeMillis()); executed.set(System.currentTimeMillis());
} }
},3000,TimeUnit.MILLISECONDS); },3000,TimeUnit.MILLISECONDS);
Thread.sleep(4000); Thread.sleep(4000);
Assert.assertFalse(task.cancel()); Assert.assertFalse(task.cancel());
Assert.assertThat(executed.get(),Matchers.greaterThanOrEqualTo(expected)); Assert.assertThat(executed.get(),Matchers.greaterThanOrEqualTo(expected));
Assert.assertThat(expected-executed.get(),Matchers.lessThan(1000L)); Assert.assertThat(expected-executed.get(),Matchers.lessThan(1000L));
} }
@Test @Test
public void testTwoExecution() throws Exception public void testTwoExecution() throws Exception
{ {
@ -113,12 +114,12 @@ public class SchedulerTest
executed.set(System.currentTimeMillis()); executed.set(System.currentTimeMillis());
} }
},3000,TimeUnit.MILLISECONDS); },3000,TimeUnit.MILLISECONDS);
Thread.sleep(4000); Thread.sleep(4000);
Assert.assertFalse(task.cancel()); Assert.assertFalse(task.cancel());
Assert.assertThat(executed.get(),Matchers.greaterThanOrEqualTo(expected)); Assert.assertThat(executed.get(),Matchers.greaterThanOrEqualTo(expected));
Assert.assertThat(expected-executed.get(),Matchers.lessThan(1000L)); Assert.assertThat(expected-executed.get(),Matchers.lessThan(1000L));
final AtomicLong executed1 = new AtomicLong(); final AtomicLong executed1 = new AtomicLong();
long expected1=System.currentTimeMillis()+3000; long expected1=System.currentTimeMillis()+3000;
Scheduler.Task task1=_scheduler.schedule(new Runnable() Scheduler.Task task1=_scheduler.schedule(new Runnable()
@ -129,13 +130,13 @@ public class SchedulerTest
executed1.set(System.currentTimeMillis()); executed1.set(System.currentTimeMillis());
} }
},3000,TimeUnit.MILLISECONDS); },3000,TimeUnit.MILLISECONDS);
Thread.sleep(4000); Thread.sleep(4000);
Assert.assertFalse(task1.cancel()); Assert.assertFalse(task1.cancel());
Assert.assertThat(executed1.get(),Matchers.greaterThanOrEqualTo(expected1)); Assert.assertThat(executed1.get(),Matchers.greaterThanOrEqualTo(expected1));
Assert.assertThat(expected1-executed1.get(),Matchers.lessThan(1000L)); Assert.assertThat(expected1-executed1.get(),Matchers.lessThan(1000L));
} }
@Test @Test
public void testQuickCancel() throws Exception public void testQuickCancel() throws Exception
{ {
@ -148,13 +149,13 @@ public class SchedulerTest
executed.set(System.currentTimeMillis()); executed.set(System.currentTimeMillis());
} }
},2000,TimeUnit.MILLISECONDS); },2000,TimeUnit.MILLISECONDS);
Thread.sleep(100); Thread.sleep(100);
Assert.assertTrue(task.cancel()); Assert.assertTrue(task.cancel());
Thread.sleep(2500); Thread.sleep(2500);
Assert.assertEquals(0,executed.get()); Assert.assertEquals(0,executed.get());
} }
@Test @Test
public void testLongCancel() throws Exception public void testLongCancel() throws Exception
{ {
@ -167,20 +168,50 @@ public class SchedulerTest
executed.set(System.currentTimeMillis()); executed.set(System.currentTimeMillis());
} }
},2000,TimeUnit.MILLISECONDS); },2000,TimeUnit.MILLISECONDS);
Thread.sleep(1600); Thread.sleep(1600);
Assert.assertTrue(task.cancel()); Assert.assertTrue(task.cancel());
Thread.sleep(1000); Thread.sleep(1000);
Assert.assertEquals(0,executed.get()); Assert.assertEquals(0,executed.get());
} }
@Test
public void testTaskThrowsException() throws Exception
{
long delay = 500;
Scheduler.Task task=_scheduler.schedule(new Runnable()
{
@Override
public void run()
{
throw new RuntimeException();
}
}, delay, TimeUnit.MILLISECONDS);
TimeUnit.MILLISECONDS.sleep(2 * delay);
// Check whether after a task throwing an exception, the scheduler is still working
final CountDownLatch latch = new CountDownLatch(1);
_scheduler.schedule(new Runnable()
{
@Override
public void run()
{
latch.countDown();
}
}, delay, TimeUnit.MILLISECONDS);
Assert.assertTrue(latch.await(2 * delay, TimeUnit.MILLISECONDS));
}
@Test @Test
@Slow @Slow
public void testManySchedulesAndCancels() throws Exception public void testManySchedulesAndCancels() throws Exception
{ {
schedule(100,5000,3800,200); schedule(100,5000,3800,200);
} }
@Test @Test
@Slow @Slow
@Ignore @Ignore
@ -192,16 +223,16 @@ public class SchedulerTest
schedule(2000,30000,2000,50); schedule(2000,30000,2000,50);
benchmark.stopStatistics(); benchmark.stopStatistics();
} }
private void schedule(int threads,final int duration, final int delay, final int interval) throws Exception private void schedule(int threads,final int duration, final int delay, final int interval) throws Exception
{ {
final Random random = new Random(1); final Random random = new Random(1);
Thread[] test = new Thread[threads]; Thread[] test = new Thread[threads];
final AtomicInteger schedules = new AtomicInteger(); final AtomicInteger schedules = new AtomicInteger();
final SampleStatistic executions = new SampleStatistic(); final SampleStatistic executions = new SampleStatistic();
final SampleStatistic cancellations = new SampleStatistic(); final SampleStatistic cancellations = new SampleStatistic();
for (int i=test.length;i-->0;) for (int i=test.length;i-->0;)
{ {
test[i]=new Thread() test[i]=new Thread()
@ -220,7 +251,7 @@ public class SchedulerTest
final long expected=now+delay; final long expected=now+delay;
int cancel=random.nextInt(interval); int cancel=random.nextInt(interval);
final boolean expected_to_execute; final boolean expected_to_execute;
last=now+2*interval>end; last=now+2*interval>end;
if (cancel==0 || last) if (cancel==0 || last)
{ {
@ -236,15 +267,15 @@ public class SchedulerTest
@Override @Override
public void run() public void run()
{ {
long lateness=System.currentTimeMillis()-expected; long lateness=System.currentTimeMillis()-expected;
if (expected_to_execute) if (expected_to_execute)
executions.set(lateness); executions.set(lateness);
else else
executions.set(6666); executions.set(6666);
} }
},delay,TimeUnit.MILLISECONDS); },delay,TimeUnit.MILLISECONDS);
Thread.sleep(cancel); Thread.sleep(cancel);
now = System.currentTimeMillis(); now = System.currentTimeMillis();
if (task.cancel()) if (task.cancel())
@ -255,7 +286,7 @@ public class SchedulerTest
else else
cancellations.set(0); cancellations.set(0);
} }
else else
{ {
if (!expected_to_execute) if (!expected_to_execute)
{ {
@ -270,17 +301,17 @@ public class SchedulerTest
{ {
e.printStackTrace(); e.printStackTrace();
} }
} }
}; };
} }
for (Thread thread : test) for (Thread thread : test)
thread.start(); thread.start();
for (Thread thread : test) for (Thread thread : test)
thread.join(); thread.join();
// System.err.println(schedules); // System.err.println(schedules);
// System.err.println(executions); // System.err.println(executions);
// System.err.println(cancellations); // System.err.println(cancellations);
@ -292,17 +323,17 @@ public class SchedulerTest
// All executed or cancelled // All executed or cancelled
// Not that SimpleScheduler can execute and cancel an event! // Not that SimpleScheduler can execute and cancel an event!
Assert.assertThat(0L+schedules.get(),Matchers.lessThanOrEqualTo(executions.getCount()+cancellations.getCount())); Assert.assertThat(0L+schedules.get(),Matchers.lessThanOrEqualTo(executions.getCount()+cancellations.getCount()));
// No really late executions // No really late executions
Assert.assertThat(executions.getMax(),Matchers.lessThan(500L)); Assert.assertThat(executions.getMax(),Matchers.lessThan(500L));
// Executions on average are close to the expected time // Executions on average are close to the expected time
Assert.assertThat(executions.getMean(),Matchers.lessThan(500.0)); Assert.assertThat(executions.getMean(),Matchers.lessThan(500.0));
// No cancellations long after expected executions // No cancellations long after expected executions
Assert.assertThat(cancellations.getMax(),Matchers.lessThan(500L)); Assert.assertThat(cancellations.getMax(),Matchers.lessThan(500L));
} }
} }