Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
WaitableEvent.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. 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 qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects
61#if defined(qExecution_WaitableEvent_SupportWaitForMultipleObjects)
62#define qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects qExecution_WaitableEvent_SupportWaitForMultipleObjects
63#else
64#define qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects 0
65#endif
66#endif
67// DEPRECATED NAME - since 3.0d25 (to be removed in v3.0a1)
68#ifndef qExecution_WaitableEvent_SupportWaitForMultipleObjects
69#define qExecution_WaitableEvent_SupportWaitForMultipleObjects qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects
70#endif
71
72 /**
73 * AutoReset Waitable Event (like Windows' CreateEvent (resetType==eManualReset, false)).
74 *
75 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
76 *
77 * \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
78 * Use POSIX sem_init/sem_post ()
79 *
80 * \note \em Design Note Considered making this copyable, or at least movable, but mutex and
81 * other similar classes are not.
82 * and you can easily use shared_ptr<> on an WaitableEvent to make it copyable.
83 *
84 * \note \em Design Note WaitForAny/WaitForAnyUntil and WaitForMultipleEvents
85 *
86 * @see qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects
87 */
89 public:
90 /**
91 */
93 WaitableEvent (WaitableEvent&&) = delete;
94 WaitableEvent (const WaitableEvent&) = delete;
95
96 public:
97 /**
98 * \note the user of this class must assure all waiters have completed their wait before destroying the event (checked with assertions).
99 */
100#if qStroika_Foundation_Debug_AssertionsChecked
102#else
103 ~WaitableEvent () = default;
104#endif
105
106 public:
107 nonvirtual WaitableEvent& operator= (const WaitableEvent&&) = delete;
108 nonvirtual WaitableEvent& operator= (const WaitableEvent&) = delete;
109
110 public:
111 /**
112 * Set the event to the non-signaled state
113 *
114 * \note This COULD have been called 'UnSet'.
115 *
116 * \note ***Not Cancelation Point***
117 */
118 nonvirtual void Reset ();
119
120 public:
121 /**
122 * This checks if the event is currently in a triggered state. Regardless of the type of event
123 * (auto-reset or not) - this does not change the trigger state.
124 *
125 * \note ***Not Cancelation Point***
126 */
127 nonvirtual bool GetIsSet () const noexcept;
128
129 public:
130 /**
131 * This checks if the event is currently in a triggered state. Regardless of the type of event
132 * (auto-reset or not) - this does not change the trigger state.
133 *
134 * \note ***NOT THREADSAFE*** - same as GetIsSet() except avoids the lock, so will be reported by TSAN (for example)
135 * as a race (cuz it is). JUST INTENDED for debug trace messages (so used in ToString).
136 *
137 * Because of this - if testing with TSAN (thread sanitizer) - probably best to prefix functions calling this with
138 * Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD
139 *
140 * \note ***Not Cancelation Point***
141 */
142 nonvirtual bool PeekIsSet () const noexcept;
143
144 public:
145 /**
146 * Set the event to the signaled state.
147 *
148 * \note This COULD have been called 'Signal', or 'SetSignaled'.
149 *
150 * \note ***Not Cancelation Point***, but can throw (cuz uses mutex lock which can throw)
151 */
152 nonvirtual void Set ();
153
154 public:
155 /**
156 * Simple wait. Can use operator HANDLE() to do fancier waits.
157 * timeout can be negative (which triggers an immediate exception).
158 *
159 * Throws a timeout (@see Execution::ThrowError (errc::timed_out)) if the timeout is exceeded.
160 *
161 * \note Wait (0) will always throw a timeout regardless of the state of the event/trigger
162 *
163 * @see WaitQuietly ()
164 * @see PeekIsSet ()
165 *
166 * \note ***Cancelation Point***
167 */
168 nonvirtual void Wait (Time::DurationSeconds timeout = Time::kInfinity);
169
170 public:
171 /**
172 * Intentionally omit case of spurious wakeup we get from condition variables.
173 */
174 enum class WaitStatus {
175 eTimeout,
176 eTriggered,
177
178 Stroika_Define_Enum_Bounds (eTimeout, eTriggered)
179 };
180
181 public:
182 /**
183 * Wait the given period of time, and return true if event occurred (Set called), and false on timeout.
184 * This is mostly useful if we want a wait, for advisory purposes (say to avoid races), but don't
185 * want an exception as its not an issue to handle specially.
186 *
187 * Returns: true (kWaitQuietlySetResult) if event signaled/occurred, and false (kWaitQuietlyTimeoutResult) if timeout
188 *
189 * \note WaitQuietly (0) will always return false regardless of the state of the event/trigger
190 *
191 * \note WaitQuietly() can raise exceptions, but only Thread::AborttException
192 *
193 * @see Wait ()
194 * @see WaitUntil ()
195 * @see WaitUntilQuietly ()
196 * @see WaitQuietlyAndReset ()
197 * @see PeekIsSet ()
198 *
199 * \note ***Cancelation Point***
200 */
201 nonvirtual WaitStatus WaitQuietly (Time::DurationSeconds timeout = Time::kInfinity);
202
203 public:
204 /**
205 * Throws a timeout (@see Execution::ThrowError (errc::timed_out)) if the event is not signaled before timeoutAt is
206 * exceeded (includes when reached).
207 *
208 * @see Wait ()
209 * @see WaitQuietly ()
210 * @see WaitUntilQuietly ()
211 * @see WaitUntilAndReset ()
212 *
213 * \note ***Cancelation Point***
214 */
215 nonvirtual void WaitUntil (Time::TimePointSeconds timeoutAt);
216
217 public:
218 /**
219 *
220 * \note WaitUntilQuietly() can raise exceptions, but only Thread::AbortException
221 *
222 * Returns: true (kWaitQuietlySetResult) if event signaled/occurred, and false (kWaitQuietlyTimeoutResult) if timeout
223 *
224 * Note - unlike condition variable - will not return with spurious wakeup.
225 * So a return of triggered means it definitely was triggered!
226 *
227 * @see Wait ()
228 * @see WaitQuietly ()
229 * @see WaitUntil ()
230 * @see WaitUntilQuietlyAndReset ()
231 *
232 * \note ***Cancelation Point***
233 */
235
236 public:
237 /**
238 * @see Wait ()
239 * @see WaitUntil ()
240 * @see WaitQuietly ()
241 * @see WaitUntilQuietly ()
242 * @see Reset ()
243 *
244 * \note ***Cancelation Point***
245 *
246 * Unclear if this is a good idea. Its a preplacement for 'auto-reset' events in Stroika v2.1.
247 */
248 nonvirtual void WaitAndReset (Time::Duration timeout = Time::kInfinity);
249 nonvirtual void WaitUntilAndReset (Time::TimePointSeconds timeoutAt);
250 nonvirtual WaitStatus WaitQuietlyAndReset (const Time::Duration& timeout);
251 nonvirtual WaitStatus WaitUntilQuietlyAndReset (Time::TimePointSeconds timeoutAt);
252
253 public:
254#if qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects
255 public:
256 /**
257 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
258 *
259 * \note WaitForAny IS EXPERIMENTAL
260 *
261 * \note ***Cancelation Point***
262 */
263 template <typename CONTAINER_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
264 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAny (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::DurationSeconds timeout = Time::kInfinity);
265 template <typename ITERATOR_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
266 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAny (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart, ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd,
267 Time::DurationSeconds timeout = Time::kInfinity);
268
269 public:
270 /**
271 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
272 *
273 * \note WaitForAny IS EXPERIMENTAL
274 *
275 * \note ***Cancelation Point***
276 */
277 template <typename CONTAINER_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
278 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAnyUntil (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::TimePointSeconds timeoutAt);
279 template <typename ITERATOR_OF_WAITABLE_EVENTS, typename SET_OF_WAITABLE_EVENTS_RESULT = set<WaitableEvent*>>
280 static SET_OF_WAITABLE_EVENTS_RESULT WaitForAnyUntil (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart,
281 ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd, Time::TimePointSeconds timeoutAt);
282
283 public:
284 /**
285 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
286 *
287 * \note WaitForAll IS EXPERIMENTAL
288 *
289 * \note ***Cancelation Point***
290 */
291 template <typename CONTAINER_OF_WAITABLE_EVENTS>
292 static void WaitForAll (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::DurationSeconds timeout = Time::kInfinity);
293 template <typename ITERATOR_OF_WAITABLE_EVENTS>
294 static void WaitForAll (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart, ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd,
295 Time::DurationSeconds timeout = Time::kInfinity);
296
297 public:
298 /**
299 * Note - CONTAINER_OF_WAITABLE_EVENTS - must iterate over WaitableEvent*!
300 *
301 * \note WaitForAllUntil IS EXPERIMENTAL
302 *
303 * \note ***Cancelation Point***
304 */
305 template <typename CONTAINER_OF_WAITABLE_EVENTS>
306 static void WaitForAllUntil (CONTAINER_OF_WAITABLE_EVENTS waitableEvents, Time::TimePointSeconds timeoutAt);
307 template <typename ITERATOR_OF_WAITABLE_EVENTS>
308 static void WaitForAllUntil (ITERATOR_OF_WAITABLE_EVENTS waitableEventsStart, ITERATOR_OF_WAITABLE_EVENTS waitableEventsEnd,
309 Time::TimePointSeconds timeoutAt);
310
311 private:
312 static inline SpinLock sExtraWaitableEventsMutex_;
313#endif
314
315 public:
316 DISABLE_COMPILER_MSC_WARNING_START (4996);
317 DISABLE_COMPILER_GCC_WARNING_START ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
318 DISABLE_COMPILER_CLANG_WARNING_START ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
319 enum class [[deprecated ("Since Stroika v3.0d4 - use WaitAndReset")]] ResetType {
320 eAutoReset,
321 eManualReset,
322 Stroika_Define_Enum_Bounds (eAutoReset, eManualReset)
323 };
324 [[deprecated ("Since Stroika v3.0d4 - use WaitAndReset")]] static constexpr ResetType eAutoReset = ResetType::eAutoReset;
325 [[deprecated ("Since Stroika v3.0d4 - use WaitAndReset")]] static constexpr ResetType eManualReset = ResetType::eManualReset;
326 [[deprecated ("since v3.0d4 - use WaitStatus::eTimeout")]] static constexpr WaitStatus kWaitQuietlyTimeoutResult{WaitStatus::eTimeout};
327 [[deprecated ("since v3.0d4 - use WaitStatus::eTriggered")]] static constexpr WaitStatus kWaitQuietlySetResult{WaitStatus::eTriggered};
328 DISABLE_COMPILER_MSC_WARNING_END (4996);
329 DISABLE_COMPILER_GCC_WARNING_END ("GCC diagnostic ignored \"-Wdeprecated-declarations\"");
330 DISABLE_COMPILER_CLANG_WARNING_END ("clang diagnostic ignored \"-Wdeprecated-declarations\"");
331
332 private:
333 struct WE_ {
334 mutable ConditionVariable<> fConditionVariable{};
335 bool fTriggered{false}; // not atomic cuz always examined under a lock (except PeekIsSet for debugging)
336
337 nonvirtual void Reset ();
338 nonvirtual bool GetIsSet () const noexcept;
339 nonvirtual bool PeekIsSet () const noexcept; // just for debugging
340 nonvirtual void Set ();
341 nonvirtual void WaitUntil (Time::TimePointSeconds timeoutAt);
342 nonvirtual WaitStatus WaitUntilQuietly (Time::TimePointSeconds timeoutAt);
343 };
344 WE_ fWE_;
345#if qStroika_Foundation_Execution_WaitableEvent_SupportWaitForMultipleObjects
346 forward_list<shared_ptr<WE_>> fExtraWaitableEvents_;
347#endif
348 };
349
350}
351
352/*
353 ********************************************************************************
354 ***************************** Implementation Details ***************************
355 ********************************************************************************
356 */
357#include "WaitableEvent.inl"
358
359#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