4#include "Stroika/Foundation/StroikaPreComp.h"
9#if defined(__cpp_lib_jthread)
13#if qStroika_Platform_POSIX
19#if qStroika_Platform_Linux
20#include <sys/eventfd.h>
22#elif qStroika_Platform_Windows
30#include "Stroika/Foundation/Containers/Sequence.h"
34#include "Stroika/Foundation/Execution/Exceptions.h"
37#if qStroika_Platform_Windows
38#include "Stroika/Foundation/Execution/Platform/Windows/WaitSupport.h"
41#include "Stroika/Foundation/IO/Network/ConnectionOrientedStreamSocket.h"
42#if qStroika_Platform_Windows
43#include "Stroika/Foundation/IO/Network/Platform/Windows/WinSock.h"
52using namespace Stroika::Foundation::Execution::WaitForIOReady_Support;
90 struct EventFD_Based_ :
public EventFD {
92 EventFD_Based_ () =
default;
93 virtual bool IsSet ()
const override
97 virtual void Set ()
override
100 scoped_lock critSec{fMutex_};
106 virtual void Clear ()
override
108 scoped_lock critSec{fMutex_};
116 virtual void _ReadAllAvail () = 0;
117 virtual void _WriteOne () = 0;
121 atomic<bool> fIsSet_{
false};
124#if qStroika_Platform_Linux
135 struct EventFD_Based_eventfd_ : EventFD_Based_ {
136 EventFD_Based_eventfd_ ()
140 ~EventFD_Based_eventfd_ ()
144 virtual pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet> GetWaitInfo ()
override
146 return pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet>{
149 virtual void _ReadAllAvail ()
override
152 if (::read (fFD_, &counter,
sizeof (counter)) < 0 and errno != EAGAIN) {
156 virtual void _WriteOne ()
override
158 constexpr uint64_t kOne_{1};
163#elif qStroika_Platform_POSIX
176 struct EventFD_Based_Pipe_ : EventFD_Based_ {
177 EventFD_Based_Pipe_ ()
182 if (::fcntl (fd, F_SETFL, ::fcntl (fd, F_GETFL) | O_NONBLOCK) < 0 or ::fcntl (fd, F_SETFD, FD_CLOEXEC) < 0) {
192 ~EventFD_Based_Pipe_ ()
197 virtual pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet> GetWaitInfo ()
override
199 return pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet>{
202 virtual void _ReadAllAvail ()
override
206 while ((n = ::read (fReadFD_, buf,
sizeof (buf))) > 0)
208 if (n < 0 and errno != EAGAIN) {
212 virtual void _WriteOne ()
override
214 constexpr byte kOne_{1};
215 if (::write (fWriteFD_, &kOne_, 1) < 0 and errno != EAGAIN) {
222#elif qStroika_Platform_Windows
237 struct EventFD_Based_UDPLoopback_ : EventFD_Based_ {
238 EventFD_Based_UDPLoopback_ ()
240 IO::Network::Platform::Windows::WinSock::AssureStarted ();
241 fSocket_ = ::WSASocketW (AF_INET, SOCK_DGRAM, IPPROTO_UDP,
nullptr, 0, WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT);
242 if (fSocket_ == INVALID_SOCKET) {
243 ThrowSystemErrNo (::WSAGetLastError ());
246 addr.sin_family = AF_INET;
247 addr.sin_addr.s_addr = ::htonl (INADDR_LOOPBACK);
249 int addrLen =
sizeof (addr);
250 u_long nonBlocking = 1;
251 if (::bind (fSocket_,
reinterpret_cast<const sockaddr*
> (&addr),
sizeof (addr)) == SOCKET_ERROR or
252 ::getsockname (fSocket_,
reinterpret_cast<sockaddr*
> (&addr), &addrLen) == SOCKET_ERROR or
253 ::connect (fSocket_,
reinterpret_cast<const sockaddr*
> (&addr),
sizeof (addr)) == SOCKET_ERROR or
254 ::ioctlsocket (fSocket_, FIONBIO, &nonBlocking) == SOCKET_ERROR) {
255 int e = ::WSAGetLastError ();
256 ::closesocket (fSocket_);
257 ThrowSystemErrNo (e);
260 ~EventFD_Based_UDPLoopback_ ()
262 ::closesocket (fSocket_);
264 virtual pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet> GetWaitInfo ()
override
266 return pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet>{
269 virtual void _ReadAllAvail ()
override
272 while (::recv (fSocket_, buf,
sizeof (buf), 0) != SOCKET_ERROR)
274 if (
int e = ::WSAGetLastError (); e != WSAEWOULDBLOCK) {
275 ThrowSystemErrNo (e);
278 virtual void _WriteOne ()
override
280 constexpr char kOne_{1};
281 if (::send (fSocket_, &kOne_, 1, 0) == SOCKET_ERROR) {
282 if (
int e = ::WSAGetLastError (); e != WSAEWOULDBLOCK) {
283 ThrowSystemErrNo (e);
299 struct EventFD_Based_SocketPair_ : EventFD_Based_ {
300 EventFD_Based_SocketPair_ ()
303 auto [r, w] = ConnectionOrientedStreamSocket::NewPair (SocketAddress::FamilyType::INET, Socket::Type::STREAM);
310 virtual pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet> GetWaitInfo ()
override
313 return pair<SDKPollableType, WaitForIOReady_Base::TypeOfMonitorSet>{
316 virtual void _ReadAllAvail ()
override
320 while (fReadSocket_.ReadNonBlocking (buf))
323 virtual void _WriteOne ()
override
326 constexpr byte kOne_{1};
327 fWriteSocket_.Write (span{&kOne_, 1});
338unique_ptr<EventFD> WaitForIOReady_Support::mkEventFD ()
342#if qStroika_Platform_Linux
343 return make_unique<EventFD_Based_eventfd_> ();
344#elif qStroika_Platform_POSIX
345 return make_unique<EventFD_Based_Pipe_> ();
346#elif qStroika_Platform_Windows
347 return make_unique<EventFD_Based_UDPLoopback_> ();
349 return make_unique<EventFD_Based_SocketPair_> ();
353#if qStroika_Foundation_Execution_WaitForIOReady_UseStopTokenAbortWakeup
360 EventFD& GetThreadAbortWakeupEventFD_ ()
362 static thread_local std::unique_ptr<EventFD> tWakeup_{WaitForIOReady_Support::mkEventFD ()};
382 void operator() () const noexcept
391 DbgTrace (
"WARNING: WaitForIOReady abort wakeup Set () failed: {}"_f, Characters::ToString (std::current_exception ()));
403auto WaitForIOReady_Base::_WaitQuietlyUntil (
const pair<SDKPollableType, TypeOfMonitorSet>* start,
406#if USE_NOISY_TRACE_IN_THIS_MODULE_
407 Debug::TraceContextBumper ctx{
"WaitForIOReady_Base::_WaitQuietlyUntil",
"args={}"_f, vector<pair<SDKPollableType, TypeOfMonitorSet>>{start, end}};
409 DurationSeconds time2Wait = Math::AtLeast<DurationSeconds> (timeoutAt - Time::GetTickCount (), 0s);
411 const size_t nMonitored =
static_cast<size_t> (end - start);
412 EventFD* abortWakeup_ =
nullptr;
413#if qStroika_Foundation_Execution_WaitForIOReady_UsePPoll
423 sigset_t originalSignalMask;
425 sigset_t blockInterruptSignal;
426 ::sigemptyset (&blockInterruptSignal);
427 ::sigaddset (&blockInterruptSignal, Thread::SignalUsedForThreadInterrupt ());
428 Verify (::pthread_sigmask (SIG_BLOCK, &blockInterruptSignal, &originalSignalMask) == 0);
431 [[maybe_unused]]
auto&& restoreSignalMask =
432 Finally ([&originalSignalMask] ()
noexcept { ::pthread_sigmask (SIG_SETMASK, &originalSignalMask,
nullptr); });
434#if qStroika_Foundation_Execution_WaitForIOReady_UseStopTokenAbortWakeup
442 std::optional<std::stop_callback<SetEventFD_>> abortWakeupRegistration;
443 if (std::optional<std::stop_token> stopToken = Thread::GetCurrentThreadStopToken ()) {
453 if (stopToken->stop_requested ()) {
457 abortWakeup_ = &GetThreadAbortWakeupEventFD_ ();
458 abortWakeup_->Clear ();
459 abortWakeupRegistration.emplace (*stopToken, SetEventFD_{abortWakeup_});
462 StackBuffer<pollfd> pollData;
464 pollData.GrowToSize_uninitialized (nMonitored + (abortWakeup_ ==
nullptr ? 0 : 1));
466 for (
auto i = start; i != end; ++i) {
468 for (TypeOfMonitor ii : i->second) {
470 case TypeOfMonitor::eRead:
473 case TypeOfMonitor::eWrite:
476 case TypeOfMonitor::ePriority:
481 pollData[idx] = pollfd{i->first, events, 0};
482 Assert (pollData[idx].revents == 0);
485 if (abortWakeup_ !=
nullptr) {
486 Assert (idx == nMonitored);
488 pollData[idx] = pollfd{abortWakeup_->GetWaitInfo ().first, POLLIN, 0};
491 [[maybe_unused]]
int timeoutMilliseconds = Math::Round<int> (time2Wait.count () * 1000);
492 Assert (timeoutMilliseconds >= 0);
499#if qStroika_Platform_Windows
500#if !qStroika_Foundation_Execution_WaitForIOReady_UseStopTokenAbortWakeup
501 Require (sPollWaitChunkTime > 0s);
504 DurationSeconds timeLeft2Wait = Math::AtLeast<DurationSeconds> (timeoutAt - Time::GetTickCount (), 0s);
505 DurationSeconds time2WaitThisLoop = clamp<DurationSeconds> (timeLeft2Wait, 0s, sPollWaitChunkTime);
506 int time2WaitMillisecondsThisLoop =
static_cast<int> (time2WaitThisLoop.count () * 1000);
507 if ((pollResult = ::WSAPoll (pollData.begin (),
static_cast<ULONG
> (pollData.GetSize ()), time2WaitMillisecondsThisLoop)) == SOCKET_ERROR) {
508 ThrowSystemErrNo (::WSAGetLastError ());
510 if (pollResult != 0 or Time::GetTickCount () >= timeoutAt) {
516 if ((pollResult = ::WSAPoll (pollData.begin (),
static_cast<ULONG
> (pollData.GetSize ()), timeoutMilliseconds)) == SOCKET_ERROR) {
519 ThrowSystemErrNo (::WSAGetLastError ());
522#elif qStroika_Foundation_Execution_WaitForIOReady_UsePPoll
524 struct timespec timeoutSpec{};
525 timeoutSpec.tv_sec = timeoutMilliseconds / 1000;
526 timeoutSpec.tv_nsec = (timeoutMilliseconds % 1000) * 1000000L;
528 [&] () { return ::ppoll (pollData.begin (), pollData.GetSize (), &timeoutSpec, &originalSignalMask); });
529#elif !qStroika_Foundation_Execution_WaitForIOReady_UseStopTokenAbortWakeup
530 Require (sPollWaitChunkTime > 0s);
532 DurationSeconds timeLeft2Wait = Math::AtLeast<DurationSeconds> (timeoutAt - Time::GetTickCount (), 0s);
533 DurationSeconds time2WaitThisLoop = clamp<DurationSeconds> (timeLeft2Wait, 0s, sPollWaitChunkTime);
534 int thisLoopMilliseconds =
static_cast<int> (time2WaitThisLoop.count () * 1000);
537 if (pollResult != 0 or Time::GetTickCount () >= timeoutAt) {
550 if (abortWakeup_ !=
nullptr) {
553 abortWakeup_->Clear ();
556#if USE_NOISY_TRACE_IN_THIS_MODULE_
559 if (pollResult != 0) {
561 for (
size_t i = 0; i < nMonitored; ++i) {
562 if (pollData[i].revents != 0) {
563#if USE_NOISY_TRACE_IN_THIS_MODULE_
564 dbgResult += start[i].first;
570#if USE_NOISY_TRACE_IN_THIS_MODULE_
571 DbgTrace (
"returning {}"_f, dbgResult);
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
A generalization of a vector: a container whose elements are keyed by the natural numbers.
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Add(ArgByValueType< value_type > item)
nonvirtual PlatformNativeHandle GetNativeSocket() const
Logically halfway between std::array and std::vector; Smart 'direct memory array' - which when needed...
void CheckForInterruption()
void ThrowPOSIXErrNo(errno_t errNo=errno)
treats argument errNo as a POSIX errno value, and calls ThrowError () with an error_code constructed ...
auto Handle_ErrNoResultInterruption(CALL call) -> decltype(call())
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from th...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
INT_TYPE ThrowPOSIXErrNoIfNegative(INT_TYPE returnCode)