1 module java.util.Timer; 2 3 import java.lang.all; 4 import java.util.TimerTask; 5 import java.lang.Thread; 6 7 version(Tango){ 8 import tango.core.sync.Mutex; 9 import tango.core.sync.Condition; 10 import tango.text.convert.Format; 11 } else { // Phobos 12 } 13 14 15 class Timer { 16 private static final class TaskQueue { 17 version(Tango){ 18 private Mutex mutex; 19 private Condition cond; 20 } else { // Phobos 21 } 22 23 24 private static const int DEFAULT_SIZE = 32; 25 private bool nullOnEmpty; 26 private TimerTask[] heap; 27 private int elements; 28 public this() { 29 version(Tango){ 30 mutex = new Mutex(); 31 cond = new Condition( mutex ); 32 heap = new TimerTask[DEFAULT_SIZE]; 33 elements = 0; 34 nullOnEmpty = false; 35 } else { // Phobos 36 implMissingInPhobos(); 37 } 38 } 39 40 private void add(TimerTask task) { 41 elements++; 42 if (elements is heap.length) { 43 TimerTask[] new_heap = new TimerTask[heap.length * 2]; 44 System.arraycopy(heap, 0, new_heap, 0, cast(int)heap.length); 45 heap = new_heap; 46 } 47 heap[elements] = task; 48 } 49 50 private void remove() { 51 // clear the entry first 52 heap[elements] = null; 53 elements--; 54 if (elements + DEFAULT_SIZE / 2 <= (heap.length / 4)) { 55 TimerTask[] new_heap = new TimerTask[heap.length / 2]; 56 System.arraycopy(heap, 0, new_heap, 0, elements + 1); 57 heap = new_heap; 58 } 59 } 60 61 public void enqueue(TimerTask task) { 62 version(Tango){ 63 synchronized( mutex ){ 64 if (heap is null) { 65 throw new IllegalStateException("cannot enqueue when stop() has been called on queue"); 66 } 67 68 heap[0] = task; 69 add(task); 70 int child = elements; 71 int parent = child / 2; 72 while (heap[parent].scheduled > task.scheduled) { 73 heap[child] = heap[parent]; 74 child = parent; 75 parent = child / 2; 76 } 77 heap[child] = task; 78 heap[0] = null; 79 cond.notify(); 80 } 81 } else { // Phobos 82 implMissingInPhobos(); 83 } 84 } 85 86 private TimerTask top() { 87 if (elements is 0) { 88 return null; 89 } 90 else { 91 return heap[1]; 92 } 93 } 94 95 public TimerTask serve() { 96 version(Tango){ 97 synchronized( mutex ){ 98 TimerTask task = null; 99 while (task is null) { 100 task = top(); 101 102 if ((heap is null) || (task is null && nullOnEmpty)) { 103 return null; 104 } 105 106 if (task !is null) { 107 // The time to wait until the task should be served 108 long time = task.scheduled - System.currentTimeMillis(); 109 if (time > 0) { 110 // This task should not yet be served 111 // So wait until this task is ready 112 // or something else happens to the queue 113 task = null; // set to null to make sure we call top() 114 try { 115 cond.wait(time); 116 } 117 catch (InterruptedException _) { 118 } 119 } 120 } 121 else { 122 // wait until a task is added 123 // or something else happens to the queue 124 try { 125 cond.wait(); 126 } 127 catch (InterruptedException _) { 128 } 129 } 130 } 131 132 TimerTask lastTask = heap[elements]; 133 remove(); 134 135 int parent = 1; 136 int child = 2; 137 heap[1] = lastTask; 138 while (child <= elements) { 139 if (child < elements) { 140 if (heap[child].scheduled > heap[child + 1].scheduled) { 141 child++; 142 } 143 } 144 145 if (lastTask.scheduled <= heap[child].scheduled) 146 break; 147 148 heap[parent] = heap[child]; 149 parent = child; 150 child = parent * 2; 151 } 152 153 heap[parent] = lastTask; 154 return task; 155 } 156 } else { // Phobos 157 implMissingInPhobos(); 158 return null; 159 } 160 } 161 162 public void setNullOnEmpty(bool nullOnEmpty) { 163 version(Tango){ 164 synchronized( mutex ){ 165 this.nullOnEmpty = nullOnEmpty; 166 cond.notify(); 167 } 168 } else { // Phobos 169 implMissingInPhobos(); 170 } 171 } 172 173 public void stop() { 174 version(Tango){ 175 synchronized( mutex ){ 176 this.heap = null; 177 this.elements = 0; 178 cond.notify(); 179 } 180 } else { // Phobos 181 implMissingInPhobos(); 182 } 183 } 184 185 } 186 187 private static final class Scheduler : Runnable { 188 private TaskQueue queue; 189 190 public this(TaskQueue queue) { 191 this.queue = queue; 192 } 193 194 public void run() { 195 TimerTask task; 196 while ((task = queue.serve()) !is null) { 197 if (task.scheduled >= 0) { 198 task.lastExecutionTime = task.scheduled; 199 if (task.period < 0) { 200 task.scheduled = -1; 201 } 202 try { 203 task.run(); 204 } 205 // catch (ThreadDeath death) { 206 // // If an exception escapes, the Timer becomes invalid. 207 // queue.stop(); 208 // throw death; 209 // } 210 catch (Exception t) { 211 queue.stop(); 212 } 213 } 214 if (task.scheduled >= 0) { 215 if (task.fixed) { 216 task.scheduled += task.period; 217 } 218 else { 219 task.scheduled = task.period + System.currentTimeMillis(); 220 } 221 222 try { 223 queue.enqueue(task); 224 } 225 catch (IllegalStateException ise) { 226 // Ignore. Apparently the Timer queue has been stopped. 227 } 228 } 229 } 230 } 231 } 232 233 private static int nr; 234 private TaskQueue queue; 235 private Scheduler scheduler; 236 private Thread thread; 237 private bool canceled; 238 239 public this() { 240 this(false); 241 } 242 243 public this(bool daemon) { 244 this(daemon, Thread.NORM_PRIORITY); 245 } 246 247 private this(bool daemon, int priority) { 248 this(daemon, priority, Format( "Timer-{}", ++nr)); 249 } 250 251 private this(bool daemon, int priority, String name) { 252 canceled = false; 253 queue = new TaskQueue(); 254 scheduler = new Scheduler(queue); 255 thread = new Thread(scheduler, name); 256 thread.setDaemon(daemon); 257 thread.setPriority(priority); 258 thread.start(); 259 } 260 261 public void cancel() { 262 canceled = true; 263 queue.stop(); 264 } 265 266 private void schedule(TimerTask task, long time, long period, bool fixed) { 267 if (time < 0) 268 throw new IllegalArgumentException("negative time"); 269 270 if (task.scheduled is 0 && task.lastExecutionTime is -1) { 271 task.scheduled = time; 272 task.period = period; 273 task.fixed = fixed; 274 } 275 else { 276 throw new IllegalStateException("task was already scheduled or canceled"); 277 } 278 279 if (!this.canceled && this.thread !is null) { 280 queue.enqueue(task); 281 } 282 else { 283 throw new IllegalStateException("timer was canceled or scheduler thread has died"); 284 } 285 } 286 287 private static void positiveDelay(long delay) { 288 if (delay < 0) { 289 throw new IllegalArgumentException("delay is negative"); 290 } 291 } 292 293 private static void positivePeriod(long period) { 294 if (period < 0) { 295 throw new IllegalArgumentException("period is negative"); 296 } 297 } 298 299 // public void schedule(TimerTask task, Date date) { 300 // long time = date.getTime(); 301 // schedule(task, time, -1, false); 302 // } 303 304 // public void schedule(TimerTask task, Date date, long period) { 305 // positivePeriod(period); 306 // long time = date.getTime(); 307 // schedule(task, time, period, false); 308 // } 309 310 public void schedule(TimerTask task, long delay) { 311 positiveDelay(delay); 312 long time = System.currentTimeMillis() + delay; 313 schedule(task, time, -1, false); 314 } 315 316 public void schedule(TimerTask task, long delay, long period) { 317 positiveDelay(delay); 318 positivePeriod(period); 319 long time = System.currentTimeMillis() + delay; 320 schedule(task, time, period, false); 321 } 322 323 // public void scheduleAtFixedRate(TimerTask task, Date date, long period) { 324 // positivePeriod(period); 325 // long time = date.getTime(); 326 // schedule(task, time, period, true); 327 // } 328 329 public void scheduleAtFixedRate(TimerTask task, long delay, long period) { 330 positiveDelay(delay); 331 positivePeriod(period); 332 long time = System.currentTimeMillis() + delay; 333 schedule(task, time, period, true); 334 } 335 336 protected void finalize() { 337 queue.setNullOnEmpty(true); 338 } 339 340 341 /////////////////////////////////////////////////// 342 /+ alias CircularList!( TimerTask ) ListType; 343 344 private Thread thread; 345 private ListType schedules; 346 private Mutex mutex; 347 private Condition cond; 348 private bool isCanceled = false; 349 350 this(){ 351 this(false); 352 } 353 this(bool isDaemon){ 354 mutex = new Mutex(); 355 cond = new Condition( mutex ); 356 357 schedules = new ListType(); 358 thread = new Thread( &run ); 359 thread.setDaemon( isDaemon ); 360 thread.start(); 361 } 362 private void run(){ 363 364 while( !isCanceled ){ 365 TimerTask timerTask = null; 366 synchronized(mutex){ 367 bool isReady = false; 368 do{ 369 if( isCanceled ){ 370 return; 371 } 372 373 if( schedules.size() is 0 ){ 374 cond.wait(); 375 } 376 else{ 377 timerTask = schedules.head(); 378 TimeSpan toWait = timerTask.executionTime - Clock.now(); 379 if( toWait.interval() > 0 ){ 380 cond.wait( toWait.interval() ); 381 } 382 else{ 383 schedules.removeHead(); 384 isReady = true; 385 } 386 } 387 }while( !isReady ); 388 } 389 if( timerTask ){ 390 timerTask.run(); 391 if( timerTask.period.millis > 0 ){ 392 timerTask.executionTime += timerTask.period; 393 synchronized(mutex){ 394 int index = 0; 395 foreach( tt; schedules ){ 396 if( tt.executionTime > timerTask.executionTime ){ 397 break; 398 } 399 index++; 400 } 401 schedules.addAt( index, timerTask ); 402 } 403 } 404 } 405 } 406 } 407 void cancel(){ 408 synchronized(mutex){ 409 isCanceled = true; 410 cond.notifyAll(); 411 } 412 } 413 void schedule(TimerTask task, long delay){ 414 scheduleAtFixedRate( task, delay, 0 ); 415 } 416 void scheduleAtFixedRate(TimerTask task, long delay, long period){ 417 assert( task ); 418 version(TANGOSVN){ 419 task.executionTime = Clock.now + TimeSpan.fromMillis(delay); 420 } else { 421 task.executionTime = Clock.now + TimeSpan.millis(delay); 422 } 423 task.timer = this; 424 synchronized(mutex){ 425 int index = 0; 426 if( schedules.size() > 0 ) 427 foreach( tt; schedules ){ 428 if( tt.executionTime > task.executionTime ){ 429 break; 430 } 431 index++; 432 } 433 schedules.addAt( index, task ); 434 cond.notifyAll(); 435 } 436 } 437 438 // void schedule(TimerTask task, Date time){} 439 // void schedule(TimerTask task, Date firstTime, long period){} 440 // void schedule(TimerTask task, long delay, long period){} 441 // void scheduleAtFixedRate(TimerTask task, Date firstTime, long period){} 442 +/ 443 } 444 445