/*** Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: Loop.hpp Date: 2021-8-21 Author: Reece Notes: This API class is specifically for kernel objects or similar userland schedular handles, this is not comparable to the async subsystem. While you may drive low perf user facing apps and small services from this, other services should divide and conquer * Allow the network subsystem to load balance sockets across a predefined amount of workers, * Use semaphores instead of massive lengthy arrays of mutexes * Use grouped long-polling LoopSources to convert kernel or waitable objects to a single time-polled loop source (freq based thread runners, alt to eEfficient) ***/ #pragma once #include "ELoopSource.hpp" #include "ILoopSource.hpp" #include "ILoopSourceSubscriber.hpp" #include "ILoopQueue.hpp" namespace Aurora::IO::Loop { // TODO: Win32 enters an alertable state, does not break // Linux does not enter an alertable state, but will soon-ish // There is no flag for enter alertable state // Cannot reach true parity with WaitForMultipleObjectsEx in this current state // Also, there is no flag for awoken by APC // optTimeoutMS = {} | indefinite // optTimeoutMS = 0 | poll // optTimeoutMS = 1 | 1ms AUKN_SYM bool WaitMultipleLoopSources(const AuList> &lsList, AuList> &signaled, bool bAny = { true }, AuOptional optTimeoutMS = {}); static const AuUInt64 kWaitMultipleFlagAny = 1 << 0; static const AuUInt64 kWaitMultipleFlagNoSpin = 1 << 1; static const AuUInt64 kWaitMultipleFlagAvoidKern = 1 << 2; static const AuUInt64 kWaitMultipleFlagBreakAfterOne = 1 << 3; AUKN_SYM bool WaitMultipleLoopSourcesEx(const AuList> &lsList, AuList> &signaled, AuUInt64 uFlags = { kWaitMultipleFlagAny }, AuOptional optTimeoutMS = {}); struct ILSSemaphore : virtual ILoopSource { virtual bool AddOne() = 0; virtual bool AddMany(AuUInt32 uCount) = 0; }; struct ILSEvent : virtual ILoopSource { virtual bool Set() = 0; virtual bool Reset() = 0; }; struct ILSMutex : virtual ILoopSource { virtual bool Unlock() = 0; }; struct ITimer : virtual ILoopSource { /* Warning: IO timers use wall time (CurrentClock[M/NS), not SteadyClock[M/N]S()). Use auasync timers for steady-clock timing and tick timing information. */ virtual void UpdateTime(AuUInt64 absTimeMs) = 0; virtual void UpdateTimeNs(AuUInt64 absTimeNs) = 0; virtual void UpdateTickRateIfAny(AuUInt32 reschedStepMsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0; virtual void UpdateTickRateIfAnyNs(AuUInt64 reschedStepNsOrZero = 0, AuUInt32 maxIterationsOrZero = 0) = 0; virtual void Stop() = 0; }; struct ILSSignalCatcher : virtual ILoopSource { virtual void *GetLastSignalInfo() = 0; }; AUKN_SYM AuSPtr NewLSTimer(AuUInt64 absStartTimeMs /*CurrentClockMS()*/, AuUInt32 reschedStepMsOrZero = 0, AuUInt32 maxIterationsOrZero = 0, bool bSingleshot = false /*cannot be changed*/); // warn: no IPC counterpart AUKN_SYM AuSPtr NewLSMutex(); AUKN_SYM AuSPtr NewLSMutexSlow(); // interop-ready (usable with DbgLoopSourceToReadFd) AUKN_SYM AuSPtr NewLSEvent(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false); AUKN_SYM AuSPtr NewLSEventSlow(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false); // interop-ready (usable with DbgLoopSourceToReadFd) AUKN_SYM AuSPtr NewLSSemaphoreSlow(AuUInt32 uInitialCount = 0); // interop-ready (usable with DbgLoopSourceToReadFd) AUKN_SYM AuSPtr NewLSSemaphore(AuUInt32 uInitialCount = 0); AUKN_SYM AuSPtr NewLSSemaphoreEx(AuUInt32 uInitialCount = 0, AuUInt32 uMaxCount = 0); // warn: no IPC counterpart AUKN_SYM AuSPtr NewLSOSHandle(AuUInt); AUKN_SYM AuSPtr NewLSAsync(Aurora::Async::WorkerPId_t workerPid); // warn: no IPC counterpart AUKN_SYM AuSPtr NewLSFile(const AuSPtr &fileTransaction); // warn: no IPC counterpart AUKN_SYM AuSPtr NewStdIn(); AUKN_SYM AuSPtr NewLSWin32Source(bool dispatchMessages); AUKN_SYM AuSPtr NewLSAppleSource(); AUKN_SYM AuSPtr NewLSIOHandle(const AuSPtr &pHandle); // See: AuIO::IPC for IPC event loop objects // warn: Only works on singular loop sources // warn: You should only use trust the interop-ready sources to serve the HANDLE/fd you expect. // The primary loop-source primitives are capable of bypassing the kernels io scheduler via in-process atomics. // These can be leveraged with AuLoop::WaitMultipleLoopSources with kWaitMultipleFlagAvoidKern (and future planned ILoopSource methods.) // For scheduling io events, the atomic optimizations are pointless. They can, however, serve to deduplicate multiple event triggers // and optimize the wait-on operation of win32-on-unix/win32 emulators. // In addition, D3D, Winsock, NT IO, and other subsystems of a modern coreos system expects (or at least, are documented to expect) // kernel event handle objects for synchronization - not semaphores with some atomic word in a process god knows where. AUKN_SYM AuInt64 DbgLoopSourceToReadFd(AuSPtr pLoopSource); // warn: Only works on singular loop sources AUKN_SYM AuInt64 DbgLoopSourceToWriteFd(AuSPtr pLoopSource); #if defined(X_PROTOCOL) static AuSPtr NewLSX11(Display *display) { return NewLSOSHandle(ConnectionNumber(display)); } #endif #if defined(AURORA_IS_POSIX_DERIVED) static AuSPtr NewLSFd(int fd) { if (fd < 0) { return {}; } return NewLSOSHandle(fd); } #endif #if defined(AURORA_IS_MODERNNT_DERIVED) && defined(_AU_SAW_WIN32_EARLY) static AuSPtr NewLSHandle(HANDLE handle) // even though a handle is just a void* { if (handle == INVALID_HANDLE_VALUE /*and we could just hard-code the literal here*/) return {}; return NewLSOSHandle(reinterpret_cast(handle)); } #endif #if defined(AURORA_IS_LINUX_DERIVED) AUKN_SYM AuSPtr NewLSSignalCatcher(const AuList &signals); #endif }