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 <vladimir@thecybershadow.net>
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 alias ae.sys.timing.Timer SysTimer;
24 alias ae.ui.timer.timer.Timer Timer;
25 
26 final class ThreadTimer : Timer
27 {
28     SysTimer sysTimer;
29     Semaphore semaphore;
30     shared bool prodding;
31 
32     this()
33     {
34     	sysTimer = new SysTimer;
35     	semaphore = new Semaphore;
36 		auto thread = new Thread(&threadProc);
37 		thread.isDaemon = true;
38 		thread.start();
39     }
40 
41 	override TimerEvent setTimeout (AppCallback fn, uint ms) { return new ThreadTimerEvent(fn, ms, false); }
42 	override TimerEvent setInterval(AppCallback fn, uint ms) { return new ThreadTimerEvent(fn, ms, true ); }
43 
44 private:
45 	void threadProc()
46 	{
47 		while (true)
48 		{
49 			Duration remainingTime;
50 
51 			synchronized(sysTimer)
52 			{
53 				prodding = true;
54 				sysTimer.prod();
55 				prodding = false;
56 				remainingTime = sysTimer.getRemainingTime();
57 			}
58 
59 			if (remainingTime == Duration.max)
60 				semaphore.wait();
61 			else
62 				semaphore.wait(remainingTime);
63 		}
64 	}
65 
66 	final class ThreadTimerEvent : TimerEvent
67 	{
68 		AppCallback fn;
69 		bool recurring;
70 		TimerTask task;
71 
72 		this(AppCallback fn, uint ms, bool recurring)
73 		{
74 			this.fn = fn;
75 			this.recurring = recurring;
76 			this.task = new TimerTask(ms.msecs, &taskCallback);
77 			synchronized(sysTimer)
78 				sysTimer.add(task);
79 		}
80 
81 		void taskCallback(SysTimer timer, TimerTask task)
82 		{
83 			if (recurring)
84 				timer.add(task);
85 			fn.call();
86 		}
87 
88 		override void cancel()
89 		{
90 			if (prodding) // cancel called from timer event handler, synchronization would cause a deadlock
91 				task.cancel();
92 			else
93 			synchronized(sysTimer)
94 				task.cancel();
95 		}
96 	}
97 }