• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 //  ========================================================================
3 //  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4 //  ------------------------------------------------------------------------
5 //  All rights reserved. This program and the accompanying materials
6 //  are made available under the terms of the Eclipse Public License v1.0
7 //  and Apache License v2.0 which accompanies this distribution.
8 //
9 //      The Eclipse Public License is available at
10 //      http://www.eclipse.org/legal/epl-v10.html
11 //
12 //      The Apache License v2.0 is available at
13 //      http://www.opensource.org/licenses/apache2.0.php
14 //
15 //  You may elect to redistribute this code under either of these licenses.
16 //  ========================================================================
17 //
18 
19 package org.eclipse.jetty.util.thread;
20 
21 import org.eclipse.jetty.util.log.Log;
22 import org.eclipse.jetty.util.log.Logger;
23 
24 
25 /* ------------------------------------------------------------ */
26 /** Timeout queue.
27  * This class implements a timeout queue for timers that are at least as likely to be cancelled as they are to expire.
28  * Unlike the util timeout class, the duration of the timeouts is shared by all scheduled tasks and if the duration
29  * is changed, this affects all scheduled tasks.
30  * <p>
31  * The nested class Task should be extended by users of this class to obtain call back notification of
32  * expires.
33  */
34 public class Timeout
35 {
36     private static final Logger LOG = Log.getLogger(Timeout.class);
37     private Object _lock;
38     private long _duration;
39     private volatile long _now=System.currentTimeMillis();
40     private Task _head=new Task();
41 
42     /* ------------------------------------------------------------ */
Timeout()43     public Timeout()
44     {
45         _lock=new Object();
46         _head._timeout=this;
47     }
48 
49     /* ------------------------------------------------------------ */
Timeout(Object lock)50     public Timeout(Object lock)
51     {
52         _lock=lock;
53         _head._timeout=this;
54     }
55 
56     /* ------------------------------------------------------------ */
57     /**
58      * @return Returns the duration.
59      */
getDuration()60     public long getDuration()
61     {
62         return _duration;
63     }
64 
65     /* ------------------------------------------------------------ */
66     /**
67      * @param duration The duration to set.
68      */
setDuration(long duration)69     public void setDuration(long duration)
70     {
71         _duration = duration;
72     }
73 
74     /* ------------------------------------------------------------ */
setNow()75     public long setNow()
76     {
77         return _now=System.currentTimeMillis();
78     }
79 
80     /* ------------------------------------------------------------ */
getNow()81     public long getNow()
82     {
83         return _now;
84     }
85 
86     /* ------------------------------------------------------------ */
setNow(long now)87     public void setNow(long now)
88     {
89         _now=now;
90     }
91 
92     /* ------------------------------------------------------------ */
93     /** Get an expired tasks.
94      * This is called instead of {@link #tick()} to obtain the next
95      * expired Task, but without calling it's {@link Task#expire()} or
96      * {@link Task#expired()} methods.
97      *
98      * @return the next expired task or null.
99      */
expired()100     public Task expired()
101     {
102         synchronized (_lock)
103         {
104             long _expiry = _now-_duration;
105 
106             if (_head._next!=_head)
107             {
108                 Task task = _head._next;
109                 if (task._timestamp>_expiry)
110                     return null;
111 
112                 task.unlink();
113                 task._expired=true;
114                 return task;
115             }
116             return null;
117         }
118     }
119 
120     /* ------------------------------------------------------------ */
tick()121     public void tick()
122     {
123         final long expiry = _now-_duration;
124 
125         Task task=null;
126         while (true)
127         {
128             try
129             {
130                 synchronized (_lock)
131                 {
132                     task= _head._next;
133                     if (task==_head || task._timestamp>expiry)
134                         break;
135                     task.unlink();
136                     task._expired=true;
137                     task.expire();
138                 }
139 
140                 task.expired();
141             }
142             catch(Throwable th)
143             {
144                 LOG.warn(Log.EXCEPTION,th);
145             }
146         }
147     }
148 
149     /* ------------------------------------------------------------ */
tick(long now)150     public void tick(long now)
151     {
152         _now=now;
153         tick();
154     }
155 
156     /* ------------------------------------------------------------ */
schedule(Task task)157     public void schedule(Task task)
158     {
159         schedule(task,0L);
160     }
161 
162     /* ------------------------------------------------------------ */
163     /**
164      * @param task
165      * @param delay A delay in addition to the default duration of the timeout
166      */
schedule(Task task,long delay)167     public void schedule(Task task,long delay)
168     {
169         synchronized (_lock)
170         {
171             if (task._timestamp!=0)
172             {
173                 task.unlink();
174                 task._timestamp=0;
175             }
176             task._timeout=this;
177             task._expired=false;
178             task._delay=delay;
179             task._timestamp = _now+delay;
180 
181             Task last=_head._prev;
182             while (last!=_head)
183             {
184                 if (last._timestamp <= task._timestamp)
185                     break;
186                 last=last._prev;
187             }
188             last.link(task);
189         }
190     }
191 
192 
193     /* ------------------------------------------------------------ */
cancelAll()194     public void cancelAll()
195     {
196         synchronized (_lock)
197         {
198             _head._next=_head._prev=_head;
199         }
200     }
201 
202     /* ------------------------------------------------------------ */
isEmpty()203     public boolean isEmpty()
204     {
205         synchronized (_lock)
206         {
207             return _head._next==_head;
208         }
209     }
210 
211     /* ------------------------------------------------------------ */
getTimeToNext()212     public long getTimeToNext()
213     {
214         synchronized (_lock)
215         {
216             if (_head._next==_head)
217                 return -1;
218             long to_next = _duration+_head._next._timestamp-_now;
219             return to_next<0?0:to_next;
220         }
221     }
222 
223     /* ------------------------------------------------------------ */
224     @Override
toString()225     public String toString()
226     {
227         StringBuffer buf = new StringBuffer();
228         buf.append(super.toString());
229 
230         Task task = _head._next;
231         while (task!=_head)
232         {
233             buf.append("-->");
234             buf.append(task);
235             task=task._next;
236         }
237 
238         return buf.toString();
239     }
240 
241     /* ------------------------------------------------------------ */
242     /* ------------------------------------------------------------ */
243     /* ------------------------------------------------------------ */
244     /* ------------------------------------------------------------ */
245     /** Task.
246      * The base class for scheduled timeouts.  This class should be
247      * extended to implement the expire() method, which is called if the
248      * timeout expires.
249      *
250      *
251      *
252      */
253     public static class Task
254     {
255         Task _next;
256         Task _prev;
257         Timeout _timeout;
258         long _delay;
259         long _timestamp=0;
260         boolean _expired=false;
261 
262         /* ------------------------------------------------------------ */
Task()263         protected Task()
264         {
265             _next=_prev=this;
266         }
267 
268         /* ------------------------------------------------------------ */
getTimestamp()269         public long getTimestamp()
270         {
271             return _timestamp;
272         }
273 
274         /* ------------------------------------------------------------ */
getAge()275         public long getAge()
276         {
277             final Timeout t = _timeout;
278             if (t!=null)
279             {
280                 final long now=t._now;
281                 if (now!=0 && _timestamp!=0)
282                     return now-_timestamp;
283             }
284             return 0;
285         }
286 
287         /* ------------------------------------------------------------ */
unlink()288         private void unlink()
289         {
290             _next._prev=_prev;
291             _prev._next=_next;
292             _next=_prev=this;
293             _expired=false;
294         }
295 
296         /* ------------------------------------------------------------ */
link(Task task)297         private void link(Task task)
298         {
299             Task next_next = _next;
300             _next._prev=task;
301             _next=task;
302             _next._next=next_next;
303             _next._prev=this;
304         }
305 
306         /* ------------------------------------------------------------ */
307         /** Schedule the task on the given timeout.
308          * The task exiry will be called after the timeout duration.
309          * @param timer
310          */
schedule(Timeout timer)311         public void schedule(Timeout timer)
312         {
313             timer.schedule(this);
314         }
315 
316         /* ------------------------------------------------------------ */
317         /** Schedule the task on the given timeout.
318          * The task exiry will be called after the timeout duration.
319          * @param timer
320          */
schedule(Timeout timer, long delay)321         public void schedule(Timeout timer, long delay)
322         {
323             timer.schedule(this,delay);
324         }
325 
326         /* ------------------------------------------------------------ */
327         /** Reschedule the task on the current timeout.
328          * The task timeout is rescheduled as if it had been cancelled and
329          * scheduled on the current timeout.
330          */
reschedule()331         public void reschedule()
332         {
333             Timeout timeout = _timeout;
334             if (timeout!=null)
335                 timeout.schedule(this,_delay);
336         }
337 
338         /* ------------------------------------------------------------ */
339         /** Cancel the task.
340          * Remove the task from the timeout.
341          */
cancel()342         public void cancel()
343         {
344             Timeout timeout = _timeout;
345             if (timeout!=null)
346             {
347                 synchronized (timeout._lock)
348                 {
349                     unlink();
350                     _timestamp=0;
351                 }
352             }
353         }
354 
355         /* ------------------------------------------------------------ */
isExpired()356         public boolean isExpired() { return _expired; }
357 
358         /* ------------------------------------------------------------ */
isScheduled()359 	public boolean isScheduled() { return _next!=this; }
360 
361         /* ------------------------------------------------------------ */
362         /** Expire task.
363          * This method is called when the timeout expires. It is called
364          * in the scope of the synchronize block (on this) that sets
365          * the {@link #isExpired()} state to true.
366          * @see #expired() For an unsynchronized callback.
367          */
expire()368         protected void expire(){}
369 
370         /* ------------------------------------------------------------ */
371         /** Expire task.
372          * This method is called when the timeout expires. It is called
373          * outside of any synchronization scope and may be delayed.
374          *
375          */
expired()376         public void expired(){}
377 
378     }
379 
380 }
381