AuroraRuntime/Include/Aurora/IO/Loop/Loop.hpp

135 lines
6.6 KiB
C++

/***
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
{
// optTimeoutMS = {} | indefinite
// optTimeoutMS = 0 | poll
// optTimeoutMS = 1 | 1ms
AUKN_SYM bool WaitMultipleLoopSources(const AuList<AuSPtr<Loop::ILoopSource>> &lsList,
AuList<AuSPtr<Loop::ILoopSource>> &signaled,
bool bAny = { true },
AuOptional<AuUInt32> 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<AuSPtr<Loop::ILoopSource>> &lsList,
AuList<AuSPtr<Loop::ILoopSource>> &signaled,
AuUInt64 uFlags = { kWaitMultipleFlagAny },
AuOptional<AuUInt32> 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<ITimer> NewLSTimer(AuUInt64 absStartTimeMs /*CurrentClockMS()*/, AuUInt32 reschedStepMsOrZero = 0, AuUInt32 maxIterationsOrZero = 0, bool bSingleshot = false /*cannot be changed*/); // warn: no IPC counterpart
AUKN_SYM AuSPtr<ILSMutex> NewLSMutex();
AUKN_SYM AuSPtr<ILSMutex> NewLSMutexSlow(); // interop-ready (usable with DbgLoopSourceToReadFd)
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false);
AUKN_SYM AuSPtr<ILSEvent> NewLSEventSlow(bool bTriggered = false, bool bAtomicRelease = true, bool bPermitMultipleTriggers = false); // interop-ready (usable with DbgLoopSourceToReadFd)
AUKN_SYM AuSPtr<ILSSemaphore> NewLSSemaphoreSlow(AuUInt32 uInitialCount = 0); // interop-ready (usable with DbgLoopSourceToReadFd)
AUKN_SYM AuSPtr<ILSSemaphore> NewLSSemaphore(AuUInt32 uInitialCount = 0);
AUKN_SYM AuSPtr<ILSSemaphore> NewLSSemaphoreEx(AuUInt32 uInitialCount = 0, AuUInt32 uMaxCount = 0); // warn: no IPC counterpart
AUKN_SYM AuSPtr<ILoopSource> NewLSOSHandle(AuUInt);
AUKN_SYM AuSPtr<ILoopSource> NewLSAsync(Aurora::Async::WorkerPId_t workerPid); // warn: no IPC counterpart
AUKN_SYM AuSPtr<ILoopSource> NewLSFile(const AuSPtr<IO::IAsyncTransaction> &fileTransaction); // warn: no IPC counterpart
AUKN_SYM AuSPtr<ILoopSource> NewStdIn();
AUKN_SYM AuSPtr<ILoopSource> NewLSWin32Source(bool dispatchMessages);
AUKN_SYM AuSPtr<ILoopSource> NewLSAppleSource();
AUKN_SYM AuSPtr<ILoopSource> NewLSIOHandle(const AuSPtr<IIOHandle> &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<ILoopSource> pLoopSource);
// warn: Only works on singular loop sources
AUKN_SYM AuInt64 DbgLoopSourceToWriteFd(AuSPtr<ILoopSource> pLoopSource);
#if defined(X_PROTOCOL)
static AuSPtr<ILoopSource> NewLSX11(Display *display)
{
return NewLSOSHandle(ConnectionNumber(display));
}
#endif
#if defined(AURORA_IS_POSIX_DERIVED)
static AuSPtr<ILoopSource> NewLSFd(int fd)
{
if (fd < 0)
{
return {};
}
return NewLSOSHandle(fd);
}
#endif
#if defined(AURORA_IS_MODERNNT_DERIVED) && defined(_AU_SAW_WIN32_EARLY)
static AuSPtr<ILoopSource> 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<AuUInt>(handle));
}
#endif
#if defined(AURORA_IS_LINUX_DERIVED)
AUKN_SYM AuSPtr<ILSSignalCatcher> NewLSSignalCatcher(const AuList<int> &signals);
#endif
}