Configured 'import std' in cmake and implemented and stub for threading module on windows
This commit is contained in:
commit
4aad26efa7
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/*build*/
|
||||
/.idea/
|
||||
26
CMakeLists.txt
Normal file
26
CMakeLists.txt
Normal file
@ -0,0 +1,26 @@
|
||||
cmake_minimum_required(VERSION 3.30 FATAL_ERROR)
|
||||
|
||||
message(STATUS "CMake version is ${CMAKE_VERSION}")
|
||||
if (${CMAKE_VERSION} STREQUAL "4.1.1")
|
||||
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "d0edc3af-4c50-42ea-a356-e2862fe7a444")
|
||||
elseif (${CMAKE_VERSION} STREQUAL "4.0.2")
|
||||
set(CMAKE_EXPERIMENTAL_CXX_IMPORT_STD "a9e1cf81-9932-4810-974b-6eccaf14e457")
|
||||
else ()
|
||||
message(FATAL_ERROR "Can't enable CMAKE_EXPERIMENTAL_CXX_IMPORT_STD for this version of cmake, please edit top-lvl CMakeLists.txt")
|
||||
endif()
|
||||
|
||||
project(networks_1 LANGUAGES CXX)
|
||||
|
||||
set(CMP0155 NEW)
|
||||
set(CMAKE_CXX_STANDARD 23)
|
||||
set(CMAKE_CXX_SCAN_FOR_MODULES ON)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||
set(CMAKE_CXX_MODULE_STD 1)
|
||||
|
||||
add_subdirectory(modules/exceptions)
|
||||
add_subdirectory(modules/threads)
|
||||
add_subdirectory(modules/asyncio)
|
||||
add_subdirectory(programs/lab4)
|
||||
|
||||
add_executable(main main.cpp)
|
||||
target_link_libraries(main PRIVATE exceptions threads)
|
||||
18
main.cpp
Normal file
18
main.cpp
Normal file
@ -0,0 +1,18 @@
|
||||
#include <iostream>
|
||||
|
||||
import ru.landgrafhomyak.BGTU.networks_1.exceptions;
|
||||
import ru.landgrafhomyak.BGTU.networks_1.threads;
|
||||
|
||||
|
||||
int main() {
|
||||
try {
|
||||
auto t = LdH::fork("123", []() { std::cout << "123" << std::endl; });
|
||||
t.join();
|
||||
t.destroy();
|
||||
} catch (LdH::Exception const &e) {
|
||||
std::cerr << "Uncaught exception in main:\n";
|
||||
e.printStackTrace();
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << "Uncaught exception in main:\n" << e.what() << "\n";
|
||||
}
|
||||
}
|
||||
7
modules/asyncio/CMakeLists.txt
Normal file
7
modules/asyncio/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
||||
add_library(
|
||||
asyncio OBJECT
|
||||
./Include/LdH/asyncio.hpp
|
||||
src/eventloop.cpp
|
||||
)
|
||||
target_include_directories(exceptions PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/Include)
|
||||
target_link_libraries(asyncio PUBLIC exceptions)
|
||||
154
modules/asyncio/Include/LdH/asyncio.hpp
Normal file
154
modules/asyncio/Include/LdH/asyncio.hpp
Normal file
@ -0,0 +1,154 @@
|
||||
#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);
|
||||
};
|
||||
}
|
||||
172
modules/asyncio/src/eventloop.cpp
Normal file
172
modules/asyncio/src/eventloop.cpp
Normal file
@ -0,0 +1,172 @@
|
||||
|
||||
|
||||
thread_local LdH::Asyncio::WindowsOverlappedEventloop::_running_loop_variable_t LdH::Asyncio::WindowsOverlappedEventloop::_running_loop{};
|
||||
|
||||
LdH::Asyncio::WindowsOverlappedEventloop::WindowsOverlappedEventloop() :
|
||||
notifier{this->createEvent()},
|
||||
need_rollback_to_thread{true},
|
||||
master_fiber{this->prepareFibers()},
|
||||
interrupted{false},
|
||||
next_existing{nullptr},
|
||||
next_active{nullptr},
|
||||
fls_fiber_metadata{this->allocFls()} {
|
||||
}
|
||||
|
||||
HANDLE LdH::Asyncio::WindowsOverlappedEventloop::createEvent() const {
|
||||
HANDLE e = CreateEventA(nullptr, false, false, nullptr);
|
||||
if (e == nullptr)
|
||||
LdH::Exception::throwFromLastWindowsErr();
|
||||
return e;
|
||||
}
|
||||
|
||||
void *LdH::Asyncio::WindowsOverlappedEventloop::prepareFibers() const {
|
||||
void *f = ConvertThreadToFiberEx(nullptr, FIBER_FLAG_FLOAT_SWITCH);
|
||||
if (f == nullptr) {
|
||||
auto err = GetLastError();
|
||||
if (err != ERROR_ALREADY_FIBER)
|
||||
LdH::Exception::throwFromWindowsErrCode(err);
|
||||
*const_cast<bool *>(&this->need_rollback_to_thread) = false;
|
||||
return GetCurrentFiber();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
|
||||
|
||||
DWORD LdH::Asyncio::WindowsOverlappedEventloop::allocFls() const {
|
||||
auto index = FlsAlloc(nullptr);
|
||||
if (index == FLS_OUT_OF_INDEXES)
|
||||
LdH::Exception::throwFromLastWindowsErr();
|
||||
return index;
|
||||
}
|
||||
|
||||
LdH::Asyncio::WindowsOverlappedEventloop::~WindowsOverlappedEventloop() {
|
||||
if (this->need_rollback_to_thread) {
|
||||
if (0 == ConvertFiberToThread()) LdH::Exception::throwFromLastWindowsErr();
|
||||
}
|
||||
if (0 == CloseHandle(this->notifier)) LdH::Exception::throwFromLastWindowsErr();
|
||||
|
||||
}
|
||||
|
||||
|
||||
void LdH::Asyncio::WindowsOverlappedEventloop::_addTask(void (*routine)(void *), void *arg) {
|
||||
LdH::Asyncio::WindowsOverlappedEventloop::FiberInitializer init{
|
||||
.loop = this,
|
||||
.routine = routine,
|
||||
.routine_arg = arg,
|
||||
.caller_fiber = GetCurrentFiber()
|
||||
};
|
||||
auto new_fiber = CreateFiber(
|
||||
0,
|
||||
reinterpret_cast<LPFIBER_START_ROUTINE >(&LdH::Asyncio::WindowsOverlappedEventloop::fiber_kernel),
|
||||
&init
|
||||
);
|
||||
if (new_fiber == nullptr) LdH::Exception::throwFromLastWindowsErr();
|
||||
SwitchToFiber(new_fiber);
|
||||
}
|
||||
|
||||
void LdH::Asyncio::WindowsOverlappedEventloop::fiber_kernel(LdH::Asyncio::WindowsOverlappedEventloop::FiberInitializer *p_init) {
|
||||
auto init = *p_init;
|
||||
WindowsOverlappedEventloop::FiberMetadata metadata;
|
||||
|
||||
metadata.fiber_id = GetCurrentFiber();
|
||||
|
||||
metadata.next_existing = init.loop->next_existing;
|
||||
if (init.loop->next_existing != nullptr)
|
||||
init.loop->next_existing->prev_existing = &metadata;
|
||||
init.loop->next_existing = &metadata;
|
||||
metadata.prev_existing = nullptr;
|
||||
|
||||
metadata.next_active = init.loop->next_active;
|
||||
if (init.loop->next_active != nullptr)
|
||||
init.loop->next_active->prev_active = &metadata;
|
||||
init.loop->next_active = &metadata;
|
||||
metadata.prev_active = nullptr;
|
||||
|
||||
FlsSetValue(init.loop->fls_fiber_metadata, &metadata);
|
||||
SwitchToFiber(init.caller_fiber);
|
||||
|
||||
try {
|
||||
init.routine(init.routine_arg);
|
||||
} catch (LdH::Exception &e) {
|
||||
e.printStackTrace();
|
||||
} catch (std::exception &e) {
|
||||
std::cerr << e.what() << "\n";
|
||||
}
|
||||
if (init.loop->next_active == &metadata)
|
||||
init.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;
|
||||
|
||||
if (init.loop->next_existing == &metadata)
|
||||
init.loop->next_existing = metadata.next_existing;
|
||||
if (metadata.next_existing != nullptr)
|
||||
metadata.next_existing->prev_existing = metadata.prev_existing;
|
||||
if (metadata.prev_existing != nullptr)
|
||||
metadata.prev_existing->next_existing = metadata.next_existing;
|
||||
metadata.next_existing = nullptr;
|
||||
metadata.prev_existing = nullptr;
|
||||
|
||||
init.loop->fiber_to_destroy = metadata.fiber_id;
|
||||
SwitchToFiber(init.loop->master_fiber);
|
||||
}
|
||||
|
||||
void LdH::Asyncio::WindowsOverlappedEventloop::_overlapped_completion_routine(
|
||||
DWORD dwErrorCode,
|
||||
DWORD dwNumberOfBytesTransfered,
|
||||
LdH::Asyncio::WindowsOverlappedEventloop::FiberMetadata *lpOverlapped
|
||||
) {
|
||||
lpOverlapped->err_code = dwErrorCode;
|
||||
lpOverlapped->read_count = dwNumberOfBytesTransfered;
|
||||
|
||||
auto loop = WindowsOverlappedEventloop::_running_loop.get();
|
||||
lpOverlapped->next_active = loop->next_active;
|
||||
if (loop->next_active != nullptr)
|
||||
loop->next_active->prev_active = lpOverlapped;
|
||||
loop->next_active = lpOverlapped;
|
||||
|
||||
if (0 == SetEvent(loop->notifier))
|
||||
LdH::Exception::throwFromLastWindowsErr();
|
||||
}
|
||||
|
||||
void LdH::Asyncio::WindowsOverlappedEventloop::runUntilHasTasksAndNotInterrupted() {
|
||||
WindowsOverlappedEventloop::_running_loop.set(this);
|
||||
while (!this->interrupted) {
|
||||
while (this->next_active != nullptr) {
|
||||
SwitchToFiber(this->next_active->fiber_id);
|
||||
if (this->fiber_to_destroy != nullptr) {
|
||||
DeleteFiber(this->fiber_to_destroy);
|
||||
this->fiber_to_destroy = nullptr;
|
||||
}
|
||||
if (this->interrupted)
|
||||
goto CLEANUP;
|
||||
}
|
||||
|
||||
if (this->next_existing == nullptr)
|
||||
goto CLEANUP;
|
||||
|
||||
switch (WaitForSingleObjectEx(this->notifier, INFINITE, true)) {
|
||||
case WAIT_FAILED:
|
||||
WindowsOverlappedEventloop::_running_loop.set(nullptr);
|
||||
LdH::Exception::throwFromLastWindowsErr();
|
||||
return;
|
||||
|
||||
case WAIT_ABANDONED:
|
||||
WindowsOverlappedEventloop::_running_loop.set(nullptr);
|
||||
throw LdH::Exception{"Unexpected WAIT_ABANDONED"};
|
||||
case WAIT_IO_COMPLETION:
|
||||
WindowsOverlappedEventloop::_running_loop.set(nullptr);
|
||||
throw LdH::Exception{"Unexpected WAIT_IO_COMPLETION"};
|
||||
|
||||
case WAIT_OBJECT_0:
|
||||
case WAIT_TIMEOUT:
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
CLEANUP:
|
||||
WindowsOverlappedEventloop::_running_loop.set(nullptr);
|
||||
}
|
||||
10
modules/exceptions/CMakeLists.txt
Normal file
10
modules/exceptions/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
||||
add_library(exceptions STATIC)
|
||||
|
||||
target_sources(
|
||||
exceptions
|
||||
|
||||
PUBLIC
|
||||
FILE_SET cxx_modules TYPE CXX_MODULES FILES
|
||||
./src/exception.cppm
|
||||
./src/windows.cppm
|
||||
)
|
||||
57
modules/exceptions/src/exception.cppm
Normal file
57
modules/exceptions/src/exception.cppm
Normal file
@ -0,0 +1,57 @@
|
||||
export module ru.landgrafhomyak.BGTU.networks_1.exceptions;
|
||||
import std;
|
||||
|
||||
namespace LdH {
|
||||
export class Exception : public std::exception {
|
||||
public:
|
||||
static thread_local std::vector<std::stacktrace_entry> _async_caller_stacktrace;
|
||||
|
||||
const std::string message;
|
||||
const std::vector<std::stacktrace_entry> stacktrace;
|
||||
|
||||
explicit inline Exception(char const *message) : message{message}, stacktrace{Exception::generateStacktrace(-1)} {}
|
||||
|
||||
explicit inline Exception(std::string const &message) : message{message}, stacktrace{Exception::generateStacktrace(-1)} {}
|
||||
|
||||
explicit inline Exception(std::string const &&message) : message{message}, stacktrace{Exception::generateStacktrace(-1)} {}
|
||||
|
||||
[[nodiscard]]
|
||||
static std::vector<std::stacktrace_entry> generateStacktrace(std::int_fast16_t hidden_frames_count);
|
||||
|
||||
[[nodiscard]]
|
||||
const char *what() const override {
|
||||
return this->message.c_str();
|
||||
};
|
||||
|
||||
std::string formatOutput() const;
|
||||
|
||||
void printStackTrace() const {
|
||||
std::cerr << this->formatOutput();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module : private;
|
||||
|
||||
thread_local std::vector<std::stacktrace_entry> LdH::Exception::_async_caller_stacktrace{};
|
||||
|
||||
std::vector<std::stacktrace_entry> LdH::Exception::generateStacktrace(std::int_fast16_t hidden_frames_count) {
|
||||
if (hidden_frames_count > 0)
|
||||
throw Exception("Frames count must be negative");
|
||||
std::vector<std::stacktrace_entry> joined{Exception::_async_caller_stacktrace};
|
||||
auto current = std::stacktrace::current();
|
||||
joined.insert(joined.begin(), current.begin() + 1 - hidden_frames_count, current.end());
|
||||
return joined;
|
||||
}
|
||||
|
||||
std::string LdH::Exception::formatOutput() const {
|
||||
std::stringstream builder;
|
||||
builder << "Exception: " << this->message << '\n';
|
||||
for (auto const &frame: this->stacktrace) {
|
||||
builder << "\t" << frame.description();
|
||||
if (!frame.source_file().empty()) {
|
||||
builder << "\t\t" << frame.source_file() << ":" << frame.source_line() << '\n';
|
||||
}
|
||||
}
|
||||
return builder.str();
|
||||
}
|
||||
51
modules/exceptions/src/windows.cppm
Normal file
51
modules/exceptions/src/windows.cppm
Normal file
@ -0,0 +1,51 @@
|
||||
module;
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
export module ru.landgrafhomyak.BGTU.networks_1.exceptions.windows;
|
||||
|
||||
import std;
|
||||
import ru.landgrafhomyak.BGTU.networks_1.exceptions;
|
||||
|
||||
namespace LdH {
|
||||
export
|
||||
[[noreturn]]
|
||||
void throwFromWindowsErrCode(DWORD code);
|
||||
|
||||
export
|
||||
[[noreturn]]
|
||||
void throwFromLastWindowsErr();
|
||||
|
||||
export void throwFromLastWindowsErrOrTimeout();
|
||||
}
|
||||
|
||||
module : private;
|
||||
|
||||
[[noreturn]]
|
||||
void LdH::throwFromWindowsErrCode(DWORD code) {
|
||||
char *msg;
|
||||
FormatMessageA(
|
||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr,
|
||||
code,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<char *>(&msg),
|
||||
0,
|
||||
nullptr
|
||||
);
|
||||
std::string msgpp{msg};
|
||||
LocalFree(msg);
|
||||
throw LdH::Exception{std::move(msgpp)};
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
void LdH::throwFromLastWindowsErr() {
|
||||
LdH::throwFromWindowsErrCode(GetLastError());
|
||||
}
|
||||
|
||||
void LdH::throwFromLastWindowsErrOrTimeout() {
|
||||
DWORD err = GetLastError();
|
||||
if (err == ERROR_TIMEOUT)
|
||||
return;
|
||||
LdH::throwFromWindowsErrCode(err);
|
||||
}
|
||||
11
modules/threads/CMakeLists.txt
Normal file
11
modules/threads/CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
add_library(threads STATIC)
|
||||
|
||||
target_sources(
|
||||
threads
|
||||
|
||||
PUBLIC
|
||||
FILE_SET cxx_modules TYPE CXX_MODULES FILES
|
||||
src/windows.cppm
|
||||
)
|
||||
|
||||
target_link_libraries(threads PRIVATE exceptions Synchronization)
|
||||
190
modules/threads/src/windows.cppm
Normal file
190
modules/threads/src/windows.cppm
Normal file
@ -0,0 +1,190 @@
|
||||
module;
|
||||
|
||||
#include <Windows.h>
|
||||
|
||||
export module ru.landgrafhomyak.BGTU.networks_1.threads;
|
||||
import std;
|
||||
|
||||
|
||||
import ru.landgrafhomyak.BGTU.networks_1.exceptions;
|
||||
import ru.landgrafhomyak.BGTU.networks_1.exceptions.windows;
|
||||
|
||||
|
||||
namespace LdH {
|
||||
enum class _thread_state {
|
||||
_thread_state_UNINITIALIZED,
|
||||
_thread_state_RUNNING,
|
||||
_thread_state_JOINED,
|
||||
_thread_state_DESTROYED,
|
||||
_thread_state_MOVED
|
||||
};
|
||||
|
||||
using _thread_state::_thread_state_UNINITIALIZED;
|
||||
using _thread_state::_thread_state_RUNNING;
|
||||
using _thread_state::_thread_state_JOINED;
|
||||
using _thread_state::_thread_state_DESTROYED;
|
||||
using _thread_state::_thread_state_MOVED;
|
||||
|
||||
|
||||
export
|
||||
template<class lambda_t>
|
||||
concept ThreadRoutine = std::invocable<lambda_t> && std::same_as<std::invoke_result_t<lambda_t>, void> && std::movable<lambda_t>;
|
||||
|
||||
export class ThreadController;
|
||||
|
||||
export ThreadController fork(std::string name, ThreadRoutine auto);
|
||||
|
||||
export class ThreadController {
|
||||
private:
|
||||
_thread_state state;
|
||||
HANDLE hThread;
|
||||
|
||||
public:
|
||||
ThreadController();
|
||||
|
||||
ThreadController(ThreadController &&other);
|
||||
|
||||
void join();
|
||||
|
||||
void destroy();
|
||||
|
||||
~ThreadController() noexcept(false);
|
||||
|
||||
private:
|
||||
ThreadController(_thread_state state, HANDLE hThread) : state{state}, hThread{hThread} {
|
||||
}
|
||||
|
||||
template<ThreadRoutine thread_routine_t>
|
||||
struct _initializer {
|
||||
public:
|
||||
void *volatile notifier;
|
||||
std::string name;
|
||||
thread_routine_t thread_routine;
|
||||
};
|
||||
|
||||
template<ThreadRoutine thread_routine_t>
|
||||
static DWORD _kernel(_initializer<thread_routine_t> *init) {
|
||||
std::string thread_name = std::move(init->name);
|
||||
thread_routine_t thread_routine = std::move(init->thread_routine);
|
||||
init->notifier = init;
|
||||
WakeByAddressSingle(const_cast<void **>(&init->notifier));
|
||||
try {
|
||||
thread_routine();
|
||||
} catch (LdH::Exception const &e) {
|
||||
std::cerr << "Uncaught exception in thread" << thread_name << ":\n";
|
||||
e.printStackTrace();
|
||||
} catch (std::exception const &e) {
|
||||
std::cerr << "Uncaught exception in thread" << thread_name << ":\n" << e.what() << "\n";
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
friend
|
||||
ThreadController fork(std::string name, ThreadRoutine auto);
|
||||
};
|
||||
}
|
||||
|
||||
export LdH::ThreadController LdH::fork(std::string name, LdH::ThreadRoutine auto routine) {
|
||||
using thread_routine_t = decltype(routine);
|
||||
|
||||
void *notifier_expected = nullptr;
|
||||
|
||||
LdH::ThreadController::_initializer<thread_routine_t> initializer{
|
||||
.notifier = nullptr,
|
||||
.name = std::move(name),
|
||||
.thread_routine = std::move(routine)
|
||||
};
|
||||
HANDLE hThread = CreateThread(
|
||||
nullptr,
|
||||
0,
|
||||
(LPTHREAD_START_ROUTINE) &LdH::ThreadController::_kernel<thread_routine_t>,
|
||||
&initializer,
|
||||
0u,
|
||||
nullptr
|
||||
);
|
||||
if (hThread == nullptr)
|
||||
LdH::throwFromLastWindowsErr();
|
||||
|
||||
while (true) {
|
||||
if (TRUE == WaitOnAddress(&(initializer.notifier), ¬ifier_expected, sizeof(void *), INFINITE))
|
||||
break;
|
||||
|
||||
LdH::throwFromLastWindowsErrOrTimeout();
|
||||
}
|
||||
|
||||
return ThreadController{_thread_state_RUNNING, hThread};
|
||||
}
|
||||
|
||||
module : private;
|
||||
|
||||
LdH::ThreadController::ThreadController() : state{_thread_state_UNINITIALIZED}, hThread{nullptr} {
|
||||
}
|
||||
|
||||
LdH::ThreadController::ThreadController(LdH::ThreadController &&other) : state{other.state}, hThread{other.hThread} {
|
||||
other.hThread = nullptr;
|
||||
other.state = _thread_state_DESTROYED;
|
||||
}
|
||||
|
||||
|
||||
void LdH::ThreadController::join() {
|
||||
switch (this->state) {
|
||||
case _thread_state_UNINITIALIZED:
|
||||
throw Exception{"Variable not initialized"};
|
||||
case _thread_state_RUNNING:
|
||||
break;
|
||||
case _thread_state_JOINED:
|
||||
throw Exception{"Thread already joined"};
|
||||
case _thread_state_DESTROYED:
|
||||
throw Exception{"Thread already destroyed"};
|
||||
case _thread_state_MOVED:
|
||||
throw Exception{"Content of this variable was moved to another variable"};
|
||||
}
|
||||
|
||||
while (true) {
|
||||
switch (WaitForSingleObject(this->hThread, INFINITE)) {
|
||||
case WAIT_FAILED:
|
||||
LdH::throwFromLastWindowsErr();
|
||||
case WAIT_TIMEOUT:
|
||||
continue;
|
||||
case WAIT_ABANDONED:
|
||||
throw LdH::Exception{"Unexpected return WAIT_ABANDONED of WaitForSingleObject"};
|
||||
case WAIT_OBJECT_0:
|
||||
this->state = _thread_state_JOINED;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LdH::ThreadController::destroy() {
|
||||
switch (this->state) {
|
||||
case _thread_state_UNINITIALIZED:
|
||||
throw Exception{"Variable not initialized"};
|
||||
case _thread_state_RUNNING:
|
||||
throw Exception{"Thread not finished yet"};
|
||||
case _thread_state_JOINED:
|
||||
break;
|
||||
case _thread_state_DESTROYED:
|
||||
throw Exception{"Thread already destroyed"};
|
||||
case _thread_state_MOVED:
|
||||
throw Exception{"Content of this variable was moved to another variable"};
|
||||
}
|
||||
if (0 == CloseHandle(this->hThread))
|
||||
LdH::throwFromLastWindowsErr();
|
||||
this->state = _thread_state_DESTROYED;
|
||||
this->hThread = nullptr;
|
||||
}
|
||||
|
||||
LdH::ThreadController::~ThreadController() noexcept(false) {
|
||||
switch (this->state) {
|
||||
case _thread_state_UNINITIALIZED:
|
||||
break;
|
||||
case _thread_state_RUNNING:
|
||||
throw Exception{"Thread not finished yet"};
|
||||
case _thread_state_JOINED:
|
||||
throw Exception{"Thread not destroyed yet"};
|
||||
case _thread_state_DESTROYED:
|
||||
break;
|
||||
case _thread_state_MOVED:
|
||||
break;
|
||||
}
|
||||
}
|
||||
5
programs/lab4/CMakeLists.txt
Normal file
5
programs/lab4/CMakeLists.txt
Normal file
@ -0,0 +1,5 @@
|
||||
add_executable(lab4_ping ./src/ping.cpp)
|
||||
add_executable(lab4_pong ./src/pong.cpp)
|
||||
|
||||
target_link_libraries(lab4_ping PRIVATE wsock32 ws2_32 exceptions asyncio)
|
||||
target_link_libraries(lab4_pong PRIVATE wsock32 ws2_32 exceptions asyncio)
|
||||
80
programs/lab4/src/ping.cpp
Normal file
80
programs/lab4/src/ping.cpp
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
#include <Winsock2.h>
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
#include <LdH/exception.hpp>
|
||||
#include <LdH/asyncio.hpp>
|
||||
|
||||
|
||||
static void ping() {
|
||||
SOCKET sock = WSASocketW(
|
||||
AF_INET,
|
||||
SOCK_DGRAM,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
/*WSA_FLAG_OVERLAPPED | */WSA_FLAG_NO_HANDLE_INHERIT
|
||||
);
|
||||
if (sock == INVALID_SOCKET)
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
|
||||
|
||||
sockaddr selfaddr;
|
||||
INT selfaddrlen = sizeof(sockaddr);
|
||||
|
||||
if (SOCKET_ERROR == WSAStringToAddressW((wchar_t *) L"127.0.0.1:8082", AF_INET, nullptr, &selfaddr, &selfaddrlen))
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
|
||||
|
||||
while (true) {
|
||||
char buffer[1024] = "hello world";
|
||||
WSABUF buffers_meta[1] = {{1024, buffer}};
|
||||
sockaddr addr;
|
||||
INT addrlen = sizeof(sockaddr);
|
||||
DWORD flags = 0;/* MSG_PARTIAL*/
|
||||
|
||||
DWORD transferred_count;
|
||||
|
||||
auto res = WSASendTo(
|
||||
sock,
|
||||
buffers_meta, 1,
|
||||
&transferred_count,
|
||||
/* MSG_PARTIAL*/ 0,
|
||||
&selfaddr, selfaddrlen,
|
||||
nullptr, nullptr
|
||||
);
|
||||
if (res == SOCKET_ERROR) {
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
}
|
||||
|
||||
res = WSARecvFrom(
|
||||
sock,
|
||||
buffers_meta, 1,
|
||||
&transferred_count,
|
||||
&flags,
|
||||
&addr, &addrlen,
|
||||
nullptr, nullptr
|
||||
);
|
||||
if (res == SOCKET_ERROR) {
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
}
|
||||
|
||||
std::cout << buffer << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
WSADATA wsaData;
|
||||
auto wsa_init_res = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (wsa_init_res != ERROR_SUCCESS)
|
||||
LdH::Exception::throwFromWindowsErrCode(wsa_init_res);
|
||||
|
||||
ping();
|
||||
|
||||
|
||||
WSACleanup();
|
||||
} catch (LdH::Exception &e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
83
programs/lab4/src/pong.cpp
Normal file
83
programs/lab4/src/pong.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
#include <Winsock2.h>
|
||||
#include <Windows.h>
|
||||
#include <iostream>
|
||||
#include <LdH/exception.hpp>
|
||||
#include <LdH/asyncio.hpp>
|
||||
|
||||
|
||||
static void connections_listener() {
|
||||
SOCKET sock = WSASocketW(
|
||||
AF_INET,
|
||||
SOCK_DGRAM,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
/*WSA_FLAG_OVERLAPPED | */WSA_FLAG_NO_HANDLE_INHERIT
|
||||
);
|
||||
if (sock == INVALID_SOCKET)
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
|
||||
|
||||
sockaddr selfaddr;
|
||||
INT selfaddrlen = sizeof(sockaddr);
|
||||
|
||||
if (SOCKET_ERROR == WSAStringToAddressW((wchar_t *) L"127.0.0.1:8082", AF_INET, nullptr, &selfaddr, &selfaddrlen))
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
|
||||
if (SOCKET_ERROR == bind(sock, &selfaddr, selfaddrlen))
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
|
||||
|
||||
while (true) {
|
||||
char buffer[1024];
|
||||
WSABUF buffers_meta[1] = {{1024, buffer}};
|
||||
sockaddr addr;
|
||||
INT addrlen = sizeof(sockaddr);
|
||||
DWORD flags = 0;/* MSG_PARTIAL*/
|
||||
|
||||
DWORD transferred_count;
|
||||
auto res = WSARecvFrom(
|
||||
sock,
|
||||
buffers_meta, 1,
|
||||
&transferred_count,
|
||||
&flags,
|
||||
&addr, &addrlen,
|
||||
nullptr, nullptr
|
||||
);
|
||||
if (res == SOCKET_ERROR) {
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
}
|
||||
|
||||
buffers_meta[0].len = transferred_count;
|
||||
std::cout << buffer << std::endl;
|
||||
|
||||
res = WSASendTo(
|
||||
sock,
|
||||
buffers_meta, 1,
|
||||
&transferred_count,
|
||||
/* MSG_PARTIAL*/ 0,
|
||||
&addr, sizeof(sockaddr),
|
||||
nullptr, nullptr
|
||||
);
|
||||
if (res == SOCKET_ERROR) {
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
WSADATA wsaData;
|
||||
auto wsa_init_res = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (wsa_init_res != ERROR_SUCCESS)
|
||||
LdH::Exception::throwFromWindowsErrCode(wsa_init_res);
|
||||
|
||||
connections_listener();
|
||||
|
||||
|
||||
WSACleanup();
|
||||
} catch (LdH::Exception &e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
94
programs/lab4/src/pong_async.cpp
Normal file
94
programs/lab4/src/pong_async.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
|
||||
#include <Winsock2.h>
|
||||
#include <Windows.h>
|
||||
#include <LdH/exception.hpp>
|
||||
#include <LdH/asyncio.hpp>
|
||||
|
||||
struct request_t {
|
||||
char buffer[1024];
|
||||
WSABUF buffers_meta[1] = {{1024, buffer}};
|
||||
sockaddr addr;
|
||||
SOCKET socket;
|
||||
};
|
||||
|
||||
static void connections_listener(void *) {
|
||||
SOCKET server = WSASocketW(
|
||||
AF_INET,
|
||||
SOCK_DGRAM,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT
|
||||
);
|
||||
if (server == INVALID_SOCKET)
|
||||
LdH::Exception::throwFromWindowsErrCode(WSAGetLastError());
|
||||
|
||||
while (true) {
|
||||
auto req = new request_t{};
|
||||
req->socket = server;
|
||||
|
||||
LdH::Asyncio::WindowsOverlappedEventloop::overlappedCall(
|
||||
[&](LPOVERLAPPED o, LPOVERLAPPED_COMPLETION_ROUTINE ocr) -> DWORD {
|
||||
DWORD recived_count;
|
||||
auto res = WSARecvFrom(
|
||||
server,
|
||||
req->buffers_meta, 1,
|
||||
&recived_count,
|
||||
/* MSG_PARTIAL*/ nullptr,
|
||||
&req->addr, nullptr,
|
||||
o, reinterpret_cast<LPWSAOVERLAPPED_COMPLETION_ROUTINE>(ocr)
|
||||
);
|
||||
if (res == SOCKET_ERROR) {
|
||||
auto err = WSAGetLastError();
|
||||
if (err == WSA_IO_PENDING)
|
||||
return 0;
|
||||
LdH::Exception::throwFromWindowsErrCode(err);
|
||||
}
|
||||
return recived_count;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void answer(request_t *data) {
|
||||
LdH::Asyncio::WindowsOverlappedEventloop::overlappedCall(
|
||||
[&](LPOVERLAPPED o, LPOVERLAPPED_COMPLETION_ROUTINE ocr) -> DWORD {
|
||||
DWORD recived_count;
|
||||
auto res = WSASendTo(
|
||||
data->socket,
|
||||
data->buffers_meta, 1,
|
||||
&recived_count,
|
||||
/* MSG_PARTIAL*/ 0,
|
||||
&data->addr, sizeof(sockaddr),
|
||||
o, reinterpret_cast<LPWSAOVERLAPPED_COMPLETION_ROUTINE>(ocr)
|
||||
);
|
||||
if (res == SOCKET_ERROR) {
|
||||
auto err = WSAGetLastError();
|
||||
if (err == WSA_IO_PENDING)
|
||||
return 0;
|
||||
LdH::Exception::throwFromWindowsErrCode(err);
|
||||
}
|
||||
return recived_count;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
int main() {
|
||||
try {
|
||||
WSADATA wsaData;
|
||||
auto wsa_init_res = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (wsa_init_res != ERROR_SUCCESS)
|
||||
LdH::Exception::throwFromWindowsErrCode(wsa_init_res);
|
||||
|
||||
LdH::Asyncio::WindowsOverlappedEventloop eventloop{};
|
||||
eventloop.addTask<void>(&connections_listener, nullptr);
|
||||
eventloop.runUntilHasTasksAndNotInterrupted();
|
||||
|
||||
|
||||
WSACleanup();
|
||||
} catch (LdH::Exception &e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user