Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
IntervalTimer.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_IntervalTimer_h_
5#define _Stroika_Foundation_Execution_IntervalTimer_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <functional>
10
11#include "Stroika/Foundation/Common/Common.h"
13#include "Stroika/Foundation/Containers/KeyedCollection.h"
16
17/**
18 * \file
19 *
20 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
21 */
22
24
25 /**
26 * \brief Manage interval timers - like the javascript setIntervalTimer API.
27 *
28 * o Add and remove timers.
29 * o Support one-shot timers.
30 * o Timers run on arbitrary thread.
31 * o Can shut down manager at any time.
32 * o Can support multiple 'managers' - but then you have to add explicitly. Or use Adder object to add
33 * to default/global IdleTimer manager.
34 *
35 * \note Easiest way to use is IntervalTimer::Adder - see constructor example below
36 *
37 * \note https://stackoverflow.com/questions/33234403/using-setinterval-in-c
38 *
39 * note TODO:
40 * \todo sometimes want to use threadpool/ Sometime async; sometimes a single thread iff stuff
41 */
43 public:
44 /**
45 * Note: these timers CAN throw, and SHOULD throw if interrupted, but the Idle Manager will 'eat' those
46 * exceptions.
47 *
48 * \pre TimerCallback must be cancelable!
49 */
50 using TimerCallback = function<void ()>;
51
52 public:
53 class Manager;
54
55 public:
56 class Adder;
57
58 public:
59 /**
60 * \brief Names a timer Manager::AddOneShot or AddRepeating added - for Remove. No two the same in a process, so
61 * one from another Manager names none of this one's; nor does TimerID{}.
62 *
63 * \note Why an ID, and not the callback, to name a timer by: @see CallbackRegistry (the same callback can be added twice,
64 * too - two timers).
65 */
67
68 public:
69 /**
70 * Used for reporting from the IntervalTimer::Manager (e.g. for debugging, to dump the status).
71 */
73 TimerID fID;
74 TimerCallback fCallback;
75 Time::TimePointSeconds fCallNextAt;
76 optional<Time::Duration> fFrequency; // if missing, this is a one-shot event
77 optional<Time::Duration> fHysteresis;
78
79 public:
80 /**
81 * @see Characters::ToString ()
82 */
83 nonvirtual Characters::String ToString () const;
84 };
85
86 private:
87 struct Key_Extractor_ {
88 TimerID operator() (const RegisteredTask& r) const
89 {
90 return r.fID;
91 };
92 };
93
94 public:
95 using RegisteredTaskCollection =
97 };
98
99 /**
100 * Calls the timers added to it, when due. Its backend is an IRep - DefaultRep (one thread, from its first timer on) unless
101 * another is given; Manager::sThe (made by a Manager::Activator) is the one an Adder adds to, unless given another.
102 *
103 * \note Timers can only be added after the start of main (), and must be removed before the end of main.
104 *
105 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
106 *
107 */
109 public:
110 class IRep;
111
112 public:
113 class DefaultRep;
114
115 public:
116 /**
117 * Argument to Manager can be nullptr, but then not usable.
118 */
119 Manager (const Manager&) = delete;
120 Manager (Manager&&) = default;
121 Manager (const shared_ptr<IRep>& rep);
122
123 public:
124 ~Manager () = default;
125
126 public:
127 nonvirtual Manager& operator= (const Manager&) = delete;
128 nonvirtual Manager& operator= (Manager&&) = default;
129
130 public:
131 /**
132 * \brief Add a timer to be called once after duration when - named by the TimerID returned, for Remove (until it is called)
133 *
134 * \pre intervalTimer valid function ptr (not null)
135 * \pre when >= 0
136 */
137 nonvirtual TimerID AddOneShot (const TimerCallback& intervalTimer, const Time::Duration& when);
138
139 public:
140 /**
141 * \brief Add a timer to be called repeatedly after duration repeatInterval - named by the TimerID returned, for Remove
142 *
143 * \pre intervalTimer valid function ptr (not null)
144 * \pre repeatInterval >= 0
145 * \pre hysteresis == nullopt or hysteresis >= 0
146 */
147 nonvirtual TimerID AddRepeating (const TimerCallback& intervalTimer, const Time::Duration& repeatInterval,
148 const optional<Time::Duration>& hysteresis = nullopt);
149
150 public:
151 /**
152 * \brief Removes the timer: once this returns, its callback is not running, and is not called again. Returns whether it was
153 * there to remove - a repeating timer, or a one-shot not yet called (or being called now) - so false for a one-shot
154 * already called, or a timer already removed.
155 *
156 * If its callback is running now, this waits for that call to finish - unless called from that callback itself (on the
157 * timer's thread), when that call finishes after. So do not call this holding a lock that callback takes.
158 */
159 nonvirtual bool Remove (TimerID timer) noexcept;
160
161 public:
162 [[deprecated ("Since Stroika v3.0d25 - use Remove, which takes a one-shot too")]] nonvirtual void RemoveRepeating (TimerID timer) noexcept;
163
164 public:
165 /**
166 */
167 nonvirtual RegisteredTaskCollection GetAllRegisteredTasks () const;
168
169 public:
170 /**
171 * \brief Having explicit activator object allows for users to control the starting/stopping of facility in a managed fashion.
172 *
173 * At most one such object may exist. When it does, the IntervalTimer::Manager::sThe is active and usable.
174 * Its illegal to call otherwise.
175 *
176 * \par Example Usage
177 * \code
178 * main () {
179 * ...
180 * // near the beginning, before IntervalManager used
181 * Execution::IntervalTimer::Manager::Activator intervalTimerMgrActivator;
182 * \endcode
183 *
184 * \pre (Debug::AppearsDuringMainLifetime ()); during activator lifetime
185 */
186 struct Activator {
187 Activator ();
188 ~Activator ();
189 };
190
191 public:
192 /**
193 * Default interval timer, but you can specify others.
194 */
195 static Manager sThe;
196
197 private:
198 shared_ptr<IRep> fRep_;
199 };
200
201 /**
202 * The Manager names each timer (its TimerID) - and a rep keeps it by that name.
203 */
205 public:
206 virtual ~IRep () = default;
207
208 public:
209 virtual void AddOneShot (TimerID timer, const TimerCallback& intervalTimer, const Time::Duration& when) = 0;
210
211 public:
212 virtual void AddRepeating (TimerID timer, const TimerCallback& intervalTimer, const Time::Duration& repeatInterval,
213 const optional<Time::Duration>& hysteresis) = 0;
214
215 public:
216 /**
217 * \brief As Manager::Remove says: once it returns, the timer's callback is not running, and is not called again - and it
218 * returns whether the timer was there to remove (a one-shot already called is not)
219 */
220 virtual bool Remove (TimerID timer) noexcept = 0;
221
222 public:
223 virtual RegisteredTaskCollection GetAllRegisteredTasks () const = 0;
224 };
225
226 /**
227 * Probably don't use directly. But this is the default implementation of interval timers.
228 */
230 public:
231 DefaultRep ();
232
233 public:
234 virtual void AddOneShot (TimerID timer, const TimerCallback& intervalTimer, const Time::Duration& when) override;
235
236 public:
237 virtual void AddRepeating (TimerID timer, const TimerCallback& intervalTimer, const Time::Duration& repeatInterval,
238 const optional<Time::Duration>& hysteresis) override;
239
240 public:
241 virtual bool Remove (TimerID timer) noexcept override;
242
243 public:
244 virtual RegisteredTaskCollection GetAllRegisteredTasks () const override;
245
246 private:
247 // hidden implementation so details not in header files
248 struct Rep_;
249 shared_ptr<Rep_> fHiddenRep_;
250 };
251
252 /**
253 * \brief Adder adds the given function object to the (for now default; later optionally explicit) IntervalTimer manager, and
254 * when its destroyed, the timer is removed.
255 *
256 * While the timer is registered, it will be called periodically from some arbitrary thread.
257 *
258 * Easiest way to add/remove idle manager. Construct one and its lifetime matches time when callback is potentially aftive.
259 * Destroying the Adder removes the timer (@see Manager::Remove): once the destructor returns, the callback is not
260 * running, and is not called again - so do not destroy one holding a lock its callback takes (the callback itself may
261 * destroy it). Be sure lifetime of these guys inside lifetime of main.
262 */
264 public:
265 /**
266 */
268 eDontRunImmediately,
269 /**
270 * If specified as argument to Adder, then the callback function 'f' will be invoked DIRECTLY from constructor once
271 * as well as being added as a repeated item.
272 *
273 * Pretty commonly, if we have a task you want done every x seconds, you will also want it done IMMEDIATELY
274 * as well.
275 */
277 };
278
279 public:
280 using RunImmediatelyFlag::eDontRunImmediately;
282
283 public:
284 /**
285 * \pre (but unenforced) - lifetime of manager must be > that of created Adder
286 * \note if no manager specified, IntervalTimer::Manager::sThe is used.
287 *
288 * \par Example Usage
289 * \code
290 * namespace {
291 * unique_ptr<IntervalTimer::Adder> sIntervalTimerAdder_;
292 * }
293 * Activator::Activator ()
294 * {
295 * sIntervalTimerAdder_ = make_unique<IntervalTimer::Adder> (
296 * [] () { sKeepCachedMonitorsUpToDate_.DoOnce (); }
297 * , 1min
298 * , IntervalTimer::Adder::eRunImmediately);
299 * }
300 * Activator::~Activator ()
301 * {
302 * sIntervalTimerAdder_.reset ();
303 * }
304 * \endcode
305 */
306 Adder () = delete;
307 Adder (Adder&& src) noexcept;
308 Adder (const TimerCallback& f, const Time::Duration& repeatInterval, const optional<Time::Duration>& hysteresis = nullopt);
309 Adder (const TimerCallback& f, const Time::Duration& repeatInterval, RunImmediatelyFlag runImmediately,
310 const optional<Time::Duration>& hysteresis = nullopt);
311 Adder (IntervalTimer::Manager& manager, const TimerCallback& f, const Time::Duration& repeatInterval,
312 const optional<Time::Duration>& hysteresis = nullopt);
313 Adder (IntervalTimer::Manager& manager, const TimerCallback& f, const Time::Duration& repeatInterval,
314 RunImmediatelyFlag runImmediately, const optional<Time::Duration>& hysteresis = nullopt);
315
316 public:
317 ~Adder ();
318
319 public:
320 /**
321 * \brief Removes this Adder's own timer, and takes over rhs's (not adding it again: rhs, moved from, removes nothing).
322 */
323 nonvirtual Adder& operator= (Adder&& rhs) noexcept;
324 nonvirtual Adder& operator= (const Adder&) = delete;
325
326 public:
327 /**
328 */
329 nonvirtual TimerCallback GetCallback () const;
330
331 private:
332 IntervalTimer::Manager* fManager_; // null if moved from
333 TimerCallback fFunction_;
334 TimerID fTimer_;
335 };
336
337}
338
339/*
340 ********************************************************************************
341 ***************************** Implementation Details ***************************
342 ********************************************************************************
343 */
344#include "IntervalTimer.inl"
345
346#endif /*_Stroika_Foundation_Execution_IntervalTimer_h_*/
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
Definition Realtime.h:82
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
a cross between Mapping<KEY, T> and Collection<T> and Set<T>
Adder adds the given function object to the (for now default; later optionally explicit) IntervalTime...
nonvirtual Adder & operator=(Adder &&rhs) noexcept
Removes this Adder's own timer, and takes over rhs's (not adding it again: rhs, moved from,...
virtual bool Remove(TimerID timer) noexcept override
As Manager::Remove says: once it returns, the timer's callback is not running, and is not called agai...
virtual bool Remove(TimerID timer) noexcept=0
As Manager::Remove says: once it returns, the timer's callback is not running, and is not called agai...
nonvirtual bool Remove(TimerID timer) noexcept
Removes the timer: once this returns, its callback is not running, and is not called again....
nonvirtual TimerID AddRepeating(const TimerCallback &intervalTimer, const Time::Duration &repeatInterval, const optional< Time::Duration > &hysteresis=nullopt)
Add a timer to be called repeatedly after duration repeatInterval - named by the TimerID returned,...
nonvirtual TimerID AddOneShot(const TimerCallback &intervalTimer, const Time::Duration &when)
Add a timer to be called once after duration when - named by the TimerID returned,...
Manage interval timers - like the javascript setIntervalTimer API.
Duration is a chrono::duration<double> (=.
Definition Duration.h:96
Having explicit activator object allows for users to control the starting/stopping of facility in a m...