Deleted class org.eclipse.jetty.util.thread.Timeout, replacing its
usages with o.e.j.u.t.Scheduler.
This commit is contained in:
parent
c1832b29fb
commit
591cf9badf
|
@ -56,7 +56,8 @@ import org.eclipse.jetty.util.annotation.ManagedOperation;
|
|||
import org.eclipse.jetty.util.annotation.Name;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Timeout;
|
||||
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
|
||||
/**
|
||||
* Denial of Service filter
|
||||
|
@ -184,12 +185,9 @@ public class DoSFilter implements Filter
|
|||
private ContinuationListener[] _listeners;
|
||||
private final ConcurrentHashMap<String, RateTracker> _rateTrackers = new ConcurrentHashMap<>();
|
||||
private final List<String> _whitelist = new CopyOnWriteArrayList<>();
|
||||
private final Timeout _requestTimeoutQ = new Timeout();
|
||||
private final Timeout _trackerTimeoutQ = new Timeout();
|
||||
private Thread _timerThread;
|
||||
private volatile boolean _running;
|
||||
private Scheduler _scheduler;
|
||||
|
||||
public void init(FilterConfig filterConfig)
|
||||
public void init(FilterConfig filterConfig) throws ServletException
|
||||
{
|
||||
_context = filterConfig.getServletContext();
|
||||
|
||||
|
@ -275,47 +273,26 @@ public class DoSFilter implements Filter
|
|||
parameter = filterConfig.getInitParameter(ENABLED_INIT_PARAM);
|
||||
setEnabled(parameter == null || Boolean.parseBoolean(parameter));
|
||||
|
||||
_requestTimeoutQ.setNow();
|
||||
_requestTimeoutQ.setDuration(_maxRequestMs);
|
||||
|
||||
_trackerTimeoutQ.setNow();
|
||||
_trackerTimeoutQ.setDuration(_maxIdleTrackerMs);
|
||||
|
||||
_running = true;
|
||||
_timerThread = (new Thread()
|
||||
{
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
while (_running)
|
||||
{
|
||||
long now = _requestTimeoutQ.setNow();
|
||||
_requestTimeoutQ.tick();
|
||||
_trackerTimeoutQ.setNow(now);
|
||||
_trackerTimeoutQ.tick();
|
||||
try
|
||||
{
|
||||
Thread.sleep(100);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
LOG.ignore(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
LOG.debug("DoSFilter timer exited");
|
||||
}
|
||||
}
|
||||
});
|
||||
_timerThread.start();
|
||||
_scheduler = startScheduler();
|
||||
|
||||
if (_context != null && Boolean.parseBoolean(filterConfig.getInitParameter(MANAGED_ATTR_INIT_PARAM)))
|
||||
_context.setAttribute(filterConfig.getFilterName(), this);
|
||||
}
|
||||
|
||||
protected Scheduler startScheduler() throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
Scheduler result = new ScheduledExecutorScheduler();
|
||||
result.start();
|
||||
return result;
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException
|
||||
{
|
||||
doFilter((HttpServletRequest)request, (HttpServletResponse)response, filterChain);
|
||||
|
@ -329,8 +306,6 @@ public class DoSFilter implements Filter
|
|||
return;
|
||||
}
|
||||
|
||||
final long now = _requestTimeoutQ.getNow();
|
||||
|
||||
// Look for the rate tracker for this request
|
||||
RateTracker tracker = (RateTracker)request.getAttribute(__TRACKER);
|
||||
|
||||
|
@ -342,7 +317,7 @@ public class DoSFilter implements Filter
|
|||
tracker = getRateTracker(request);
|
||||
|
||||
// Calculate the rate and check it is over the allowed limit
|
||||
final boolean overRateLimit = tracker.isRateExceeded(now);
|
||||
final boolean overRateLimit = tracker.isRateExceeded(System.currentTimeMillis());
|
||||
|
||||
// pass it through if we are not currently over the rate limit
|
||||
if (!overRateLimit)
|
||||
|
@ -466,23 +441,23 @@ public class DoSFilter implements Filter
|
|||
protected void doFilterChain(FilterChain chain, final HttpServletRequest request, final HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
final Thread thread = Thread.currentThread();
|
||||
|
||||
final Timeout.Task requestTimeout = new Timeout.Task()
|
||||
Runnable requestTimeout = new Runnable()
|
||||
{
|
||||
public void expired()
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
closeConnection(request, response, thread);
|
||||
}
|
||||
};
|
||||
|
||||
Scheduler.Task task = _scheduler.schedule(requestTimeout, getMaxRequestMs(), TimeUnit.MILLISECONDS);
|
||||
try
|
||||
{
|
||||
_requestTimeoutQ.schedule(requestTimeout);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
finally
|
||||
{
|
||||
requestTimeout.cancel();
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -573,14 +548,14 @@ public class DoSFilter implements Filter
|
|||
}
|
||||
else
|
||||
{
|
||||
if (_trackSessions && session != null && !session.isNew())
|
||||
if (isTrackSessions() && session != null && !session.isNew())
|
||||
{
|
||||
loadId = session.getId();
|
||||
type = USER_SESSION;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadId = _remotePort ? (request.getRemoteAddr() + request.getRemotePort()) : request.getRemoteAddr();
|
||||
loadId = isRemotePort() ? (request.getRemoteAddr() + request.getRemotePort()) : request.getRemoteAddr();
|
||||
type = USER_IP;
|
||||
}
|
||||
}
|
||||
|
@ -590,16 +565,17 @@ public class DoSFilter implements Filter
|
|||
if (tracker == null)
|
||||
{
|
||||
boolean allowed = checkWhitelist(_whitelist, request.getRemoteAddr());
|
||||
tracker = allowed ? new FixedRateTracker(loadId, type, _maxRequestsPerSec)
|
||||
: new RateTracker(loadId, type, _maxRequestsPerSec);
|
||||
int maxRequestsPerSec = getMaxRequestsPerSec();
|
||||
tracker = allowed ? new FixedRateTracker(loadId, type, maxRequestsPerSec)
|
||||
: new RateTracker(loadId, type, maxRequestsPerSec);
|
||||
RateTracker existing = _rateTrackers.putIfAbsent(loadId, tracker);
|
||||
if (existing != null)
|
||||
tracker = existing;
|
||||
|
||||
if (type == USER_IP)
|
||||
{
|
||||
// USER_IP expiration from _rateTrackers is handled by the _trackerTimeoutQ
|
||||
_trackerTimeoutQ.schedule(tracker);
|
||||
// USER_IP expiration from _rateTrackers is handled by the _scheduler
|
||||
_scheduler.schedule(tracker, getMaxIdleTrackerMs(), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
else if (session != null)
|
||||
{
|
||||
|
@ -722,14 +698,23 @@ public class DoSFilter implements Filter
|
|||
public void destroy()
|
||||
{
|
||||
LOG.debug("Destroy {}",this);
|
||||
_running = false;
|
||||
_timerThread.interrupt();
|
||||
_requestTimeoutQ.cancelAll();
|
||||
_trackerTimeoutQ.cancelAll();
|
||||
stopScheduler();
|
||||
_rateTrackers.clear();
|
||||
_whitelist.clear();
|
||||
}
|
||||
|
||||
protected void stopScheduler()
|
||||
{
|
||||
try
|
||||
{
|
||||
_scheduler.stop();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
LOG.ignore(x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user id, used to track this connection.
|
||||
* This SHOULD be overridden by subclasses.
|
||||
|
@ -1067,7 +1052,7 @@ public class DoSFilter implements Filter
|
|||
* A RateTracker is associated with a connection, and stores request rate
|
||||
* data.
|
||||
*/
|
||||
class RateTracker extends Timeout.Task implements HttpSessionBindingListener, HttpSessionActivationListener, Serializable
|
||||
class RateTracker implements Runnable, HttpSessionBindingListener, HttpSessionActivationListener, Serializable
|
||||
{
|
||||
private static final long serialVersionUID = 3534663738034577872L;
|
||||
|
||||
|
@ -1138,15 +1123,15 @@ public class DoSFilter implements Filter
|
|||
LOG.warn("Unexpected session activation");
|
||||
}
|
||||
|
||||
public void expired()
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
long now = _trackerTimeoutQ.getNow();
|
||||
int latestIndex = _next == 0 ? (_timestamps.length - 1) : (_next - 1);
|
||||
long last = _timestamps[latestIndex];
|
||||
boolean hasRecentRequest = last != 0 && (now - last) < 1000L;
|
||||
boolean hasRecentRequest = last != 0 && (System.currentTimeMillis() - last) < 1000L;
|
||||
|
||||
if (hasRecentRequest)
|
||||
reschedule();
|
||||
_scheduler.schedule(this, getMaxIdleTrackerMs(), TimeUnit.MILLISECONDS);
|
||||
else
|
||||
_rateTrackers.remove(_id);
|
||||
}
|
||||
|
|
|
@ -1,381 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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 org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Timeout queue.
|
||||
* This class implements a timeout queue for timers that are at least as likely to be cancelled as they are to expire.
|
||||
* Unlike the util timeout class, the duration of the timeouts is shared by all scheduled tasks and if the duration
|
||||
* is changed, this affects all scheduled tasks.
|
||||
* <p>
|
||||
* The nested class Task should be extended by users of this class to obtain call back notification of
|
||||
* expires.
|
||||
*/
|
||||
@Deprecated
|
||||
public class Timeout
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(Timeout.class);
|
||||
private Object _lock;
|
||||
private long _duration;
|
||||
private volatile long _now=System.currentTimeMillis();
|
||||
private Task _head=new Task();
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Timeout()
|
||||
{
|
||||
_lock=new Object();
|
||||
_head._timeout=this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public Timeout(Object lock)
|
||||
{
|
||||
_lock=lock;
|
||||
_head._timeout=this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @return Returns the duration.
|
||||
*/
|
||||
public long getDuration()
|
||||
{
|
||||
return _duration;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param duration The duration to set.
|
||||
*/
|
||||
public void setDuration(long duration)
|
||||
{
|
||||
_duration = duration;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long setNow()
|
||||
{
|
||||
return _now=System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getNow()
|
||||
{
|
||||
return _now;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void setNow(long now)
|
||||
{
|
||||
_now=now;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Get an expired tasks.
|
||||
* This is called instead of {@link #tick()} to obtain the next
|
||||
* expired Task, but without calling it's {@link Task#expire()} or
|
||||
* {@link Task#expired()} methods.
|
||||
*
|
||||
* @return the next expired task or null.
|
||||
*/
|
||||
public Task expired()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
long _expiry = _now-_duration;
|
||||
|
||||
if (_head._next!=_head)
|
||||
{
|
||||
Task task = _head._next;
|
||||
if (task._timestamp>_expiry)
|
||||
return null;
|
||||
|
||||
task.unlink();
|
||||
task._expired=true;
|
||||
return task;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void tick()
|
||||
{
|
||||
final long expiry = _now-_duration;
|
||||
|
||||
Task task=null;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
task= _head._next;
|
||||
if (task==_head || task._timestamp>expiry)
|
||||
break;
|
||||
task.unlink();
|
||||
task._expired=true;
|
||||
task.expire();
|
||||
}
|
||||
|
||||
task.expired();
|
||||
}
|
||||
catch(Throwable th)
|
||||
{
|
||||
LOG.warn(Log.EXCEPTION,th);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void tick(long now)
|
||||
{
|
||||
_now=now;
|
||||
tick();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void schedule(Task task)
|
||||
{
|
||||
schedule(task,0L);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
* @param task
|
||||
* @param delay A delay in addition to the default duration of the timeout
|
||||
*/
|
||||
public void schedule(Task task,long delay)
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (task._timestamp!=0)
|
||||
{
|
||||
task.unlink();
|
||||
task._timestamp=0;
|
||||
}
|
||||
task._timeout=this;
|
||||
task._expired=false;
|
||||
task._delay=delay;
|
||||
task._timestamp = _now+delay;
|
||||
|
||||
Task last=_head._prev;
|
||||
while (last!=_head)
|
||||
{
|
||||
if (last._timestamp <= task._timestamp)
|
||||
break;
|
||||
last=last._prev;
|
||||
}
|
||||
last.link(task);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public void cancelAll()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
_head._next=_head._prev=_head;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isEmpty()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
return _head._next==_head;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getTimeToNext()
|
||||
{
|
||||
synchronized (_lock)
|
||||
{
|
||||
if (_head._next==_head)
|
||||
return -1;
|
||||
long to_next = _duration+_head._next._timestamp-_now;
|
||||
return to_next<0?0:to_next;
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buf = new StringBuffer();
|
||||
buf.append(super.toString());
|
||||
|
||||
Task task = _head._next;
|
||||
while (task!=_head)
|
||||
{
|
||||
buf.append("-->");
|
||||
buf.append(task);
|
||||
task=task._next;
|
||||
}
|
||||
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Task.
|
||||
* The base class for scheduled timeouts. This class should be
|
||||
* extended to implement the expire() method, which is called if the
|
||||
* timeout expires.
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static class Task
|
||||
{
|
||||
Task _next;
|
||||
Task _prev;
|
||||
Timeout _timeout;
|
||||
long _delay;
|
||||
long _timestamp=0;
|
||||
boolean _expired=false;
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
protected Task()
|
||||
{
|
||||
_next=_prev=this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getTimestamp()
|
||||
{
|
||||
return _timestamp;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public long getAge()
|
||||
{
|
||||
final Timeout t = _timeout;
|
||||
if (t!=null)
|
||||
{
|
||||
final long now=t._now;
|
||||
if (now!=0 && _timestamp!=0)
|
||||
return now-_timestamp;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void unlink()
|
||||
{
|
||||
_next._prev=_prev;
|
||||
_prev._next=_next;
|
||||
_next=_prev=this;
|
||||
_expired=false;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
private void link(Task task)
|
||||
{
|
||||
Task next_next = _next;
|
||||
_next._prev=task;
|
||||
_next=task;
|
||||
_next._next=next_next;
|
||||
_next._prev=this;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Schedule the task on the given timeout.
|
||||
* The task exiry will be called after the timeout duration.
|
||||
* @param timer
|
||||
*/
|
||||
public void schedule(Timeout timer)
|
||||
{
|
||||
timer.schedule(this);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Schedule the task on the given timeout.
|
||||
* The task exiry will be called after the timeout duration.
|
||||
* @param timer
|
||||
*/
|
||||
public void schedule(Timeout timer, long delay)
|
||||
{
|
||||
timer.schedule(this,delay);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Reschedule the task on the current timeout.
|
||||
* The task timeout is rescheduled as if it had been cancelled and
|
||||
* scheduled on the current timeout.
|
||||
*/
|
||||
public void reschedule()
|
||||
{
|
||||
Timeout timeout = _timeout;
|
||||
if (timeout!=null)
|
||||
timeout.schedule(this,_delay);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Cancel the task.
|
||||
* Remove the task from the timeout.
|
||||
*/
|
||||
public void cancel()
|
||||
{
|
||||
Timeout timeout = _timeout;
|
||||
if (timeout!=null)
|
||||
{
|
||||
synchronized (timeout._lock)
|
||||
{
|
||||
unlink();
|
||||
_timestamp=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isExpired() { return _expired; }
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public boolean isScheduled() { return _next!=this; }
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Expire task.
|
||||
* This method is called when the timeout expires. It is called
|
||||
* in the scope of the synchronize block (on this) that sets
|
||||
* the {@link #isExpired()} state to true.
|
||||
* @see #expired() For an unsynchronized callback.
|
||||
*/
|
||||
protected void expire(){}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/** Expire task.
|
||||
* This method is called when the timeout expires. It is called
|
||||
* outside of any synchronization scope and may be delayed.
|
||||
*
|
||||
*/
|
||||
public void expired(){}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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
|
||||
{
|
||||
private boolean _stress=Boolean.getBoolean("STRESS");
|
||||
|
||||
Object lock = new Object();
|
||||
Timeout timeout = new Timeout(null);
|
||||
Timeout.Task[] tasks;
|
||||
|
||||
@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+1)%2==0)
|
||||
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
|
||||
{
|
||||
if ( !_stress )
|
||||
return;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue