FINAL CUT
timer_monitor.h
1 /***********************************************************************
2 * timer_monitor.h - Time monitoring object *
3 * *
4 * This file is part of the FINAL CUT widget toolkit *
5 * *
6 * Copyright 2023-2025 Andreas Noe *
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 /* Inheritance diagram
24  * ═══════════════════
25  *
26  * ▕▔▔▔▔▔▔▔▔▔▏
27  * ▕ Monitor ▏
28  * ▕▁▁▁▁▁▁▁▁▁▏
29  * ▲
30  * │
31  * ▕▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▏
32  * ▕ TimerMonitorImpl ▏
33  * ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▏
34  * ▲ ▲
35  * │ │
36  * ▕▔▔▔▔▔▔▔▔▔▔▏1 1▕▔▔▔▔▔▔▔▔▔▔▔▔▏ ▕▔▔▔▔▔▔▔▔▔▔▔▔▔▏
37  * ▕ PipeData ▏- - -▕ PosixTimer ▏ ▕ KqueueTimer ▏ (platform-specific)
38  * ▕▁▁▁▁▁▁▁▁▁▁▏ ▕▁▁▁▁▁▁▁▁▁▁▁▁▏ ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▏
39  * ▲ ▲
40  * │ │
41  * ▕▔▔▔▔▔▔▔▔▔▔▔▔▔▔▏
42  * ▕ TimerMonitor ▏
43  * ▕▁▁▁▁▁▁▁▁▁▁▁▁▁▁▏
44  */
45 
46 #ifndef TIMER_MONITOR_H
47 #define TIMER_MONITOR_H
48 
49 #if (defined(__APPLE__) && defined(__MACH__)) || defined(__OpenBSD__)
50  #define USE_KQUEUE_TIMER
51  #include <unistd.h>
52  #include <sys/event.h>
53  #include <sys/types.h>
54 #else
55  #define USE_POSIX_TIMER
56 #endif
57 
58 #include <ctime>
59 
60 #include <chrono>
61 #include <vector>
62 
63 #include "final/eventloop/monitor.h"
64 #include "final/eventloop/pipedata.h"
65 #include "final/ftypes.h"
66 
67 namespace finalcut
68 {
69 
70 // class and struct forward declaration
71 class SigAlrmHandlerInstaller;
72 struct KqueueHandlerInstaller;
73 
74 #if defined(UNIT_TEST)
75 auto useAlternativeKqueueTimerID() -> bool&;
76 auto alternativeKqueueTimerID() -> int&;
77 #endif // defined(UNIT_TEST)
78 
79 //----------------------------------------------------------------------
80 // class TimerMonitorImpl
81 //----------------------------------------------------------------------
82 class TimerMonitorImpl : public Monitor
83 {
84  public:
85  // Using-declaration
86  using Monitor::Monitor;
87 
88  // Destructor
89  ~TimerMonitorImpl() noexcept override;
90 
91  // Methods
92  virtual void setInterval ( std::chrono::nanoseconds
93  , std::chrono::nanoseconds ) = 0;
94  protected:
95  void validateIntervals ( std::chrono::nanoseconds
96  , std::chrono::nanoseconds ) const;
97 };
98 
99 //----------------------------------------------------------------------
100 inline void TimerMonitorImpl::validateIntervals ( std::chrono::nanoseconds first,
101  std::chrono::nanoseconds periodic ) const
102 {
103  if ( first.count() == 0 && periodic.count() == 0 )
104  throw monitor_error{"Invalid timer intervals: "
105  "both first and periodic cannot be zero"};
106 
107  if ( first.count() < 0 )
108  throw monitor_error{"Invalid first intervals: "
109  "must be >= 0 nanoseconds"};
110 
111  if ( periodic.count() < 0 )
112  throw monitor_error{"Invalid periodic intervals: "
113  "must be >= 0 nanoseconds"};
114 }
115 
116 
117 //----------------------------------------------------------------------
118 // class PosixTimer
119 //----------------------------------------------------------------------
121 {
122  public:
123  // Using-declaration
124  using TimerMonitorImpl::TimerMonitorImpl;
125 
126  // Constructor
127  explicit PosixTimer (EventLoop*);
128 
129  // Disable copy constructor
130  PosixTimer (const PosixTimer&) = delete;
131 
132  // Disable move constructor
133  PosixTimer (PosixTimer&&) noexcept = delete;
134 
135  // Destructor
136  ~PosixTimer() noexcept override;
137 
138  // Disable copy assignment operator (=)
139  auto operator = (const PosixTimer&) -> PosixTimer& = delete;
140 
141  // Disable move assignment operator (=)
142  auto operator = (PosixTimer&&) noexcept -> PosixTimer& = delete;
143 
144  // Methods
145  template <typename T>
146  void init (handler_t, T&&);
147  void setInterval ( std::chrono::nanoseconds
148  , std::chrono::nanoseconds ) override;
149  void trigger(short) override;
150 
151 #if defined(UNIT_TEST)
152  void ReinstallSigAlrmHandler();
153 #endif // defined(UNIT_TEST)
154 
155  private:
156  void init();
157  void validate (const handler_t& hdl) const;
158  void createAlarmPipe();
159  void installTime();
160  void cleanupResources() noexcept;
161 
162 #if defined(USE_POSIX_TIMER)
163  // Data members
164  timer_t timer_id{};
165  PipeData alarm_pipe{NO_FILE_DESCRIPTOR, NO_FILE_DESCRIPTOR};
166  static SigAlrmHandlerInstaller* sig_alrm_handler_installer;
167 #endif // defined(USE_POSIX_TIMER)
168 };
169 
170 #if defined(USE_POSIX_TIMER)
171 //----------------------------------------------------------------------
172 template <typename T>
173 inline void PosixTimer::init (handler_t hdl, T&& uc)
174 {
175  validate (hdl);
176 
177  try
178  {
179  setHandler (std::move(hdl));
180  setUserContext (std::forward<T>(uc));
181  init();
182  }
183  catch (...)
184  {
185  setHandler(handler_t{}); // Clear handler
186  clearUserContext(); // Clear user context
187  throw; // Re-throw the original exception
188  }
189 }
190 
191 //----------------------------------------------------------------------
192 inline void PosixTimer::validate (const handler_t& hdl) const
193 {
194  if ( isInitialized() )
195  throw monitor_error{"This instance has already been initialised."};
196 
197  if ( ! hdl )
198  throw monitor_error{"Handler cannot be null."};
199 }
200 #endif // defined(USE_POSIX_TIMER)
201 
202 
203 //----------------------------------------------------------------------
204 // class KqueueTimer
205 //----------------------------------------------------------------------
206 
208 {
209  public:
210  // Using-declaration
211  using TimerMonitorImpl::TimerMonitorImpl;
212 
213  // Constructor
214  explicit KqueueTimer (EventLoop*);
215 
216  // Disable copy constructor
217  KqueueTimer (const KqueueTimer&) = delete;
218 
219  // Disable move constructor
220  KqueueTimer (KqueueTimer&&) noexcept = delete;
221 
222  // Destructor
223  ~KqueueTimer() noexcept override;
224 
225  // Disable copy assignment operator (=)
226  auto operator = (const KqueueTimer&) -> KqueueTimer& = delete;
227 
228  // Disable move assignment operator (=)
229  auto operator = (KqueueTimer&&) noexcept -> KqueueTimer& = delete;
230 
231  // Methods
232  template <typename T>
233  void init (handler_t, T&&);
234  void setInterval ( std::chrono::nanoseconds
235  , std::chrono::nanoseconds ) override;
236  void trigger (short) override;
237 
238  private:
239  void init();
240  void validate (const handler_t& hdl) const;
241  void cleanupResources() noexcept;
242 
243 #if defined(USE_KQUEUE_TIMER) || defined(UNIT_TEST)
244  struct TimerSpec
245  {
246  int period_ms{};
247  int first_ms{};
248  };
249 
250  // Constants
251  static constexpr int NO_TIMER_ID{-1};
252 
253  // Data members
254  int timer_id{NO_TIMER_ID};
255  bool first_interval{true};
256  TimerSpec timer_spec{};
257  handler_t timer_handler{};
258  static KqueueHandlerInstaller* kqueue_handler_installer;
259 
260  // Friend classes
261  friend class KqueueHandler;
262 #endif // defined(USE_KQUEUE_TIMER) || defined(UNIT_TEST)
263 };
264 
265 #if defined(USE_KQUEUE_TIMER) || defined(UNIT_TEST)
266 //----------------------------------------------------------------------
267 template <typename T>
268 inline void KqueueTimer::init (handler_t hdl, T&& uc)
269 {
270  validate (hdl);
271 
272  try
273  {
274  timer_handler = std::move(hdl);
275  setUserContext (std::forward<T>(uc));
276  init();
277  }
278  catch (...)
279  {
280  timer_handler = handler_t{}; // Clear handler
281  clearUserContext(); // Clear user context
282  throw; // Re-throw the original exception
283  }
284 }
285 
286 //----------------------------------------------------------------------
287 inline void KqueueTimer::validate (const handler_t& hdl) const
288 {
289  if ( isInitialized() )
290  throw monitor_error{"This instance has already been initialised."};
291 
292  if ( ! hdl )
293  throw monitor_error{"Handler cannot be null."};
294 
295  if ( timer_id != NO_TIMER_ID )
296  throw monitor_error{"Timer ID already assigned."};
297 }
298 #endif // defined(USE_KQUEUE_TIMER) || defined(UNIT_TEST)
299 
300 
301 //----------------------------------------------------------------------
302 // struct TimerClass
303 //----------------------------------------------------------------------
304 
306 {
307  #if defined(__APPLE__) && defined(__MACH__)
308  using type = KqueueTimer;
309  #elif defined(__OpenBSD__)
310  using type = KqueueTimer;
311  #else
312  using type = PosixTimer;
313  #endif
314 };
315 
316 
317 //----------------------------------------------------------------------
318 // class TimerMonitor
319 //----------------------------------------------------------------------
320 
321 class TimerMonitor final : public TimerClass::type
322 {
323  public:
324  // Using-declarations
326  using base_class::base_class;
327 
328  TimerMonitor() = delete;
329 
330  // Disable copy constructor
331  TimerMonitor (const TimerMonitor&) = delete;
332 
333  // Disable move constructor
334  TimerMonitor (TimerMonitor&&) noexcept = delete;
335 
336  // Destructor
337  ~TimerMonitor() noexcept override;
338 
339  // Disable copy assignment operator (=)
340  auto operator = (const TimerMonitor&) -> TimerMonitor& = delete;
341 
342  // Disable move assignment operator (=)
343  auto operator = (TimerMonitor&&) noexcept -> TimerMonitor& = delete;
344 
345  // Accessor
346  auto getClassName() const -> FString override;
347 };
348 
349 // TimerMonitor inline functions
350 //----------------------------------------------------------------------
351 inline auto TimerMonitor::getClassName() const -> FString
352 { return "TimerMonitor"; }
353 
354 } // namespace finalcut
355 
356 #endif // TIMER_MONITOR_H
Definition: timer_monitor.h:321
Definition: monitor.h:82
Definition: eventloop.h:49
Definition: class_template.cpp:25
Definition: timer_monitor.h:82
Definition: monitor.h:53
Definition: posix_timer.cpp:179
Definition: timer_monitor.h:120
Definition: fstring.h:82
Definition: timer_monitor.h:207
Definition: pipedata.h:48
Definition: timer_monitor.h:305