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