timer.hpp
Go to the documentation of this file.
1/**
2 * @author: Daniel Fuchs
3 * @contact: fuxeysolutions@gmail.com
4 *
5 * distributed under the MIT License (MIT).
6 * Copyright (c) Daniel Fuchs
7 *
8 */
9#pragma once
10
11#include <atomic>
12#include <chrono>
13#include <condition_variable>
14#include <functional>
15#include <future>
16#include <iostream>
17#include <mutex>
18#include <thread>
19#include <tuple>
20#include <utility>
21
27
29{
30public:
31 virtual ~ITimerObserver() = default;
32
33 virtual void Update() = 0;
34};
35
36using slot = std::function<void()>;
37
39{
40public:
41 virtual ~ITimerSubject() = default;
42
43 virtual void
44 Attach(ITimerObserver* observer, timer_mode mode, std::chrono::milliseconds ms) = 0;
45
46 virtual void Attach(slot functionPtr, timer_mode mode, std::chrono::milliseconds ms) = 0;
47
48 virtual void Detach(ITimerObserver* observer) = 0;
49};
50
52{
53public:
54 virtual ~IObservable() = default;
55 virtual void notify() = 0;
56
57 virtual bool toDestroy() = 0;
58};
59
61{
62
63 class TimerObservable : public IObservable
64 {
65 public:
66 ~TimerObservable() override{};
67
68 TimerObservable(ITimerObserver* observer, timer_mode mode, std::chrono::milliseconds ms) :
69 timerMode(mode), m_observable(observer), interval(ms), isFinish(false)
70 {
71 this->nextExecution = std::chrono::system_clock::now() + ms;
72 }
73
74 void
75 notify() override
76 {
77 if (std::chrono::system_clock::now() >= this->nextExecution)
78 {
79 this->isFinished = std::async(std::launch::async,
80 [this]
81 {
82 if (this->m_observable != nullptr)
83 {
84 this->m_observable->Update();
85 }
86 });
87 if (this->timerMode == continuous)
88 {
89 this->nextExecution = std::chrono::system_clock::now() + interval;
90 }
91 else
92 {
93 // destroy object
94 this->isFinish = true;
95 }
96 }
97 }
98
99 bool
100 toDestroy() override
101 {
102 return isFinish && this->timerMode == singleshot;
103 }
104
105 private:
106 timer_mode timerMode;
107 ITimerObserver* m_observable;
108 std::chrono::milliseconds interval;
109 std::chrono::time_point<std::chrono::system_clock> nextExecution;
110 std::future<void> isFinished;
111 bool isFinish;
112 };
113
114 class SlotObservable : public IObservable
115 {
116 public:
117 ~SlotObservable() override
118 {
119 }
120
121 SlotObservable(slot fPointer, timer_mode mode, std::chrono::milliseconds ms) :
122 timerMode(mode), interval(ms), functionPointer(std::move(fPointer)), isFinish(false)
123 {
124
125 this->nextExecution = std::chrono::system_clock::now() + ms;
126 }
127
128 void
129 notify() override
130 {
131 if (std::chrono::system_clock::now() >= this->nextExecution)
132 {
133 //this->isFinished = this->promise.get_future();
134 auto p = std::async(std::launch::async,
135 [this]
136 {
137 try
138 {
139 if (this->functionPointer != nullptr)
140 {
141 this->functionPointer();
142 this->promise.set_value(true);
143 }
144 this->promise.set_value(false);
145 }
146 catch (std::future_error& e)
147 {
148 std::cout << "future already satisfied" << e.what()
149 << std::endl;
150 }
151 });
152 if (this->timerMode == continuous)
153 {
154 this->nextExecution = std::chrono::system_clock::now() + interval;
155 }
156 else
157 {
158 // destroy object
159 this->isFinish = true;
160 }
161 }
162 }
163
164 bool
165 toDestroy() override
166 {
167 return isFinish && this->timerMode == singleshot;
168 }
169
170 std::future<bool>&
171 getFut()
172 {
173 return this->isFinished;
174 }
175
176 private:
177 timer_mode timerMode;
178 std::chrono::milliseconds interval;
179 std::chrono::time_point<std::chrono::system_clock> nextExecution;
180 std::future<bool> isFinished;
181 std::promise<bool> promise;
182 slot functionPointer;
183 bool isFinish;
184 };
185
186 class FutureObservable : public IObservable
187 {
188 public:
189 ~FutureObservable() override
190 {
191 }
192
193 FutureObservable(slot fPointer, std::shared_future<bool> sfuture) :
194 sfut(std::move(sfuture)), functionPointer(std::move(fPointer)), isFinish(false)
195 {
196 }
197
198 void
199 notify() override
200 {
201 if (this->sfut.valid())
202 {
203 if (this->sfut.get())
204 {
205 this->isFinished = this->isFinishedPromise.get_future();
206 auto promise = std::async(std::launch::async,
207 [this]
208 {
209 if (this->functionPointer != nullptr)
210 {
211 this->functionPointer();
212 this->isFinish = true;
213 this->isFinishedPromise.set_value(true);
214 }
215 this->isFinishedPromise.set_value(false);
216 });
217 }
218 }
219 }
220
221 bool
222 toDestroy() override
223 {
224 return this->isFinish;
225 }
226
227 std::future<bool>&
228 getFut()
229 {
230 return this->isFinished;
231 }
232
233 private:
234 std::future<bool> isFinished;
235 std::promise<bool> isFinishedPromise;
236 std::shared_future<bool> sfut;
237 slot functionPointer;
238 bool isFinish;
239 };
240
241
242public:
243 static std::shared_ptr<Timer>
245 {
246 if (instance == nullptr)
247 {
248 instance = std::make_shared<Timer>();
249 }
250 return instance;
251 }
252
253 static std::future<bool>&
254 singleShot(const slot& functionPointer, std::chrono::milliseconds ms)
255 {
256 return Timer::createTimer()->AttachSingleshot(functionPointer, ms);
257 }
258
259 static void
260 periodicShot(slot functionPointer, std::chrono::milliseconds ms)
261 {
262 Timer::createTimer()->Attach(std::move(functionPointer), continuous, ms);
263 }
264
265 static std::future<bool>&
266 futureWatch(const slot& functionPointer, const std::shared_future<bool>& fut)
267 {
268 return Timer::createTimer()->AttachFutureWatcher(functionPointer, fut);
269 }
270
271 ~Timer() override
272 {
273 for (auto it = this->v_observables.begin(); it != this->v_observables.end(); it++)
274 {
275 this->v_observables.erase(it--);
276 }
277 this->b_isRunning = false;
278 }
279
280 static void
282 {
283 Timer::instance->b_isRunning = false;
284 }
285
286 static bool
288 {
289 return Timer::instance->b_isRunning;
290 }
291
292 void
294 {
295 if (!b_isRunning)
296 {
297 b_isRunning = true;
298 this->wait_thread->detach();
299 }
300 }
301
302 void
303 Attach(ITimerObserver* observer, timer_mode mode, std::chrono::milliseconds ms) override
304 {
305 this->start();
306 if (this->time > ms)
307 {
308 this->time = ms;
309 }
310 this->v_observables.push_back(std::make_unique<TimerObservable>(observer, mode, ms));
311 }
312
313 void
314 Detach(ITimerObserver* observer) override
315 {
316 (void)observer;
317 }
318
319 explicit Timer()
320 {
321 this->wait_thread = std::make_unique<std::thread>(&Timer::dispatcher, this);
322 this->time = std::chrono::milliseconds(1000);
323 }
324
325private:
326 std::future<bool>&
327 AttachFutureWatcher(const slot& functionPointer, const std::shared_future<bool>& sfut)
328 {
329 this->start();
330 auto o = std::make_unique<FutureObservable>(functionPointer, sfut);
331 std::future<bool>& fut{o->getFut()};
332 this->v_observables.push_back(std::move(o));
333 return fut;
334 }
335
336 std::future<bool>&
337 AttachSingleshot(const slot& functionPointer, std::chrono::milliseconds ms)
338 {
339 this->start();
340 if (this->time > ms)
341 {
342 this->time = ms;
343 }
344 auto o = std::make_unique<SlotObservable>(functionPointer, singleshot, ms);
345 auto& fut = o->getFut();
346 this->v_observables.push_back(std::move(o));
347 return fut;
348 }
349
350 void
351 Attach(slot functionPointer, timer_mode mode, std::chrono::milliseconds ms) override
352 {
353 this->start();
354 if (this->time > ms)
355 {
356 this->time = ms;
357 }
358 this->v_observables.push_back(std::make_unique<SlotObservable>(functionPointer, mode, ms));
359 }
360
361 void
362 dispatcher()
363 {
364 do
365 {
366 std::unique_lock<std::mutex> lck{mtx};
367 for (int i{10}; i > 0 && b_isRunning; --i)
368 {
369 cv.wait_for(lck, time / 10);
370
371 for (auto it = this->v_observables.begin(); it != this->v_observables.end(); it++)
372 {
373 (*it)->notify();
374 if ((*it)->toDestroy())
375 {
376 (*it).reset(nullptr);
377 this->v_observables.erase(it--);
378 }
379 }
380 }
381
382
383 } while (this->b_isRunning);
384 for (auto it = this->v_observables.begin(); it != this->v_observables.end(); it++)
385 {
386 this->v_observables.erase(it--);
387 }
388 }
389
390 std::mutex mtx;
391 std::condition_variable cv{};
392 std::vector<std::unique_ptr<IObservable>> v_observables;
393
394 std::unique_ptr<std::thread> wait_thread;
395 std::atomic_bool b_isRunning{};
396 std::chrono::milliseconds time{};
397
398 static std::shared_ptr<Timer> instance;
399};
virtual ~IObservable()=default
virtual void notify()=0
virtual bool toDestroy()=0
virtual void Update()=0
virtual ~ITimerObserver()=default
virtual ~ITimerSubject()=default
virtual void Detach(ITimerObserver *observer)=0
virtual void Attach(slot functionPtr, timer_mode mode, std::chrono::milliseconds ms)=0
virtual void Attach(ITimerObserver *observer, timer_mode mode, std::chrono::milliseconds ms)=0
static std::future< bool > & futureWatch(const slot &functionPointer, const std::shared_future< bool > &fut)
Definition timer.hpp:266
static void periodicShot(slot functionPointer, std::chrono::milliseconds ms)
Definition timer.hpp:260
~Timer() override
Definition timer.hpp:271
void Detach(ITimerObserver *observer) override
Definition timer.hpp:314
static bool isRunning()
Definition timer.hpp:287
void start()
Definition timer.hpp:293
void Attach(ITimerObserver *observer, timer_mode mode, std::chrono::milliseconds ms) override
Definition timer.hpp:303
Timer()
Definition timer.hpp:319
static std::shared_ptr< Timer > createTimer()
Definition timer.hpp:244
static std::future< bool > & singleShot(const slot &functionPointer, std::chrono::milliseconds ms)
Definition timer.hpp:254
static void stop()
Definition timer.hpp:281
std::function< void()> slot
Definition timer.hpp:36
timer_mode
Definition timer.hpp:23
@ continuous
Definition timer.hpp:25
@ singleshot
Definition timer.hpp:24