// ========================================================================
// Copyright (c) 2004-2009 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
package org.eclipse.jetty.util.thread;
import static org.junit.Assert.assertEquals;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicIntegerArray;
import org.junit.Before;
import org.junit.Test;
public class TimeoutTest
{
Object lock = new Object();
Timeout timeout = new Timeout(null);
Timeout.Task[] tasks;
/* ------------------------------------------------------------ */
/*
* @see junit.framework.TestCase#setUp()
*/
@Before
public void setUp() throws Exception
{
timeout=new Timeout(lock);
tasks= new Timeout.Task[10];
for (int i=0;i<tasks.length;i++)
{
tasks[i]=new Timeout.Task();
timeout.setNow(1000+i*100);
timeout.schedule(tasks[i]);
}
timeout.setNow(100);
}
/* ------------------------------------------------------------ */
@Test
public void testExpiry()
{
timeout.setDuration(200);
timeout.setNow(1500);
timeout.tick();
for (int i=0;i<tasks.length;i++)
{
assertEquals("isExpired "+i,i<4, tasks[i].isExpired());
}
}
/* ------------------------------------------------------------ */
@Test
public void testCancel()
{
timeout.setDuration(200);
timeout.setNow(1700);
for (int i=0;i<tasks.length;i++)
if (i%2==1)
tasks[i].cancel();
timeout.tick();
for (int i=0;i<tasks.length;i++)
{
assertEquals("isExpired "+i,i%2==0 && i<6, tasks[i].isExpired());
}
}
/* ------------------------------------------------------------ */
@Test
public void testTouch()
{
timeout.setDuration(200);
timeout.setNow(1350);
timeout.schedule(tasks[2]);
timeout.setNow(1500);
timeout.tick();
for (int i=0;i<tasks.length;i++)
{
assertEquals("isExpired "+i,i!=2 && i<4, tasks[i].isExpired());
}
timeout.setNow(1550);
timeout.tick();
for (int i=0;i<tasks.length;i++)
{
assertEquals("isExpired "+i, i<4, tasks[i].isExpired());
}
}
/* ------------------------------------------------------------ */
@Test
public void testDelay()
{
Timeout.Task task = new Timeout.Task();
timeout.setNow(1100);
timeout.schedule(task, 300);
timeout.setDuration(200);
timeout.setNow(1300);
timeout.tick();
assertEquals("delay", false, task.isExpired());
timeout.setNow(1500);
timeout.tick();
assertEquals("delay", false, task.isExpired());
timeout.setNow(1700);
timeout.tick();
assertEquals("delay", true, task.isExpired());
}
/* ------------------------------------------------------------ */
@Test
public void testStress() throws Exception
{
final int LOOP=250;
final AtomicBoolean running=new AtomicBoolean(true);
final AtomicIntegerArray count = new AtomicIntegerArray( 4 );
timeout.setNow(System.currentTimeMillis());
timeout.setDuration(500);
// Start a ticker thread that will tick over the timer frequently.
Thread ticker = new Thread()
{
@Override
public void run()
{
while (running.get())
{
try
{
// use lock.wait so we have a memory barrier and
// have no funny optimisation issues.
synchronized (lock)
{
lock.wait(30);
}
Thread.sleep(30);
timeout.tick(System.currentTimeMillis());
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
};
ticker.start();
// start lots of test threads
for (int i=0;i<LOOP;i++)
{
//
Thread th = new Thread()
{
@Override
public void run()
{
// count how many threads were started (should == LOOP)
int once = (int) 10 + count.incrementAndGet( 0 )%50;
// create a task for this thread
Timeout.Task task = new Timeout.Task()
{
@Override
public void expired()
{
// count the number of expires
count.incrementAndGet( 2 );
}
};
// this thread will loop and each loop with schedule a
// task with a delay on top of the timeouts duration
// mostly this thread will then cancel the task
// But once it will wait and the task will expire
// do the looping until we are stopped
int loop=0;
while (running.get())
{
try
{
long delay=1000;
long wait=100-once;
if (loop++==once)
{
// THIS loop is the one time we wait longer than the delay
count.incrementAndGet( 1 );
delay=200;
wait=1000;
}
timeout.schedule(task,delay);
// do the wait
Thread.sleep(wait);
// cancel task (which may have expired)
task.cancel();
}
catch(Exception e)
{
e.printStackTrace();
}
}
count.incrementAndGet(3);
}
};
th.start();
}
long start=System.currentTimeMillis();
// run test until all threads are started
while (count.get(0)<LOOP && (System.currentTimeMillis()-start)<20000)
Thread.sleep(50);
// run test until all expires initiated
while (count.get(1)<LOOP && (System.currentTimeMillis()-start)<20000)
Thread.sleep(50);
// run test until all expires initiated
while (count.get(2)<LOOP && (System.currentTimeMillis()-start)<20000)
Thread.sleep(50);
running.set(false);
// run test until all threads complete
while (count.get(3)<LOOP && (System.currentTimeMillis()-start)<20000)
Thread.sleep(50);
// check the counts
assertEquals("count threads", LOOP,count.get( 0 ));
assertEquals("count once waits",LOOP,count.get(1 ));
assertEquals("count expires",LOOP,count.get(2));
assertEquals("done",LOOP,count.get(3));
}
}