| // |
| // ======================================================================== |
| // Copyright (c) 1995-2014 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. |
| */ |
| 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(){} |
| |
| } |
| |
| } |