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