1 /** 2 * ae.ui.timer.thread.timer 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.ui.timer.thread.timer; 15 16 import core.thread; 17 import core.sync.semaphore; 18 19 public import ae.ui.timer.timer; 20 import ae.ui.app.application; 21 import ae.sys.timing; 22 23 private alias ae.sys.timing.Timer SysTimer; 24 private alias ae.ui.timer.timer.Timer Timer; 25 26 /// A simple thread-based `Timer` implementation. 27 final class ThreadTimer : Timer 28 { 29 this() 30 { 31 sysTimer = new SysTimer; 32 semaphore = new Semaphore; 33 auto thread = new Thread(&threadProc); 34 thread.isDaemon = true; 35 thread.start(); 36 } /// 37 38 protected: 39 SysTimer sysTimer; 40 Semaphore semaphore; 41 shared bool prodding; 42 43 override TimerEvent setTimeout (AppCallback fn, uint ms) { return new ThreadTimerEvent(fn, ms, false); } 44 override TimerEvent setInterval(AppCallback fn, uint ms) { return new ThreadTimerEvent(fn, ms, true ); } 45 46 private: 47 void threadProc() 48 { 49 while (true) 50 { 51 Duration remainingTime; 52 53 synchronized(sysTimer) 54 { 55 auto now = MonoTime.currTime(); 56 prodding = true; 57 sysTimer.prod(now); 58 prodding = false; 59 60 now = MonoTime.currTime(); 61 remainingTime = sysTimer.getRemainingTime(now); 62 } 63 64 if (remainingTime == Duration.max) 65 semaphore.wait(); 66 else 67 semaphore.wait(remainingTime); 68 } 69 } 70 71 final class ThreadTimerEvent : TimerEvent 72 { 73 AppCallback fn; 74 bool recurring; 75 TimerTask task; 76 uint ms; 77 78 this(AppCallback fn, uint ms, bool recurring) 79 { 80 auto now = MonoTime.currTime(); 81 this.fn = fn; 82 this.ms = ms; 83 this.recurring = recurring; 84 this.task = new TimerTask(&taskCallback); 85 synchronized(sysTimer) 86 sysTimer.add(task, now + ms.msecs); 87 } 88 89 void taskCallback(SysTimer timer, TimerTask task) 90 { 91 if (recurring) 92 { 93 auto now = MonoTime.currTime(); 94 timer.add(task, now + ms.msecs); 95 } 96 fn.call(); 97 } 98 99 override void cancel() 100 { 101 if (prodding) // cancel called from timer event handler, synchronization would cause a deadlock 102 task.cancel(); 103 else 104 synchronized(sysTimer) 105 task.cancel(); 106 } 107 } 108 }