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