Stroika Library 3.0d23
 
Loading...
Searching...
No Matches
WaitForIOReady.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_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 * Poll Priority/Poll Urgent data (out-of-band data) is available for read - POLLPRI
96 *
97 * @see https://man7.org/linux/man-pages/man2/poll.2.html - It is used to detect "out-of-band" (OOB) data on sockets or high-priority, non-zero priority band data in STREAMS.
98 */
99 ePriority,
100
101 Stroika_Define_Enum_Bounds (eRead, ePriority)
102 };
103
104 public:
105 using TypeOfMonitorSet = Containers::Set<TypeOfMonitor>;
106
107 public:
108 /**
109 * default is to watch for read, error, and HUP (close) events (all but write).
110 */
111 // static inline const TypeOfMonitorSet kDefaultTypeOfMonitor{TypeOfMonitor::eRead, TypeOfMonitor::eError, TypeOfMonitor::eHUP};
112 static inline const TypeOfMonitorSet kDefaultTypeOfMonitor{TypeOfMonitor::eRead};
113
114 protected:
115 /**
116 * Take an array of pair<SDKPollableType, TypeOfMonitorSet> objects, and return a Set{} with the INDEXES of 'ready' pollable objects.
117 *
118 * \note Design Note: This could have returned a set of pointer to SDKPollableType which would in some sense be simpler and
119 * clearer, but its easier to validate/assert the returned INDEXES are valid than the returned POINTERS are valid.
120 */
121 static auto _WaitQuietlyUntil (const pair<SDKPollableType, TypeOfMonitorSet>* start, const pair<SDKPollableType, TypeOfMonitorSet>* end,
123 };
124
125 /**
126 * (Private) utility to allow select() to wakeup without sending EINTR such signals...
127 *
128 * \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
129 *
130 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
131 */
132 class EventFD {
133 public:
134 EventFD () = default;
135
136 public:
137 virtual ~EventFD () = default;
138
139 public:
140 virtual bool IsSet () const = 0;
141
142 public:
143 virtual void Set () = 0;
144
145 public:
146 virtual void Clear () = 0;
147
148 public:
149 // return low level FD + set of poll events
150 virtual pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet> GetWaitInfo () = 0;
151 };
152 unique_ptr<EventFD> mkEventFD ();
153
154 template <typename T>
155 struct WaitForIOReady_Traits {
156 /**
157 * This is the type of object which is being wrapped (around a SDKPollableType object) and used with WaitForIOReady
158 */
159 using HighLevelType = T;
160
161 /**
162 * To use WaitForIOReady, the high level 'descriptor' objects used must be convertible to associated low level
163 * file descriptor objects to use with select/poll/etc...
164 */
165 static inline WaitForIOReady_Support::SDKPollableType GetSDKPollable (const HighLevelType& t)
166 {
167 return t;
168 }
169 };
170
171 }
172
173 /**
174 * Simple portable wrapper on OS select/2, pselect/2, poll/2, epoll (), and/or WaitForMultipleEvents(), etc
175 *
176 * \note pollable2Wakeup specifies an OPTIONAL file descriptor, which, if signalled (written to or whatever signal sent to it
177 * depending on the POLL arg to this field) - any pending waits will return prematurely.
178 *
179 * This can be used to trigger premature wakeup (without being treated as a timeout) - like if the list of file descriptors to watch
180 * changes.
181 *
182 * Alternatively, users may interrupt Execution::WaitForIOReady portably using
183 * Thread::Interrupt () (which is what Stroika generally did until v2.1a5) - but this is less efficient, and generates
184 * lots of log noise (dbgtrace). Also, interrupt means if there were real answers mixed with
185 * non-answers we would miss the real answers and this way captures them too)
186 *
187 * \par Example Usage
188 * \code
189 * Execution::WaitForIOReady waiter{fd};
190 * bool eof = false;
191 * while (not eof) {
192 * waiter.WaitQuietly (1);
193 * readALittleFromProcess (fd, stream, write2StdErrCache, &eof);
194 * }
195 * \endcode
196 *
197 * \par Example Usage
198 * \code
199 * Execution::WaitForIOReady sockSetPoller{socket2FDBijection.Image ()};
200 * while (true) {
201 * try {
202 * for (const auto& readyFD : sockSetPoller.WaitQuietly ()) {
203 * ConnectionOrientedMasterSocket::Ptr localSocketToAcceptOn = *socket2FDBijection.InverseLookup (readyFD);
204 * ConnectionOrientedStreamSocket::Ptr s = localSocketToAcceptOn.Accept ();
205 * fNewConnectionAcceptor (s);
206 * }
207 * }
208 * ...
209 * }
210 * \endcode
211 *
212 * \note WaitForIOReady internally uses SDKPollableType, which is a UNIX file descriptor or Windows SOCKET. TRAITS
213 * must be provided to map 'T' objects to that SDKPollableType. These are provided by default for most appropriate types.
214 *
215 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
216 *
217 * \note pointless to ever create ObjectVariantMapper and not use, so [[nodiscard]] appropriate
218 */
219 template <typename T = WaitForIOReady_Support::SDKPollableType, typename TRAITS = WaitForIOReady_Support::WaitForIOReady_Traits<T>>
220 class [[nodiscard]] WaitForIOReady : public WaitForIOReady_Support::WaitForIOReady_Base {
221 public:
222 using TraitsType = TRAITS;
223
224 public:
225 /**
226 */
227 WaitForIOReady (WaitForIOReady&&) noexcept = default;
228 WaitForIOReady (const WaitForIOReady&) = default;
229 WaitForIOReady (const Traversal::Iterable<pair<T, TypeOfMonitorSet>>& fds,
230 optional<pair<SDKPollableType, TypeOfMonitorSet>> pollable2Wakeup = nullopt);
231 WaitForIOReady (const Traversal::Iterable<T>& fds, const TypeOfMonitorSet& flags = kDefaultTypeOfMonitor,
232 optional<pair<SDKPollableType, TypeOfMonitorSet>> pollable2Wakeup = nullopt);
233 WaitForIOReady (T fd, const TypeOfMonitorSet& flags = kDefaultTypeOfMonitor,
234 optional<pair<SDKPollableType, TypeOfMonitorSet>> pollable2Wakeup = nullopt);
235
236 public:
237 ~WaitForIOReady () = default;
238
239 public:
240 nonvirtual WaitForIOReady& operator= (WaitForIOReady&&) noexcept = default;
241 nonvirtual WaitForIOReady& operator= (const WaitForIOReady&) = default;
242
243 public:
244 /**
245 */
246 nonvirtual Traversal::Iterable<pair<T, TypeOfMonitorSet>> GetDescriptors () const;
247
248 public:
249 /*
250 * 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).
251 *
252 * \note Throws TimeOutException () on timeout.
253 *
254 * \note ***Cancelation Point***
255 *
256 * @see WaitQuietly
257 * @see WaitUntil
258 * @see WaitQuietlyUntil
259 */
260 nonvirtual Containers::Set<T> Wait (Time::DurationSeconds waitFor = Time::kInfinity);
261
262 public:
263 /*
264 * Waits the given amount of time, and returns as soon as any one (or more) requires service (see TypeOfMonitor).
265 *
266 * Returns set of file descriptors which are ready, or empty set if timeout, or if signaled by pollable2Wakeup.
267 *
268 * \note ***Cancelation Point***
269 *
270 * @see Wait
271 * @see WaitUntil
272 * @see WaitQuietlyUntil
273 */
274 nonvirtual Containers::Set<T> WaitQuietly (Time::DurationSeconds waitFor = Time::kInfinity);
275
276 public:
277 /*
278 * 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)..
279 *
280 * \note Throws TimeOutException () on timeout.
281 *
282 * \note ***Cancelation Point***
283 *
284 * @see Wait
285 * @see WaitQuietly
286 * @see WaitQuietlyUntil
287 */
288 nonvirtual Containers::Set<T> WaitUntil (Time::TimePointSeconds timeoutAt = Time::TimePointSeconds{Time::kInfinity});
289
290 public:
291 /*
292 * 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)..
293 *
294 * Returns set of file descriptors which are ready, or an empty set if time expired before any became ready.
295 *
296 * if timeout is <= 0, this will not wait (but may still find some file descriptors ready).
297 *
298 * \note ***Cancelation Point***
299 *
300 * @see Wait
301 * @see WaitQuietly
302 * @see WaitUntil
303 */
304 nonvirtual Containers::Set<T> WaitQuietlyUntil (Time::TimePointSeconds timeoutAt = Time::TimePointSeconds{Time::kInfinity});
305
306 private:
309 const optional<pair<SDKPollableType, TypeOfMonitorSet>> fPollable2Wakeup_;
310 };
311
312}
313
314/*
315 ********************************************************************************
316 ***************************** Implementation Details ***************************
317 ********************************************************************************
318 */
319#include "WaitForIOReady.inl"
320
321#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
#define qStroika_ATTRIBUTE_NO_UNIQUE_ADDRESS_VCFORCE
[[msvc::no_unique_address]] isn't always broken in MSVC. Annotate with this on things where its not b...
Definition StdCompat.h:445
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
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