View Javadoc

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