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