Stroika Library 3.0d18
 
Loading...
Searching...
No Matches
WaitableEvent.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_WaitableEvent_h_
5#define _Stroika_Foundation_Execution_WaitableEvent_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <forward_list>
10#include <set>
11
12#include "Stroika/Foundation/Common/Common.h"
15
16/**
17 * \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
18 */
19
21
22 /*
23 * UNSURE IF/HOW LONG WE WANT TO SUPPORT THIS API. EXPERIMENTAL!
24 * (introduced in Stroika 2.0a20 - 2014-02-08)
25 *
26 * This appears to be an issue with strong arguments on both sides. I'm very uncertain.
27 *
28 * I've used the Windows WaitForMutlipleObjects API for years, and feel pretty comfortable with it.
29 * I find it handy.
30 *
31 * However, there are compelling arguments (for example):
32 * https://groups.google.com/forum/#!msg/comp.unix.programmer/q2x0yQR5txk/34v1qQZN_u0J
33 * https://groups.google.com/forum/#!msg/comp.unix.programmer/WsgZZmu4ESA/Rv1MCun1CmUJ
34 *
35 * which argue that its a bad idea, and that it leads to bad programming (bugs).
36 *
37 * To some extent I we may have addressed the reported concerns by having WaitForAny/WaitForAnyUntil
38 * return the full set of events that were signaled (and the issue about races I don't think applies
39 * to WaitForAll).
40 *
41 * Note also that some recommend using the BlockingQueue<> pattern to avoid WaitForMultipleObjects (WaitForAny)
42 * such as:
43 * http://stackoverflow.com/questions/788835/waitformultipleobjects-in-java
44 * Stroika supports this sort of BlockingQueue<>
45 *
46 * Note - WaitForAny() takes a templated SET because I don't want to create interdependency
47 * with Containers and something this low level, and yet we want to make it easy for users of this
48 * to use Stroika Set<> objects.
49 *
50 * References:
51 * o Notes on implementing Windows WaitForMultipleEvents API using POSIX (which are similar to stdc++) APIs:
52 * https://www.hackerzvoice.net/madchat/windoz/coding/winapi/waitfor_api.pdf
53 *
54 * o Interesting notes on how to implement WaitForMultipleEvents
55 * http://lists.boost.org/Archives/boost/2004/12/77175.php
56 *
57 * I THINK condition-variable API is better, so disabling this by default.. and maybe lose it altogether soon.
58 * -- LGP 2023-10-17
59 */
60#ifndef qExecution_WaitableEvent_SupportWaitForMultipleObjects
61#define qExecution_WaitableEvent_SupportWaitForMultipleObjects 0
62#endif
63
64 /**
65 * AutoReset Waitable Event (like Windows' CreateEvent (resetType==eManualReset, false)).
66 *
67 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
68 *
69 * \note \em async-signal-safety - this is NOT safe to use from signals (from http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_cond_broadcast.html - It is not safe to use the pthread_cond_signal() function in a signal handler that is invoked asynchronously
70 * Use POSIX sem_init/sem_post ()
71 *
72 * \note \em Design Note Considered making this copyable, or at least movable, but mutex and
73 * other similar classes are not.
74 * and you can easily use shared_ptr<> on an WaitableEvent to make it copyable.
75 *
76 * \note \em Design Note WaitForAny/WaitForAnyUntil and WaitForMultipleEvents
77 *
78 * @see qExecution_WaitableEvent_SupportWaitForMultipleObjects
79 */
81 public:
82 /**
83 */
85 WaitableEvent (WaitableEvent&&) = delete;
86 WaitableEvent (const WaitableEvent&) = delete;
87
88 public:
89 /**
90 * \note the user of this class must assure all waiters have completed their wait before destroying the event (checked with assertions).
91 */
92#if qStroika_Foundation_Debug_AssertionsChecked || qStroika_FeatureSupported_Valgrind
94#else
95 ~WaitableEvent () = default;
96#endif
97
98 public:
99 nonvirtual WaitableEvent& operator= (const WaitableEvent&&) = delete;
100 nonvirtual WaitableEvent& operator= (const WaitableEvent&) = delete;
101
102 public:
103 /**
104 * Set the event to the non-signaled state
105 *
106 * \note This COULD have been called 'UnSet'.
107 *
108 * \note ***Not Cancelation Point***
109 */
110 nonvirtual void Reset ();
111
112 public:
113 /**
114 * This checks if the event is currently in a triggered state. Regardless of the type of event
115 * (auto-reset or not) - this does not change the trigger state.
116 *
117 * \note ***Not Cancelation Point***
118 */
119 nonvirtual bool GetIsSet () const noexcept;
120
121 public:
122 /**
123 * This checks if the event is currently in a triggered state. Regardless of the type of event
124 * (auto-reset or not) - this does not change the trigger state.
125 *
126 * \note ***NOT THREADSAFE*** - same as GetIsSet() except avoids the lock, so will be reported by TSAN (for example)
127 * as a race (cuz it is). JUST INTENDED for debug trace messages (so used in ToString).
128 *
129 * Because of this - if testing with TSAN (thread sanitizer) - probably best to prefix functions calling this with
130 * Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD
131 *
132 * \note ***Not Cancelation Point***
133 */
134 nonvirtual bool PeekIsSet () const noexcept;
135
136 public:
137 /**
138 * Set the event to the signaled state.
139 *
140 * \note This COULD have been called 'Signal', or 'SetSignaled'.
141 *
142 * \note ***Not Cancelation Point***
143 */
144 nonvirtual void Set ();
145
146 public:
147 /**
148 * Simple wait. Can use operator HANDLE() to do fancier waits.
149 * timeout can be negative (which triggers an immediate exception).
150 *
151 * TimeOutException throws if the timeout is exceeeded.
152 *
153 * \note Wait (0) will always throw TimeOutException regardless of the state of the event/trigger
154 *
155 * @see WaitQuietly ()
156 * @see PeekIsSet ()
157 *
158 * \note ***Cancelation Point***
159 */
160 nonvirtual void Wait (Time::DurationSeconds timeout = Time::kInfinity);
161
162 public:
163 /**
164 * Intentionally omit case of spurious wakeup we get from condition variables.
165 */
166 enum class WaitStatus {
167 eTimeout,
168 eTriggered,
169
170 Stroika_Define_Enum_Bounds (eTimeout, eTriggered)
171 };
172
173 public:
174 /**
175 * Wait the given period of time, and return true if event occurred (Set called), and false on timeout.
176 * This is mostly useful if we want a wait, for advisory purposes (say to avoid races), but don't
177 * want an exception as its not an issue to handle specially.
178 *
179 * Returns: true (kWaitQuietlySetResult) if event signaled/occurred, and false (kWaitQuietlyTimeoutResult) if timeout
180 *
181 * \note WaitQuietly (0) will always return false regardless of the state of the event/trigger
182 *
183 * \note WaitQuietly() can raise exceptions, but only Thread::AborttException
184 *
185 * @see Wait ()
186 * @see WaitUntil ()
187 * @see WaitUntilQuietly ()
188 * @see WaitQuietlyAndReset ()
189 * @see PeekIsSet ()
190 *
191 * \note ***Cancelation Point***
192 */
193 nonvirtual WaitStatus WaitQuietly (Time::DurationSeconds timeout = Time::kInfinity);
194
195 public:
196 /**
197 * TimeOutException throws if the event is not signaled before timeoutAt is
198 * exceeded (includes when reached).
199 *
200 * @see Wait ()
201 * @see WaitQuietly ()
202 * @see WaitUntilQuietly ()
203 * @see WaitUntilAndReset ()
204 *
205 * \note ***Cancelation Point***
206 */
207 nonvirtual void WaitUntil (Time::TimePointSeconds timeoutAt);
208
209 public:
210 /**
211 *
212 * \note WaitUntilQuietly() can raise exceptions, but only Thread::AbortException
213 *
214 * Returns: true (kWaitQuietlySetResult) if event signaled/occurred, and false (kWaitQuietlyTimeoutResult) if timeout
215 *
216 * Note - unlike condition variable - will not return with spurious wakeup.
217 * So a return of triggered means it definitely was triggered!
218 *
219 * @see Wait ()
220 * @see WaitQuietly ()
221 * @see WaitUntil ()
222 * @see WaitUntilQuietlyAndReset ()
223 *
224 * \note ***Cancelation Point***
225 */
227
228 public:
229 /**
230 * @see Wait ()
231 * @see WaitUntil ()
232 * @see WaitQuietly ()
233 * @see WaitUntilQuietly ()
234 * @see Reset ()
235 *
236 * \note ***Cancelation Point***
237 *
238 * Unclear if this is a good idea. Its a preplacement for 'auto-reset' events in Stroika v2.1.
239 */
240 nonvirtual void WaitAndReset (Time::Duration timeout = Time::kInfinity);
241 nonvirtual void WaitUntilAndReset (Time::TimePointSeconds timeoutAt);
242 nonvirtual WaitStatus WaitQuietlyAndReset (const Time::Duration& timeout);
243 nonvirtual WaitStatus WaitUntilQuietlyAndReset (Time::TimePointSeconds timeoutAt);
244
245 public:
246#if qExecution_WaitableEvent_SupportWaitForMultipleObjects
247 public:
248 /**
249 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
250 *
251 * \note WaitForAny IS EXPERIMENTAL
252 *
253 * \note ***Cancelation Point***
254 */
255 template <typename CONTAINER_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
256 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAny (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::DurationSeconds timeout = Time::kInfinity);
257 template <typename ITERATOR_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
258 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAny (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart, ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd,
259 Time::DurationSeconds timeout = Time::kInfinity);
260
261 public:
262 /**
263 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
264 *
265 * \note WaitForAny IS EXPERIMENTAL
266 *
267 * \note ***Cancelation Point***
268 */
269 template <typename CONTAINER_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
270 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAnyUntil (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::TimePointSeconds timeoutAt);
271 template <typename ITERATOR_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
272 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAnyUntil (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart,
273 ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd, Time::TimePointSeconds timeoutAt);
274
275 public:
276 /**
277 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
278 *
279 * \note WaitForAll IS EXPERIMENTAL
280 *
281 * \note ***Cancelation Point***
282 */
283 template <typename CONTAINER_OF_WAITABLE_EVENTS>
284 static void WaitForAll (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::DurationSeconds timeout = Time::kInfinity);
285 template <typename ITERATOR_OF_WAITABLE_EVENTS>
286 static void WaitForAll (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart, ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd,
287 Time::DurationSeconds timeout = Time::kInfinity);
288
289 public:
290 /**
291 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
292 *
293 * \note WaitForAllUntil IS EXPERIMENTAL
294 *
295 * \note ***Cancelation Point***
296 */
297 template <typename CONTAINER_OF_WAITABLE_EVENTS>
298 static void WaitForAllUntil (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::TimePointSeconds timeoutAt);
299 template <typename ITERATOR_OF_WAITABLE_EVENTS>
300 static void WaitForAllUntil (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart, ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd,
301 Time::TimePointSeconds timeoutAt);
302
303 private:
304 static inline SpinLock sExtraWaitableEventsMutex_;
305#endif
306
307 public:
308 DISABLE_COMPILER_MSC_WARNING_START (4996);
309 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
310 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
311 enum class [[deprecated ("Since Stroika v3.0d4 - use WaitAndReset")]] ResetType {
312 eAutoReset,
313 eManualReset,
314 Stroika_Define_Enum_Bounds (eAutoReset, eManualReset)
315 };
316 [[deprecated ("Since Stroika v3.0d4 - use WaitAndReset")]] static constexpr ResetType eAutoReset = ResetType::eAutoReset;
317 [[deprecated ("Since Stroika v3.0d4 - use WaitAndReset")]] static constexpr ResetType eManualReset = ResetType::eManualReset;
318 [[deprecated ("since v3.0d4 - use WaitStatus::eTimeout")]] static constexpr WaitStatus kWaitQuietlyTimeoutResult{WaitStatus::eTimeout};
319 [[deprecated ("since v3.0d4 - use WaitStatus::eTriggered")]] static constexpr WaitStatus kWaitQuietlySetResult{WaitStatus::eTriggered};
320 DISABLE_COMPILER_MSC_WARNING_END (4996);
321 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
322 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
323
324 private:
325 struct WE_ {
326 mutable ConditionVariable<> fConditionVariable{};
327 bool fTriggered{false};
328
329 nonvirtual void Reset ();
330 nonvirtual bool GetIsSet () const noexcept;
331 nonvirtual bool PeekIsSet () const noexcept;
332 nonvirtual void Set ();
333 nonvirtual void WaitUntil (Time::TimePointSeconds timeoutAt);
334 nonvirtual WaitStatus WaitUntilQuietly (Time::TimePointSeconds timeoutAt);
335 };
336 WE_ fWE_;
337#if qExecution_WaitableEvent_SupportWaitForMultipleObjects
338 forward_list<shared_ptr<WE_>> fExtraWaitableEvents_;
339#endif
340 };
341
342}
343
344/*
345 ********************************************************************************
346 ***************************** Implementation Details ***************************
347 ********************************************************************************
348 */
349#include "WaitableEvent.inl"
350
351#endif /*_Stroika_Foundation_Execution_WaitableEvent_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
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
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
nonvirtual WaitStatus WaitUntilQuietly(Time::TimePointSeconds timeoutAt)
nonvirtual WaitStatus WaitQuietly(Time::DurationSeconds timeout=Time::kInfinity)
nonvirtual void Wait(Time::DurationSeconds timeout=Time::kInfinity)
nonvirtual void WaitAndReset(Time::Duration timeout=Time::kInfinity)
nonvirtual bool PeekIsSet() const noexcept
nonvirtual bool GetIsSet() const noexcept
nonvirtual void WaitUntil(Time::TimePointSeconds timeoutAt)
Duration is a chrono::duration<double> (=.
Definition Duration.h:96