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 				prodding = true;
56 				sysTimer.prod();
57 				prodding = false;
58 				remainingTime = sysTimer.getRemainingTime();
59 			}
60 
61 			if (remainingTime == Duration.max)
62 				semaphore.wait();
63 			else
64 				semaphore.wait(remainingTime);
65 		}
66 	}
67 
68 	final class ThreadTimerEvent : TimerEvent
69 	{
70 		AppCallback fn;
71 		bool recurring;
72 		TimerTask task;
73 
74 		this(AppCallback fn, uint ms, bool recurring)
75 		{
76 			this.fn = fn;
77 			this.recurring = recurring;
78 			this.task = new TimerTask(ms.msecs, &taskCallback);
79 			synchronized(sysTimer)
80 				sysTimer.add(task);
81 		}
82 
83 		void taskCallback(SysTimer timer, TimerTask task)
84 		{
85 			if (recurring)
86 				timer.add(task);
87 			fn.call();
88 		}
89 
90 		override void cancel()
91 		{
92 			if (prodding) // cancel called from timer event handler, synchronization would cause a deadlock
93 				task.cancel();
94 			else
95 			synchronized(sysTimer)
96 				task.cancel();
97 		}
98 	}
99 }