commit 4aad26efa7a27598a1bf7c478ccc405368cc3982 Author: Andrew Golovashevich Date: Sat Sep 20 19:05:03 2025 +0300 Configured 'import std' in cmake and implemented and stub for threading module on windows diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..27fe36a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/*build*/ +/.idea/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2ecf5e2 --- /dev/null +++ b/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..b45cada --- /dev/null +++ b/main.cpp @@ -0,0 +1,18 @@ +#include + +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"; + } +} diff --git a/modules/asyncio/CMakeLists.txt b/modules/asyncio/CMakeLists.txt new file mode 100644 index 0000000..e217289 --- /dev/null +++ b/modules/asyncio/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/modules/asyncio/Include/LdH/asyncio.hpp b/modules/asyncio/Include/LdH/asyncio.hpp new file mode 100644 index 0000000..5ac6e0a --- /dev/null +++ b/modules/asyncio/Include/LdH/asyncio.hpp @@ -0,0 +1,154 @@ +#pragma once + +//#define WIN32_LEAN_AND_MEAN + +#include +#include +#include + +#include + + +namespace LdH::Asyncio { +#if 0 + template class lambda_t> + concept OverlappedFunc = std::invocable, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE> + && std::same_as, LPOVERLAPPED, LPOVERLAPPED_COMPLETION_ROUTINE>, ret_t>; +#endif + + template + concept OverlappedFunc = std::invocable + && std::same_as, 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 + static void addTaskToRuningLoop(void (*routine)(T *), T *arg) { + WindowsOverlappedEventloop::_running_loop.get()->_addTask( + reinterpret_cast(routine), + reinterpret_cast(arg) + ); + } + + template + void addTask(void (*routine)(T *), T *arg) { + this->_addTask( + reinterpret_cast(routine), + reinterpret_cast(arg) + ); + } + + static DWORD overlappedCall(OverlappedFunc auto func) { + auto loop = WindowsOverlappedEventloop::_running_loop.get(); + auto metadata = reinterpret_cast(FlsGetValue(loop->fls_fiber_metadata)); + if (metadata == nullptr) LdH::Exception::throwFromLastWindowsErr(); + + DWORD read_count = func(reinterpret_cast(metadata), reinterpret_cast(&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); + }; +} diff --git a/modules/asyncio/src/eventloop.cpp b/modules/asyncio/src/eventloop.cpp new file mode 100644 index 0000000..bef05b2 --- /dev/null +++ b/modules/asyncio/src/eventloop.cpp @@ -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(&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(&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); +} \ No newline at end of file diff --git a/modules/exceptions/CMakeLists.txt b/modules/exceptions/CMakeLists.txt new file mode 100644 index 0000000..8a4d5c5 --- /dev/null +++ b/modules/exceptions/CMakeLists.txt @@ -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 +) \ No newline at end of file diff --git a/modules/exceptions/src/exception.cppm b/modules/exceptions/src/exception.cppm new file mode 100644 index 0000000..d81c350 --- /dev/null +++ b/modules/exceptions/src/exception.cppm @@ -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 _async_caller_stacktrace; + + const std::string message; + const std::vector 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 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 LdH::Exception::_async_caller_stacktrace{}; + +std::vector LdH::Exception::generateStacktrace(std::int_fast16_t hidden_frames_count) { + if (hidden_frames_count > 0) + throw Exception("Frames count must be negative"); + std::vector 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(); +} diff --git a/modules/exceptions/src/windows.cppm b/modules/exceptions/src/windows.cppm new file mode 100644 index 0000000..b866414 --- /dev/null +++ b/modules/exceptions/src/windows.cppm @@ -0,0 +1,51 @@ +module; + +#include + +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(&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); +} diff --git a/modules/threads/CMakeLists.txt b/modules/threads/CMakeLists.txt new file mode 100644 index 0000000..6510115 --- /dev/null +++ b/modules/threads/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/modules/threads/src/windows.cppm b/modules/threads/src/windows.cppm new file mode 100644 index 0000000..5022e57 --- /dev/null +++ b/modules/threads/src/windows.cppm @@ -0,0 +1,190 @@ +module; + +#include + +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 + concept ThreadRoutine = std::invocable && std::same_as, void> && std::movable; + + 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 + struct _initializer { + public: + void *volatile notifier; + std::string name; + thread_routine_t thread_routine; + }; + + template + static DWORD _kernel(_initializer *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(&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 initializer{ + .notifier = nullptr, + .name = std::move(name), + .thread_routine = std::move(routine) + }; + HANDLE hThread = CreateThread( + nullptr, + 0, + (LPTHREAD_START_ROUTINE) &LdH::ThreadController::_kernel, + &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; + } +} diff --git a/programs/lab4/CMakeLists.txt b/programs/lab4/CMakeLists.txt new file mode 100644 index 0000000..37d583b --- /dev/null +++ b/programs/lab4/CMakeLists.txt @@ -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) \ No newline at end of file diff --git a/programs/lab4/src/ping.cpp b/programs/lab4/src/ping.cpp new file mode 100644 index 0000000..b5340fa --- /dev/null +++ b/programs/lab4/src/ping.cpp @@ -0,0 +1,80 @@ + +#include +#include +#include +#include +#include + + +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(); + } +} \ No newline at end of file diff --git a/programs/lab4/src/pong.cpp b/programs/lab4/src/pong.cpp new file mode 100644 index 0000000..94a8c26 --- /dev/null +++ b/programs/lab4/src/pong.cpp @@ -0,0 +1,83 @@ + +#include +#include +#include +#include +#include + + +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(); + } +} \ No newline at end of file diff --git a/programs/lab4/src/pong_async.cpp b/programs/lab4/src/pong_async.cpp new file mode 100644 index 0000000..8893004 --- /dev/null +++ b/programs/lab4/src/pong_async.cpp @@ -0,0 +1,94 @@ + +#include +#include +#include +#include + +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(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(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(&connections_listener, nullptr); + eventloop.runUntilHasTasksAndNotInterrupted(); + + + WSACleanup(); + } catch (LdH::Exception &e) { + e.printStackTrace(); + } +} \ No newline at end of file