FINAL CUT
ftimer.h
1 /***********************************************************************
2 * ftimer.h - Timer for executing recurring tasks *
3 * *
4 * This file is part of the FINAL CUT widget toolkit *
5 * *
6 * Copyright 2022-2023 Markus Gans *
7 * *
8 * FINAL CUT is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU Lesser General Public License as *
10 * published by the Free Software Foundation; either version 3 of *
11 * the License, or (at your option) any later version. *
12 * *
13 * FINAL CUT is distributed in the hope that it will be useful, but *
14 * WITHOUT ANY WARRANTY; without even the implied warranty of *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16 * GNU Lesser General Public License for more details. *
17 * *
18 * You should have received a copy of the GNU Lesser General Public *
19 * License along with this program. If not, see *
20 * <http://www.gnu.org/licenses/>. *
21 ***********************************************************************/
22 
23 /* Standalone class Base class
24  * ════════════════ ══════════
25  *
26  * ▕▔▔▔▔▔▔▔▔▏ ▕▔▔▔▔▔▔▔▔▔▔▔▔▔▔▏
27  * ▕ FTimer ▏ ▕ FObjectTimer ▏
28  * ▕▁▁▁▁▁▁▁▁▏ ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▁▏
29  *
30  */
31 
32 #ifndef FTIMER_H
33 #define FTIMER_H
34 
35 #if !defined (USE_FINAL_H) && !defined (COMPILE_FINAL_CUT)
36  #error "Only <final/final.h> can be included directly."
37 #endif
38 
39 #include <algorithm>
40 #include <chrono>
41 #include <memory>
42 #include <shared_mutex>
43 #include <vector>
44 
45 #include "final/fevent.h"
46 #include "final/util/fstring.h"
47 
48 namespace finalcut
49 {
50 
51 namespace internal
52 {
53 
54 struct timer_var
55 {
56  static std::shared_timed_mutex mutex;
57 };
58 
59 } // namespace internal
60 
61 using std::chrono::duration_cast;
62 using std::chrono::seconds;
63 using std::chrono::milliseconds;
64 using std::chrono::microseconds;
65 using std::chrono::system_clock;
66 using std::chrono::time_point;
67 
68 // class forward declaration
69 class FEvent;
70 
71 //----------------------------------------------------------------------
72 // class FTimer
73 //----------------------------------------------------------------------
74 
75 template <typename ObjectT>
76 class FTimer
77 {
78  public:
79  // Destructor
80  virtual ~FTimer() = default;
81 
82  // Accessors
83  inline virtual auto getClassName() const -> FString
84  {
85  return "FTimer";
86  }
87 
88  inline auto getCurrentTime() const -> TimeValue
89  {
90  return system_clock::now(); // Get the current time
91  }
92 
93  // Inquiries
94  auto isTimeout (const TimeValue&, uInt64) -> bool;
95 
96  // Methods
97  auto addTimer (ObjectT*, int) & -> int;
98  auto delTimer (int) const & -> bool;
99  auto delOwnTimers (const ObjectT*) const & -> bool;
100  auto delAllTimers() const & -> bool;
101 
102  protected:
103  struct FTimerData
104  {
105  int id;
106  milliseconds interval;
107  TimeValue timeout;
108  ObjectT* object;
109  };
110 
111  // Using-declaration
112  using FTimerList = std::vector<FTimerData>;
113  using FTimerListUniquePtr = std::unique_ptr<FTimerList>;
114 
115  // Accessor
116  auto getTimerList() const -> FTimerList*
117  {
118  return globalTimerList().get();
119  }
120 
121  // Method
122  template <typename CallbackT>
123  auto processTimerEvent (CallbackT) -> uInt;
124 
125  private:
126  // Method
127  static auto globalTimerList() -> const FTimerListUniquePtr&;
128 
129  // Friend classes
130  friend class FObjectTimer;
131 };
132 
133 // non-member function forward declarations
134 //----------------------------------------------------------------------
135 auto getNextId() -> int;
136 
137 // public methods of FTimer
138 //----------------------------------------------------------------------
139 template <typename ObjectT>
140 inline auto FTimer<ObjectT>::isTimeout (const TimeValue& time, uInt64 timeout) -> bool
141 {
142  // Checks whether the specified time span (timeout in µs) has elapsed
143 
144  const auto& now = getCurrentTime();
145 
146  if ( now < time )
147  return false;
148 
149  const auto diff = now - time;
150  const auto& diff_usec = uInt64(duration_cast<microseconds>(diff).count());
151  return diff_usec > timeout;
152 }
153 
154 //----------------------------------------------------------------------
155 template <typename ObjectT>
156 auto FTimer<ObjectT>::addTimer (ObjectT* object, int interval) & -> int
157 {
158  // Create a timer and returns the timer identifier number
159  // (interval in ms)
160 
161  std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
162  auto& timer_list = globalTimerList();
163  int id = getNextId();
164  const auto time_interval = milliseconds(interval);
165  const auto timeout = getCurrentTime() + time_interval;
166  FTimerData timedata{ id, time_interval, timeout, object };
167 
168  // Insert in list sorted by timeout
169  timer_list->insert( std::lower_bound( timer_list->cbegin()
170  , timer_list->cend()
171  , timedata
172  , [] (const auto& a, const auto& b)
173  {
174  return a.timeout < b.timeout;
175  }
176  )
177  , timedata);
178  return id;
179 }
180 
181 //----------------------------------------------------------------------
182 template <typename ObjectT>
183 auto FTimer<ObjectT>::delTimer (int id) const & -> bool
184 {
185  // Deletes a timer by using the timer identifier number
186 
187  if ( id <= 0 )
188  return false;
189 
190  std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
191  auto& timer_list = globalTimerList();
192 
193  if ( ! timer_list || timer_list->empty() )
194  return false;
195 
196  auto iter = std::find_if ( timer_list->cbegin()
197  , timer_list->cend()
198  , [id] (const auto& timer)
199  {
200  return timer.id == id;
201  }
202  );
203 
204  if ( iter == timer_list->cend() )
205  return false;
206 
207  timer_list->erase(iter);
208  return true;
209 }
210 
211 //----------------------------------------------------------------------
212 template <typename ObjectT>
213 auto FTimer<ObjectT>::delOwnTimers(const ObjectT* object) const & -> bool
214 {
215  // Deletes all timers of this object
216 
217  std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
218  auto& timer_list = globalTimerList();
219 
220  if ( ! timer_list || timer_list->empty() )
221  return false;
222 
223  timer_list->erase ( std::remove_if( timer_list->begin()
224  , timer_list->end()
225  , [&object] (const auto& timer)
226  {
227  return timer.object == object;
228  }
229  )
230  , timer_list->end() );
231  return true;
232 }
233 
234 //----------------------------------------------------------------------
235 template <typename ObjectT>
236 auto FTimer<ObjectT>::delAllTimers() const & -> bool
237 {
238  // Deletes all timers of all objects
239 
240  std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
241  auto& timer_list = globalTimerList();
242 
243  if ( ! timer_list || timer_list->empty() )
244  return false;
245 
246  timer_list->clear();
247  timer_list->shrink_to_fit();
248  return true;
249 }
250 
251 // protected methods of FTimer
252 //----------------------------------------------------------------------
253 template <typename ObjectT>
254 template <typename CallbackT>
255 auto FTimer<ObjectT>::processTimerEvent (CallbackT callback) -> uInt
256 {
257  uInt activated{0};
258  std::shared_lock<std::shared_timed_mutex> lock ( internal::timer_var::mutex
259  , std::defer_lock );
260 
261  if ( ! lock.try_lock() )
262  return 0;
263 
264  auto& timer_list = globalTimerList();
265 
266  if ( ! timer_list || timer_list->empty() )
267  return 0;
268 
269  const auto& currentTime = getCurrentTime();
270 
271  for (auto&& timer : *timer_list)
272  {
273  if ( ! timer.id
274  || ! timer.object
275  || currentTime < timer.timeout ) // Timer not expired
276  continue;
277 
278  timer.timeout += timer.interval;
279 
280  if ( timer.timeout < currentTime )
281  timer.timeout = currentTime + timer.interval;
282 
283  if ( timer.interval > microseconds(0) )
284  ++activated;
285 
286  lock.unlock();
287  FTimerEvent t_ev(Event::Timer, timer.id);
288  callback (timer.object, &t_ev);
289  lock.lock();
290  }
291 
292  return activated;
293 }
294 
295 // private methods of FTimer
296 //----------------------------------------------------------------------
297 template <typename ObjectT>
298 auto FTimer<ObjectT>::globalTimerList() -> const FTimerListUniquePtr&
299 {
300  static const auto& timer_list = std::make_unique<FTimerList>();
301  return timer_list;
302 }
303 
304 // class forward declaration
305 class FObject;
306 
307 // Specialization for FObject
308 template <>
309 auto FTimer<FObject>::globalTimerList() -> const FTimerListUniquePtr&;
310 
311 
312 //----------------------------------------------------------------------
313 // class FObjectTimer
314 //----------------------------------------------------------------------
316 {
317  public:
318  FObjectTimer();
319 
320  // Destructor
321  virtual ~FObjectTimer() = default;
322 
323  // Accessors
324  inline virtual auto getClassName() const -> FString
325  {
326  return "FObjectTimer";
327  }
328 
329  static inline auto getCurrentTime() -> TimeValue
330  {
331  return timer->getCurrentTime();
332  }
333 
334  // Inquiries
335  static auto isTimeout (const TimeValue& time, uInt64 timeout) -> bool
336  {
337  return timer->isTimeout(time, timeout);
338  }
339 
340  // Methods
341  auto addTimer (int interval) -> int
342  {
343  return timer->addTimer(selfPointer<FObject*>(), interval);
344  }
345 
346  auto delTimer (int id) const -> bool
347  {
348  return timer->delTimer(id);
349  }
350 
351  auto delOwnTimers() const -> bool
352  {
353  return timer->delOwnTimers(selfPointer<const FObject*>());
354  }
355 
356  auto delAllTimers() const -> bool
357  {
358  return timer->delAllTimers();
359  }
360 
361  protected:
362  auto getTimerList() const -> FTimer<FObject>::FTimerList*
363  {
364  return timer->globalTimerList().get();
365  }
366 
367  // Method
368  auto processTimerEvent() -> uInt
369  {
370  return timer->processTimerEvent ( [this] (FObject* receiver, FEvent* event)
371  {
372  performTimerAction(receiver, event);
373  }
374  );
375  }
376 
377  private:
378  // Method
379  virtual void performTimerAction (FObject*, FEvent*);
380 
381  template <typename T>
382  inline auto selfPointer() -> T
383  {
384  return T(this);
385  }
386 
387  template <typename T>
388  inline auto selfPointer() const -> T
389  {
390  return T(this);
391  }
392 
393  // Data members
394  static FTimer<FObject>* timer;
395 };
396 
397 
398 } // namespace finalcut
399 
400 #endif // FTIMER_H
401 
Definition: ftimer.h:103
Definition: fevent.h:300
Definition: ftimer.h:54
Definition: class_template.cpp:25
Definition: fobject.h:79
Definition: fstring.h:79
Definition: fevent.h:101
Definition: ftimer.h:315
Definition: ftimer.h:76