Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
WaitForIOReady.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_WaitForIOReady_h_
5#define _Stroika_Foundation_Execution_WaitForIOReady_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include "Stroika/Foundation/Common/Common.h"
10#include "Stroika/Foundation/Containers/Collection.h"
11#include "Stroika/Foundation/Containers/Set.h"
16
17/**
18 * \file
19 *
20 * WaitForIOReady utility - portably provide facility to check a bunch of file descriptors/sockets
21 * if input is ready (like select, epoll, WaitForMutlipleObjects, etc)
22 *
23 * TODO:
24 * @todo THINK OUT signal flags/params to ppoll()
25 *
26 * @todo Consider using Mapping<> for fPollData_;
27 *
28 * @todo See if some way to make WaitForIOReady work with stuff other than sockets - on windows
29 * (WaitFormUltipleEventsEx didnt work well at all)
30 */
31
33
34 /**
35 * \note see http://stroika-bugs.sophists.com/browse/STK-653
36 *
37 * WSAPoll is not (fully/mostly) alertable, in the Windows API. So for Windows, this trick is needed to make
38 * WaitForIOReady::Wait* a ***Cancelation Point***.
39 *
40 * Set qStroika_Foundation_Execution_WaitForIOReady_BreakWSAPollIntoTimedMillisecondChunks to a number of milliseconds between WSAPoll
41 * forced wakeups. A smaller value means more responsive, but more wasted CPU time.
42 *
43 * \note Since Stroika 2.1a5, we no longer (generally use but still support) using Thread::Interrupt() to break the sleep.
44 * So this doesn't need to be quite as rapid. Changed from 1000ms to 3000ms --LGP 2020-04-09
45 * STILL NEEDED however, due to aborting threads (and cuz some users may still choose to use the thread interruption approach to wakeup)
46 */
47#ifndef qStroika_Foundation_Execution_WaitForIOReady_BreakWSAPollIntoTimedMillisecondChunks
48#if qStroika_Foundation_Common_Platform_Windows
49#define qStroika_Foundation_Execution_WaitForIOReady_BreakWSAPollIntoTimedMillisecondChunks 3000
50#endif
51#endif
52
53 namespace WaitForIOReady_Support {
54 /**
55 * This is the underlying native type 'HighLevelType objects must be converted to in order to
56 * be used with the operating-system poll/select feature.
57 */
58#if qStroika_Foundation_Common_Platform_Windows
59 using SDKPollableType = SOCKET;
60#else
61 using SDKPollableType = int;
62#endif
63
64 /**
65 */
66 class WaitForIOReady_Base {
67 public:
68 /**
69 * This is the underlying native type 'T' objects must be converted to in order to be used with the OLD poll/select feature.
70 */
72
73 public:
74 /**
75 * @todo consider adding more params - like out of band flags - but doesn't immediately seem helpful -- LGP 2017-04-16
76 *
77 * \note Common::DefaultNames<> supported
78 */
79 enum class TypeOfMonitor {
80 /**
81 * There is data to read.
82 *
83 * @see http://man7.org/linux/man-pages/man2/poll.2.html - POLLIN
84 */
85 eRead,
86
87 /**
88 * Writing is now possible.
89 *
90 * @see http://man7.org/linux/man-pages/man2/poll.2.html - POLLOUT
91 */
92 eWrite,
93
94 /**
95 * Error condition.
96 *
97 * @see http://man7.org/linux/man-pages/man2/poll.2.html - POLLERR
98 */
99 eError,
100
101 /**
102 * stream-oriented connection was either disconnected or aborted.
103 *
104 * @see http://man7.org/linux/man-pages/man2/poll.2.html - POLLHUP
105 */
106 eHUP,
107
108 Stroika_Define_Enum_Bounds (eRead, eHUP)
109 };
110
111 public:
112 using TypeOfMonitorSet = Containers::Set<TypeOfMonitor>;
113
114 public:
115 /**
116 */
117 static inline const TypeOfMonitorSet kDefaultTypeOfMonitor{TypeOfMonitor::eRead};
118
119 protected:
120 /**
121 * Take an array of pair<SDKPollableType, TypeOfMonitorSet> objects, and return a Set{} with the INDEXES of 'ready' pollable objects.
122 *
123 * \note Design Note: This could have returned a set of pointer to SDKPollableType which would in some sense be simpler and
124 * clearer, but its easier to validate/assert the returned INDEXES are valid than the returned POINTERS are valid.
125 */
126 static auto _WaitQuietlyUntil (const pair<SDKPollableType, TypeOfMonitorSet>* start, const pair<SDKPollableType, TypeOfMonitorSet>* end,
128 };
129
130 /**
131 * (Private) utility to allow select() to wakeup without sending EINTR such signals...
132 *
133 * \note idea originally from https://stackoverflow.com/questions/12050072/how-to-wake-up-a-thread-being-blocked-by-select-poll-poll-function-from-anothe/22239521
134 *
135 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
136 */
137 class EventFD {
138 public:
139 EventFD () = default;
140
141 public:
142 virtual ~EventFD () = default;
143
144 public:
145 virtual bool IsSet () const = 0;
146
147 public:
148 virtual void Set () = 0;
149
150 public:
151 virtual void Clear () = 0;
152
153 public:
154 // return low level FD + set of poll events
155 virtual pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet> GetWaitInfo () = 0;
156 };
157 unique_ptr<EventFD> mkEventFD ();
158
159 template <typename T>
160 struct WaitForIOReady_Traits {
161 /**
162 * This is the type of object which is being wrapped (around a SDKPollableType object) and used with WaitForIOReady
163 */
164 using HighLevelType = T;
165
166 /**
167 * To use WaitForIOReady, the high level 'descriptor' objects used must be convertible to associated low level
168 * file descriptor objects to use with select/poll/etc...
169 */
170 static inline WaitForIOReady_Support::SDKPollableType GetSDKPollable (const HighLevelType& t)
171 {
172 return t;
173 }
174 };
175
176 }
177
178 /**
179 * Simple portable wrapper on OS select/2, pselect/2, poll/2, epoll (), and/or WaitForMultipleEvents(), etc
180 *
181 * \note pollable2Wakeup specifies an OPTIONAL file descriptor, which, if signalled (written to or whatever signal sent to it
182 * depending on the POLL arg to this field) - any pending waits will return prematurely.
183 *
184 * This can be used to trigger premature wakeup (without being treated as a timeout) - like if the list of file descriptors to watch
185 * changes.
186 *
187 * Alternatively, users may interrupt Execution::WaitForIOReady portably using
188 * Thread::Interrupt () (which is what Stroika generally did until v2.1a5) - but this is less efficient, and generates
189 * lots of log noise (dbgtrace). Also, interrupt means if there were real answers mixed with
190 * non-answers we would miss the real answers and this way captures them too)
191 *
192 * \par Example Usage
193 * \code
194 * Execution::WaitForIOReady waiter{fd};
195 * bool eof = false;
196 * while (not eof) {
197 * waiter.WaitQuietly (1);
198 * readALittleFromProcess (fd, stream, write2StdErrCache, &eof);
199 * }
200 * \endcode
201 *
202 * \par Example Usage
203 * \code
204 * Execution::WaitForIOReady sockSetPoller{socket2FDBijection.Image ()};
205 * while (true) {
206 * try {
207 * for (const auto& readyFD : sockSetPoller.WaitQuietly ()) {
208 * ConnectionOrientedMasterSocket::Ptr localSocketToAcceptOn = *socket2FDBijection.InverseLookup (readyFD);
209 * ConnectionOrientedStreamSocket::Ptr s = localSocketToAcceptOn.Accept ();
210 * fNewConnectionAcceptor (s);
211 * }
212 * }
213 * ...
214 * }
215 * \endcode
216 *
217 * \note WaitForIOReady internally uses SDKPollableType, which is a UNIX file descriptor or Windows SOCKET. TRAITS
218 * must be provided to map 'T' objects to that SDKPollableType. These are provided by default for most appropriate types.
219 *
220 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
221 */
222 template <typename T = WaitForIOReady_Support::SDKPollableType, typename TRAITS = WaitForIOReady_Support::WaitForIOReady_Traits<T>>
223 class [[nodiscard]] WaitForIOReady : public WaitForIOReady_Support::WaitForIOReady_Base {
224 public:
225 using TraitsType = TRAITS;
226
227 public:
228 /**
229 */
230 WaitForIOReady (WaitForIOReady&&) noexcept = default;
231 WaitForIOReady (const WaitForIOReady&) = default;
232 WaitForIOReady (const Traversal::Iterable<pair<T, TypeOfMonitorSet>>& fds,
233 optional<pair<SDKPollableType, TypeOfMonitorSet>> pollable2Wakeup = nullopt);
234 WaitForIOReady (const Traversal::Iterable<T>& fds, const TypeOfMonitorSet& flags = kDefaultTypeOfMonitor,
235 optional<pair<SDKPollableType, TypeOfMonitorSet>> pollable2Wakeup = nullopt);
236 WaitForIOReady (T fd, const TypeOfMonitorSet& flags = kDefaultTypeOfMonitor,
237 optional<pair<SDKPollableType, TypeOfMonitorSet>> pollable2Wakeup = nullopt);
238
239 public:
240 ~WaitForIOReady () = default;
241
242 public:
243 nonvirtual WaitForIOReady& operator= (WaitForIOReady&&) noexcept = default;
244 nonvirtual WaitForIOReady& operator= (const WaitForIOReady&) = default;
245
246 public:
247 /**
248 */
249 nonvirtual Traversal::Iterable<pair<T, TypeOfMonitorSet>> GetDescriptors () const;
250
251 public:
252 /*
253 * Waits the given amount of time, and returns as soon as any one (or more) requires service (see TypeOfMonitor), or pollable2Wakeup signaled (in which case may return empty set).
254 *
255 * \note Throws TimeOutException () on timeout.
256 *
257 * \note ***Cancelation Point***
258 *
259 * @see WaitQuietly
260 * @see WaitUntil
261 * @see WaitQuietlyUntil
262 */
263 nonvirtual Containers::Set<T> Wait (Time::DurationSeconds waitFor = Time::kInfinity);
264
265 public:
266 /*
267 * Waits the given amount of time, and returns as soon as any one (or more) requires service (see TypeOfMonitor).
268 *
269 * Returns set of file descriptors which are ready, or empty set if timeout, or if signaled by pollable2Wakeup.
270 *
271 * \note ***Cancelation Point***
272 *
273 * @see Wait
274 * @see WaitUntil
275 * @see WaitQuietlyUntil
276 */
277 nonvirtual Containers::Set<T> WaitQuietly (Time::DurationSeconds waitFor = Time::kInfinity);
278
279 public:
280 /*
281 * Waits until the given timeoutAt, and returns as soon as any one (or more) requires service (see TypeOfMonitor), or pollable2Wakeup signaled (in which case may return empty set)..
282 *
283 * \note Throws TimeOutException () on timeout.
284 *
285 * \note ***Cancelation Point***
286 *
287 * @see Wait
288 * @see WaitQuietly
289 * @see WaitQuietlyUntil
290 */
291 nonvirtual Containers::Set<T> WaitUntil (Time::TimePointSeconds timeoutAt = Time::TimePointSeconds{Time::kInfinity});
292
293 public:
294 /*
295 * Waits until the given timeoutAt, and returns as soon as any one (or more) requires service (see TypeOfMonitor), or pollable2Wakeup signaled (in which case may return empty set)..
296 *
297 * Returns set of file descriptors which are ready, or an empty set if time expired before any became ready.
298 *
299 * if timeout is <= 0, this will not wait (but may still find some file descriptors ready).
300 *
301 * \note ***Cancelation Point***
302 *
303 * @see Wait
304 * @see WaitQuietly
305 * @see WaitUntil
306 */
307 nonvirtual Containers::Set<T> WaitQuietlyUntil (Time::TimePointSeconds timeoutAt = Time::TimePointSeconds{Time::kInfinity});
308
309 private:
310 [[no_unique_address]] Debug::AssertExternallySynchronizedMutex fThisAssertExternallySynchronized_;
312 const optional<pair<SDKPollableType, TypeOfMonitorSet>> fPollable2Wakeup_;
313 };
314
315}
316
317/*
318 ********************************************************************************
319 ***************************** Implementation Details ***************************
320 ********************************************************************************
321 */
322#include "WaitForIOReady.inl"
323
324#endif /*_Stroika_Foundation_Execution_WaitForIOReady_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
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
Definition Set.h:105
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237