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 }