View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 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      /* ------------------------------------------------------------ */
43      public Timeout()
44      {
45          _lock=new Object();
46          _head._timeout=this;
47      }
48  
49      /* ------------------------------------------------------------ */
50      public Timeout(Object lock)
51      {
52          _lock=lock;
53          _head._timeout=this;
54      }
55  
56      /* ------------------------------------------------------------ */
57      /**
58       * @return Returns the duration.
59       */
60      public long getDuration()
61      {
62          return _duration;
63      }
64  
65      /* ------------------------------------------------------------ */
66      /**
67       * @param duration The duration to set.
68       */
69      public void setDuration(long duration)
70      {
71          _duration = duration;
72      }
73  
74      /* ------------------------------------------------------------ */
75      public long setNow()
76      {
77          return _now=System.currentTimeMillis();
78      }
79      
80      /* ------------------------------------------------------------ */
81      public long getNow()
82      {
83          return _now;
84      }
85  
86      /* ------------------------------------------------------------ */
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       */
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     /* ------------------------------------------------------------ */
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     /* ------------------------------------------------------------ */
150     public void tick(long now)
151     {
152         _now=now;
153         tick();
154     }
155 
156     /* ------------------------------------------------------------ */
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      */
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     /* ------------------------------------------------------------ */
194     public void cancelAll()
195     {
196         synchronized (_lock)
197         {
198             _head._next=_head._prev=_head;
199         }
200     }
201 
202     /* ------------------------------------------------------------ */
203     public boolean isEmpty()
204     {
205         synchronized (_lock)
206         {
207             return _head._next==_head;
208         }
209     }
210 
211     /* ------------------------------------------------------------ */
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
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         /* ------------------------------------------------------------ */
263         protected Task()
264         {
265             _next=_prev=this;
266         }
267 
268         /* ------------------------------------------------------------ */
269         public long getTimestamp()
270         {
271             return _timestamp;
272         }
273 
274         /* ------------------------------------------------------------ */
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         /* ------------------------------------------------------------ */
288         private void unlink()
289         {
290             _next._prev=_prev;
291             _prev._next=_next;
292             _next=_prev=this;
293             _expired=false;
294         }
295 
296         /* ------------------------------------------------------------ */
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          */
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          */
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          */
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          */
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         /* ------------------------------------------------------------ */
356         public boolean isExpired() { return _expired; }
357 
358         /* ------------------------------------------------------------ */
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          */
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          */
376         public void expired(){}
377 
378     }
379 
380 }