diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ecf5e2..9280323 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ 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_STANDARD_REQUIRED ON) set(CMAKE_CXX_MODULE_STD 1) add_subdirectory(modules/exceptions) diff --git a/modules/threads/CMakeLists.txt b/modules/threads/CMakeLists.txt index 6510115..d6fff15 100644 --- a/modules/threads/CMakeLists.txt +++ b/modules/threads/CMakeLists.txt @@ -5,6 +5,8 @@ target_sources( PUBLIC FILE_SET cxx_modules TYPE CXX_MODULES FILES + src/common.cppm + src/thread_routine.cppm src/windows.cppm ) diff --git a/modules/threads/src/common.cppm b/modules/threads/src/common.cppm new file mode 100644 index 0000000..fa7e9ff --- /dev/null +++ b/modules/threads/src/common.cppm @@ -0,0 +1,48 @@ +export module ru.landgrafhomyak.BGTU.networks_1.threads; +import std; +export import :thread_routine; +import :impl; + + +namespace LdH { + export class ThreadController { + private: + LdH::_NativeThreads::ThreadController _value; + + public: + ThreadController() = default; + + ThreadController(LdH::ThreadController &) = delete; + + ThreadController(LdH::ThreadController const &) = delete; + + ThreadController(LdH::ThreadController &&other) noexcept : _value{std::move(other._value)} { + } + + void operator=(LdH::ThreadController &&other) noexcept { + this->_value = std::move(other._value); + } + + void join() { + this->_value.join(); + } + + void destroy() { + this->_value.destroy(); + } + + private: + ThreadController(LdH::_NativeThreads::ThreadController &&value) : _value{std::move(value)} { + }; + + friend + LdH::ThreadController fork(std::string &&name, LdH::ThreadRoutine auto &&routine); + }; + + + export LdH::ThreadController fork(std::string &&name, ThreadRoutine auto &&routine) { + return LdH::ThreadController{LdH::_NativeThreads::fork(std::move(name), std::move(routine))}; + } +} + +module:private; diff --git a/modules/threads/src/thread_routine.cppm b/modules/threads/src/thread_routine.cppm new file mode 100644 index 0000000..ba682c4 --- /dev/null +++ b/modules/threads/src/thread_routine.cppm @@ -0,0 +1,8 @@ +export module ru.landgrafhomyak.BGTU.networks_1.threads:thread_routine; +import std; + +namespace LdH { + export + template + concept ThreadRoutine = std::invocable && std::same_as, void> && std::movable && std::destructible; +} diff --git a/modules/threads/src/windows.cppm b/modules/threads/src/windows.cppm index 5022e57..43e4c7c 100644 --- a/modules/threads/src/windows.cppm +++ b/modules/threads/src/windows.cppm @@ -1,19 +1,22 @@ module; +#ifdef _WIN32 + #include -export module ru.landgrafhomyak.BGTU.networks_1.threads; +export module ru.landgrafhomyak.BGTU.networks_1.threads:impl; import std; import ru.landgrafhomyak.BGTU.networks_1.exceptions; import ru.landgrafhomyak.BGTU.networks_1.exceptions.windows; +import :thread_routine; - -namespace LdH { +namespace LdH::_NativeThreads { enum class _thread_state { _thread_state_UNINITIALIZED, _thread_state_RUNNING, + _thread_state_JOINING, _thread_state_JOINED, _thread_state_DESTROYED, _thread_state_MOVED @@ -21,28 +24,30 @@ namespace LdH { using _thread_state::_thread_state_UNINITIALIZED; using _thread_state::_thread_state_RUNNING; + using _thread_state::_thread_state_JOINING; 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 ThreadController fork(std::string &&name, ThreadRoutine auto &&); export class ThreadController { private: - _thread_state state; + std::atomic<_thread_state> state; HANDLE hThread; public: ThreadController(); - ThreadController(ThreadController &&other); + ThreadController(ThreadController &) = delete; + + ThreadController(ThreadController const &) = delete; + + ThreadController(ThreadController &&other) noexcept; + + void operator=(ThreadController &&other) noexcept; void join(); @@ -80,16 +85,21 @@ namespace LdH { }; friend - ThreadController fork(std::string name, ThreadRoutine auto); + ThreadController fork(std::string &&, ThreadRoutine auto &&); + + [[nodiscard]] _thread_state _cas_state(_thread_state expected, _thread_state next) { + this->state.compare_exchange_strong(expected, next); + return expected; + } }; } -export LdH::ThreadController LdH::fork(std::string name, LdH::ThreadRoutine auto routine) { - using thread_routine_t = decltype(routine); +export LdH::_NativeThreads::ThreadController LdH::_NativeThreads::fork(std::string &&name, LdH::ThreadRoutine auto &&routine) { + using thread_routine_t = std::remove_reference_t; void *notifier_expected = nullptr; - LdH::ThreadController::_initializer initializer{ + LdH::_NativeThreads::ThreadController::_initializer initializer{ .notifier = nullptr, .name = std::move(name), .thread_routine = std::move(routine) @@ -97,7 +107,7 @@ export LdH::ThreadController LdH::fork(std::string name, LdH::ThreadRoutine auto HANDLE hThread = CreateThread( nullptr, 0, - (LPTHREAD_START_ROUTINE) &LdH::ThreadController::_kernel, + (LPTHREAD_START_ROUTINE) &LdH::_NativeThreads::ThreadController::_kernel, &initializer, 0u, nullptr @@ -115,29 +125,42 @@ export LdH::ThreadController LdH::fork(std::string name, LdH::ThreadRoutine auto return ThreadController{_thread_state_RUNNING, hThread}; } -module : private; - -LdH::ThreadController::ThreadController() : state{_thread_state_UNINITIALIZED}, hThread{nullptr} { +LdH::_NativeThreads::ThreadController::ThreadController() : state{_thread_state_UNINITIALIZED}, hThread{nullptr} { } -LdH::ThreadController::ThreadController(LdH::ThreadController &&other) : state{other.state}, hThread{other.hThread} { +LdH::_NativeThreads::ThreadController::ThreadController(LdH::_NativeThreads::ThreadController &&other) noexcept : state{other.state.exchange(_thread_state_MOVED)}, hThread{other.hThread} { other.hThread = nullptr; - other.state = _thread_state_DESTROYED; } -void LdH::ThreadController::join() { - switch (this->state) { +void LdH::_NativeThreads::ThreadController::operator=(ThreadController &&other) noexcept { + switch (this->state.load()) { case _thread_state_UNINITIALIZED: - throw Exception{"Variable not initialized"}; + case _thread_state_MOVED: + case _thread_state_DESTROYED: + break; + case _thread_state_RUNNING: + case _thread_state_JOINING: + case _thread_state_JOINED: + throw LdH::Exception{"Variable already initialized"}; + } + new(this)ThreadController{std::move(other)}; +} + +void LdH::_NativeThreads::ThreadController::join() { + switch (this->_cas_state(_thread_state_RUNNING, _thread_state_JOINING)) { + case _thread_state_UNINITIALIZED: + throw LdH::Exception{"Variable not initialized"}; case _thread_state_RUNNING: break; + case _thread_state_JOINING: case _thread_state_JOINED: - throw Exception{"Thread already joined"}; + throw LdH::Exception{"Thread already joined"}; case _thread_state_DESTROYED: - throw Exception{"Thread already destroyed"}; + throw LdH::Exception{"Thread already destroyed"}; case _thread_state_MOVED: - throw Exception{"Content of this variable was moved to another variable"}; + throw LdH::Exception{"Content of this variable was moved to another variable"}; + break; } while (true) { @@ -149,42 +172,45 @@ void LdH::ThreadController::join() { case WAIT_ABANDONED: throw LdH::Exception{"Unexpected return WAIT_ABANDONED of WaitForSingleObject"}; case WAIT_OBJECT_0: - this->state = _thread_state_JOINED; + this->state.store(_thread_state_JOINED); return; } } } -void LdH::ThreadController::destroy() { - switch (this->state) { +void LdH::_NativeThreads::ThreadController::destroy() { + switch (this->_cas_state(_thread_state_JOINED, _thread_state_DESTROYED)) { case _thread_state_UNINITIALIZED: - throw Exception{"Variable not initialized"}; + throw LdH::Exception{"Variable not initialized"}; case _thread_state_RUNNING: - throw Exception{"Thread not finished yet"}; + case _thread_state_JOINING: + throw LdH::Exception{"Thread not finished yet"}; case _thread_state_JOINED: break; case _thread_state_DESTROYED: - throw Exception{"Thread already destroyed"}; + throw LdH::Exception{"Thread already destroyed"}; case _thread_state_MOVED: - throw Exception{"Content of this variable was moved to another variable"}; + throw LdH::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) { +LdH::_NativeThreads::ThreadController::~ThreadController() noexcept(false) { + switch (this->state.load()) { case _thread_state_UNINITIALIZED: break; case _thread_state_RUNNING: - throw Exception{"Thread not finished yet"}; + case _thread_state_JOINING: + throw LdH::Exception{"Thread not finished yet"}; case _thread_state_JOINED: - throw Exception{"Thread not destroyed yet"}; + throw LdH::Exception{"Thread not destroyed yet"}; case _thread_state_DESTROYED: break; case _thread_state_MOVED: break; } } + +#endif