155 lines
4.0 KiB
C++
155 lines
4.0 KiB
C++
#pragma once
|
|
|
|
//#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <Windows.h>
|
|
#include <concepts>
|
|
#include <type_traits>
|
|
|
|
#include <LdH/exception.hpp>
|
|
|
|
|
|
namespace LdH::Asyncio {
|
|
#if 0
|
|
template<class ret_t, template<typename> class lambda_t>
|
|
concept OverlappedFunc = std::invocable<lambda_t<ret_t>, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE>
|
|
&& std::same_as<std::invoke_result_t<lambda_t<ret_t>, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE>, ret_t>;
|
|
#endif
|
|
|
|
template<class lambda_t>
|
|
concept OverlappedFunc = std::invocable<lambda_t, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE>
|
|
&& std::same_as<std::invoke_result_t<lambda_t, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE>, DWORD>;
|
|
|
|
class WindowsOverlappedEventloop {
|
|
|
|
private:
|
|
static thread_local struct _running_loop_variable_t {
|
|
private:
|
|
WindowsOverlappedEventloop *_value;
|
|
|
|
public:
|
|
[[nodiscard]]
|
|
WindowsOverlappedEventloop *get() {
|
|
auto cached = this->_value;
|
|
if (cached == nullptr) throw LdH::Exception{"No running loop"};
|
|
return cached;
|
|
}
|
|
|
|
[[nodiscard]]
|
|
WindowsOverlappedEventloop *getOrNull() {
|
|
return this->_value;
|
|
}
|
|
|
|
void set(WindowsOverlappedEventloop *v) {
|
|
if (this->_value != nullptr && v != nullptr) throw LdH::Exception{"Recursive loops not allowed"};
|
|
this->_value = v;
|
|
}
|
|
} _running_loop;
|
|
|
|
struct FiberMetadata {
|
|
public:
|
|
OVERLAPPED overlapped;
|
|
|
|
void *fiber_id;
|
|
|
|
FiberMetadata *next_existing;
|
|
FiberMetadata *prev_existing;
|
|
FiberMetadata *next_active;
|
|
FiberMetadata *prev_active;
|
|
|
|
DWORD err_code;
|
|
DWORD read_count;
|
|
};
|
|
|
|
|
|
HANDLE const notifier;
|
|
bool const need_rollback_to_thread;
|
|
void *const master_fiber;
|
|
bool interrupted;
|
|
FiberMetadata *next_existing;
|
|
FiberMetadata *next_active;
|
|
DWORD const fls_fiber_metadata;
|
|
void *fiber_to_destroy;
|
|
|
|
public:
|
|
WindowsOverlappedEventloop();
|
|
|
|
~WindowsOverlappedEventloop();
|
|
|
|
private:
|
|
[[nodiscard]]
|
|
HANDLE createEvent() const;
|
|
|
|
[[nodiscard]]
|
|
HANDLE prepareFibers() const;
|
|
|
|
[[nodiscard]]
|
|
DWORD allocFls() const;
|
|
|
|
public:
|
|
void runUntilHasTasksAndNotInterrupted();
|
|
|
|
private:
|
|
void _addTask(void (*routine)(void *), void *arg);
|
|
|
|
static void _overlapped_completion_routine(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, FiberMetadata *lpOverlapped);
|
|
|
|
public:
|
|
template<class T>
|
|
static void addTaskToRuningLoop(void (*routine)(T *), T *arg) {
|
|
WindowsOverlappedEventloop::_running_loop.get()->_addTask(
|
|
reinterpret_cast<void (*)(void *)>(routine),
|
|
reinterpret_cast<void *>(arg)
|
|
);
|
|
}
|
|
|
|
template<class T>
|
|
void addTask(void (*routine)(T *), T *arg) {
|
|
this->_addTask(
|
|
reinterpret_cast<void (*)(void *)>(routine),
|
|
reinterpret_cast<void *>(arg)
|
|
);
|
|
}
|
|
|
|
static DWORD overlappedCall(OverlappedFunc auto func) {
|
|
auto loop = WindowsOverlappedEventloop::_running_loop.get();
|
|
auto metadata = reinterpret_cast<FiberMetadata *>(FlsGetValue(loop->fls_fiber_metadata));
|
|
if (metadata == nullptr) LdH::Exception::throwFromLastWindowsErr();
|
|
|
|
DWORD read_count = func(reinterpret_cast<LPOVERLAPPED>(metadata), reinterpret_cast<LPOVERLAPPED_COMPLETION_ROUTINE>(&WindowsOverlappedEventloop::_overlapped_completion_routine));
|
|
if (read_count != 0) return read_count;
|
|
|
|
if (loop->next_active == metadata)
|
|
loop->next_active = metadata->next_active;
|
|
if (metadata->next_active != nullptr)
|
|
metadata->next_active->prev_active = metadata->prev_active;
|
|
if (metadata->prev_active != nullptr)
|
|
metadata->prev_active->next_active = metadata->next_active;
|
|
metadata->next_active = nullptr;
|
|
metadata->prev_active = nullptr;
|
|
|
|
SwitchToFiber(loop->master_fiber);
|
|
|
|
if (metadata->err_code != ERROR_SUCCESS)
|
|
LdH::Exception::throwFromWindowsErrCode(metadata->err_code);
|
|
return metadata->read_count;
|
|
}
|
|
|
|
static void interruptEventLoop() {
|
|
WindowsOverlappedEventloop::_running_loop.get()->interrupted = true;
|
|
}
|
|
|
|
private:
|
|
struct FiberInitializer {
|
|
WindowsOverlappedEventloop *loop;
|
|
|
|
void (*routine)(void *);
|
|
|
|
void *routine_arg;
|
|
void *caller_fiber;
|
|
};
|
|
|
|
static void fiber_kernel(FiberInitializer *init);
|
|
};
|
|
}
|