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-2025 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 noexcept -> TimeValue
89  {
90  return system_clock::now(); // Get the current time
91  }
92 
93  // Predicates
94  auto isTimeout (const TimeValue&, uInt64) const noexcept -> 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 noexcept -> 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() noexcept -> 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
141  , uInt64 timeout_us ) const noexcept -> bool
142 {
143  // Checks whether the specified time span (timeout in µs) has elapsed
144 
145  const auto now = getCurrentTime();
146 
147  if ( now < time )
148  return false;
149 
150  const auto diff = now - time;
151  const auto diff_us = uInt64(duration_cast<microseconds>(diff).count());
152  return diff_us > timeout_us;
153 }
154 
155 //----------------------------------------------------------------------
156 template <typename ObjectT>
157 auto FTimer<ObjectT>::addTimer (ObjectT* object, int interval) -> int
158 {
159  // Create a timer and returns the timer identifier number
160  // (interval in ms)
161 
162  const std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
163  auto& timer_list = globalTimerList();
164  const int id = getNextId();
165  const auto time_interval = milliseconds(interval);
166  const auto timeout = getCurrentTime() + time_interval;
167  const FTimerData timedata{ id, time_interval, timeout, object };
168 
169  // Insert in list sorted by timeout
170  timer_list->insert( std::lower_bound( timer_list->cbegin()
171  , timer_list->cend()
172  , timedata
173  , [] (const auto& a, const auto& b) noexcept
174  {
175  return a.timeout < b.timeout;
176  }
177  )
178  , timedata);
179  return id;
180 }
181 
182 //----------------------------------------------------------------------
183 template <typename ObjectT>
184 auto FTimer<ObjectT>::delTimer (int id) const -> bool
185 {
186  // Deletes a timer by using the timer identifier number
187 
188  if ( id <= 0 )
189  return false;
190 
191  const std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
192  const auto& timer_list = globalTimerList();
193 
194  if ( ! timer_list || timer_list->empty() )
195  return false;
196 
197  const auto iter = std::find_if ( timer_list->cbegin()
198  , timer_list->cend()
199  , [id] (const auto& timer) noexcept
200  {
201  return timer.id == id;
202  }
203  );
204 
205  if ( iter == timer_list->cend() )
206  return false;
207 
208  timer_list->erase(iter);
209  return true;
210 }
211 
212 //----------------------------------------------------------------------
213 template <typename ObjectT>
214 auto FTimer<ObjectT>::delOwnTimers(const ObjectT* object) const -> bool
215 {
216  // Deletes all timers of this object
217 
218  const std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
219  const auto& timer_list = globalTimerList();
220 
221  if ( ! timer_list || timer_list->empty() )
222  return false;
223 
224  timer_list->erase ( std::remove_if( timer_list->begin()
225  , timer_list->end()
226  , [object] (const auto& timer) noexcept
227  {
228  return timer.object == object;
229  }
230  )
231  , timer_list->end() );
232  return true;
233 }
234 
235 //----------------------------------------------------------------------
236 template <typename ObjectT>
237 auto FTimer<ObjectT>::delAllTimers() const -> bool
238 {
239  // Deletes all timers of all objects
240 
241  const std::lock_guard<std::shared_timed_mutex> lock_guard(internal::timer_var::mutex);
242  const auto& timer_list = globalTimerList();
243 
244  if ( ! timer_list || timer_list->empty() )
245  return false;
246 
247  timer_list->clear();
248  timer_list->shrink_to_fit();
249  return true;
250 }
251 
252 // protected methods of FTimer
253 //----------------------------------------------------------------------
254 template <typename ObjectT>
255 template <typename CallbackT>
256 auto FTimer<ObjectT>::processTimerEvent (CallbackT callback) -> uInt
257 {
258  uInt activated{0};
259  std::shared_lock<std::shared_timed_mutex> lock ( internal::timer_var::mutex
260  , std::defer_lock );
261 
262  if ( ! lock.try_lock() )
263  return 0;
264 
265  const auto& timer_list = globalTimerList();
266 
267  if ( ! timer_list || timer_list->empty() )
268  return 0;
269 
270  const auto currentTime = getCurrentTime();
271 
272  for (auto& timer : *timer_list)
273  {
274  if ( ! timer.id
275  || ! timer.object
276  || currentTime < timer.timeout ) // Timer not expired
277  continue;
278 
279  timer.timeout += timer.interval;
280 
281  if ( timer.timeout < currentTime )
282  timer.timeout = currentTime + timer.interval;
283 
284  if ( timer.interval > microseconds(0) )
285  ++activated;
286 
287  lock.unlock();
288  FTimerEvent t_ev(Event::Timer, timer.id);
289  callback (timer.object, &t_ev);
290  lock.lock();
291  }
292 
293  return activated;
294 }
295 
296 // private methods of FTimer
297 //----------------------------------------------------------------------
298 template <typename ObjectT>
299 auto FTimer<ObjectT>::globalTimerList() noexcept -> const FTimerListUniquePtr&
300 {
301  static const auto& timer_list = std::make_unique<FTimerList>();
302  return timer_list;
303 }
304 
305 // class forward declaration
306 class FObject;
307 
308 // Specialization for FObject
309 template <>
310 auto FTimer<FObject>::globalTimerList() noexcept -> const FTimerListUniquePtr&;
311 
312 
313 //----------------------------------------------------------------------
314 // class FObjectTimer
315 //----------------------------------------------------------------------
317 {
318  public:
319  FObjectTimer();
320 
321  // Destructor
322  virtual ~FObjectTimer() = default;
323 
324  // Accessors
325  inline virtual auto getClassName() const -> FString
326  {
327  return "FObjectTimer";
328  }
329 
330  static inline auto getCurrentTime() noexcept -> TimeValue
331  {
332  return timer->getCurrentTime();
333  }
334 
335  // Predicates
336  static auto isTimeout (const TimeValue& time, uInt64 timeout) noexcept -> bool
337  {
338  return timer->isTimeout(time, timeout);
339  }
340 
341  // Methods
342  auto addTimer (int interval) -> int
343  {
344  return timer->addTimer(selfPointer<FObject*>(), interval);
345  }
346 
347  auto delTimer (int id) const -> bool
348  {
349  return timer->delTimer(id);
350  }
351 
352  auto delOwnTimers() const -> bool
353  {
354  return timer->delOwnTimers(selfPointer<const FObject*>());
355  }
356 
357  auto delAllTimers() const -> bool
358  {
359  return timer->delAllTimers();
360  }
361 
362  protected:
363  auto getTimerList() const noexcept -> FTimer<FObject>::FTimerList*
364  {
365  return timer->globalTimerList().get();
366  }
367 
368  // Method
369  auto processTimerEvent() -> uInt
370  {
371  return timer->processTimerEvent ( [this] (FObject* receiver, FEvent* event)
372  {
373  performTimerAction(receiver, event);
374  }
375  );
376  }
377 
378  private:
379  // Method
380  virtual void performTimerAction (FObject*, FEvent*);
381 
382  template <typename T>
383  inline auto selfPointer() -> T
384  {
385  return T(this);
386  }
387 
388  template <typename T>
389  inline auto selfPointer() const noexcept -> T
390  {
391  return T(this);
392  }
393 
394  // Data members
395  static FTimer<FObject>* timer;
396 };
397 
398 
399 } // namespace finalcut
400 
401 #endif // FTIMER_H
402 
Definition: ftimer.h:103
Definition: fevent.h:311
Definition: ftimer.h:54
Definition: class_template.cpp:25
Definition: fobject.h:79
Definition: fstring.h:82
Definition: fevent.h:101
Definition: ftimer.h:316
Definition: ftimer.h:76