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 
23 {
26 };
27 
29 {
30 public:
31  virtual ~ITimerObserver() = default;
32 
33  virtual void Update() = 0;
34 };
35 
36 using slot = std::function<void()>;
37 
39 {
40 public:
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 {
53 public:
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 
242 public:
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 
325 private:
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 };
ITimerSubject::Detach
virtual void Detach(ITimerObserver *observer)=0
ITimerObserver::Update
virtual void Update()=0
Timer::Attach
void Attach(ITimerObserver *observer, timer_mode mode, std::chrono::milliseconds ms) override
Definition: timer.hpp:303
IObservable::notify
virtual void notify()=0
ITimerSubject::Attach
virtual void Attach(ITimerObserver *observer, timer_mode mode, std::chrono::milliseconds ms)=0
Timer::~Timer
~Timer() override
Definition: timer.hpp:271
slot
std::function< void()> slot
Definition: timer.hpp:36
ITimerSubject
Definition: timer.hpp:38
ITimerObserver
Definition: timer.hpp:28
IObservable
Definition: timer.hpp:51
Timer
Definition: timer.hpp:60
Timer::singleShot
static std::future< bool > & singleShot(const slot &functionPointer, std::chrono::milliseconds ms)
Definition: timer.hpp:254
IObservable::~IObservable
virtual ~IObservable()=default
ITimerObserver::~ITimerObserver
virtual ~ITimerObserver()=default
IObservable::toDestroy
virtual bool toDestroy()=0
armarx::interval
Interval< T > interval(T lo, T hi)
Definition: OccupancyGrid.h:33
continuous
@ continuous
Definition: timer.hpp:25
Timer::stop
static void stop()
Definition: timer.hpp:281
Timer::periodicShot
static void periodicShot(slot functionPointer, std::chrono::milliseconds ms)
Definition: timer.hpp:260
Timer::Timer
Timer()
Definition: timer.hpp:319
ITimerSubject::~ITimerSubject
virtual ~ITimerSubject()=default
Timer::createTimer
static std::shared_ptr< Timer > createTimer()
Definition: timer.hpp:244
cv
Definition: helper.h:34
singleshot
@ singleshot
Definition: timer.hpp:24
Timer::futureWatch
static std::future< bool > & futureWatch(const slot &functionPointer, const std::shared_future< bool > &fut)
Definition: timer.hpp:266
timer_mode
timer_mode
Definition: timer.hpp:22
Timer::isRunning
static bool isRunning()
Definition: timer.hpp:287
Timer::start
void start()
Definition: timer.hpp:293
Timer::Detach
void Detach(ITimerObserver *observer) override
Definition: timer.hpp:314