diff --git a/cpp/.gitignore b/cpp/.gitignore new file mode 100644 index 0000000..4f4503e --- /dev/null +++ b/cpp/.gitignore @@ -0,0 +1,33 @@ +# Build directories +build/ +*/build/ + +# CMake +CMakeCache.txt +CMakeFiles +CMakeScripts +Testing +Makefile +cmake_install.cmake +install_manifest.txt +compile_commands.json +CTestTestfile.cmake +_deps + +# Object files +*.o +*.a +*.so +*.dll +*.dylib + +# IDE +.vscode/ +.idea/ +*.user +*.vcxproj +*.sln + +# OS +.DS_Store +Thumbs.db \ No newline at end of file diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 0000000..b8e3288 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,108 @@ +cmake_minimum_required(VERSION 3.15) + +project(Platform.Threading + VERSION 1.0.0 + DESCRIPTION "C++ translation of Platform.Threading library" + LANGUAGES CXX +) + +# Set C++20 standard (required for std::jthread) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# Compiler-specific options +if(MSVC) + add_compile_options(/W4 /permissive-) + # Enable std::jthread on MSVC + add_definitions(-D_WIN32_WINNT=0x0A00) +else() + add_compile_options(-Wall -Wextra -Wpedantic) +endif() + +# Platform.Threading library +set(PLATFORM_THREADING_HEADERS + Platform.Threading/ThreadHelpers.h + Platform.Threading/TaskExtensions.h + Platform.Threading/ConcurrentQueueExtensions.h + Platform.Threading/Synchronization/ISynchronization.h + Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h + Platform.Threading/Synchronization/Unsynchronization.h + Platform.Threading/Synchronization/ISynchronized.h + Platform.Threading/Synchronization/ISynchronizationExtensions.h +) + +set(PLATFORM_THREADING_SOURCES + Platform.Threading/ThreadHelpers.cpp +) + +add_library(Platform.Threading ${PLATFORM_THREADING_SOURCES} ${PLATFORM_THREADING_HEADERS}) + +target_include_directories(Platform.Threading + PUBLIC + $ + $ +) + +# Find required packages +find_package(Threads REQUIRED) + +# Link threading library +target_link_libraries(Platform.Threading PUBLIC Threads::Threads) + +# Set properties +set_target_properties(Platform.Threading PROPERTIES + CXX_VISIBILITY_PRESET hidden + VISIBILITY_INLINES_HIDDEN YES + VERSION ${PROJECT_VERSION} + SOVERSION 1 +) + +# Enable testing +enable_testing() + +# Tests +if(BUILD_TESTING) + add_subdirectory(Platform.Threading.Tests) +endif() + +# Install targets +install(TARGETS Platform.Threading + EXPORT Platform.ThreadingTargets + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib + RUNTIME DESTINATION bin + INCLUDES DESTINATION include +) + +# Install headers +install(DIRECTORY Platform.Threading/ + DESTINATION include/Platform.Threading + FILES_MATCHING PATTERN "*.h" +) + +# Create package config files +include(CMakePackageConfigHelpers) +write_basic_package_version_file( + "Platform.ThreadingConfigVersion.cmake" + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) + +install(EXPORT Platform.ThreadingTargets + FILE Platform.ThreadingTargets.cmake + NAMESPACE Platform:: + DESTINATION lib/cmake/Platform.Threading +) + +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Platform.ThreadingConfig.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/Platform.ThreadingConfig.cmake" + @ONLY +) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/Platform.ThreadingConfig.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/Platform.ThreadingConfigVersion.cmake" + DESTINATION lib/cmake/Platform.Threading +) \ No newline at end of file diff --git a/cpp/Platform.Threading.Tests/CMakeLists.txt b/cpp/Platform.Threading.Tests/CMakeLists.txt new file mode 100644 index 0000000..c1a5fe9 --- /dev/null +++ b/cpp/Platform.Threading.Tests/CMakeLists.txt @@ -0,0 +1,36 @@ +# Platform.Threading.Tests + +# Find or download Google Test +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/03597a01ee50f33f9142fd2db5e2bc3d47f18e95.zip +) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +# Test sources +set(TEST_SOURCES + ThreadHelpersTests.cpp +) + +# Create test executable +add_executable(Platform.Threading.Tests ${TEST_SOURCES}) + +# Link libraries +target_link_libraries(Platform.Threading.Tests + Platform.Threading + gtest_main + gmock_main +) + +# Include directories +target_include_directories(Platform.Threading.Tests PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/../ +) + +# Discover tests +include(GoogleTest) +gtest_discover_tests(Platform.Threading.Tests) \ No newline at end of file diff --git a/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp b/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp index 3f9115c..375e73d 100644 --- a/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp +++ b/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp @@ -1,18 +1,68 @@ -namespace Platform::Threading::Tests +#include +#include +#include + +using namespace Platform::Threading; + +class ThreadHelpersTests : public ::testing::Test { - TEST_CLASS(ThreadHelpersTests) - { - public: TEST_METHOD(InvokeTest) - { - auto number = 0; - ThreadHelpers.InvokeWithExtendedMaxStackSize([&]()-> auto { return number = 1; }); - Assert::AreEqual(1, number); - ThreadHelpers.InvokeWithExtendedMaxStackSize(2, param { return number = (std::int32_t)param); } - Assert::AreEqual(2, number); - ThreadHelpers.InvokeWithModifiedMaxStackSize([&]()-> auto { return number = 1; }, maxStackSize: 512); - Assert::AreEqual(1, number); - ThreadHelpers.InvokeWithModifiedMaxStackSize(2, param { return number = (std::int32_t)param, maxStackSize: 512); } - Assert::AreEqual(2, number); - } - }; +protected: + void SetUp() override {} + void TearDown() override {} +}; + +TEST_F(ThreadHelpersTests, InvokeTest) +{ + std::atomic number{0}; + + // Test InvokeWithExtendedMaxStackSize with lambda + ThreadHelpers::InvokeWithExtendedMaxStackSize([&number]() { + number = 1; + }); + EXPECT_EQ(1, number.load()); + + // Test InvokeWithExtendedMaxStackSize with parameter + ThreadHelpers::InvokeWithExtendedMaxStackSize(2, [&number](int param) { + number = param; + }); + EXPECT_EQ(2, number.load()); + + // Test InvokeWithModifiedMaxStackSize with lambda + ThreadHelpers::InvokeWithModifiedMaxStackSize([&number]() { + number = 3; + }, 512); + EXPECT_EQ(3, number.load()); + + // Test InvokeWithModifiedMaxStackSize with parameter + ThreadHelpers::InvokeWithModifiedMaxStackSize(4, [&number](int param) { + number = param; + }, 512); + EXPECT_EQ(4, number.load()); +} + +TEST_F(ThreadHelpersTests, StartNewTest) +{ + std::atomic number{0}; + + // Test StartNew with lambda + auto thread1 = ThreadHelpers::StartNew([&number]() { + number = 10; + }); + thread1.join(); + EXPECT_EQ(10, number.load()); + + // Test StartNew with parameter + auto thread2 = ThreadHelpers::StartNew(20, [&number](int param) { + number = param; + }); + thread2.join(); + EXPECT_EQ(20, number.load()); +} + +TEST_F(ThreadHelpersTests, SleepTest) +{ + // Test that Sleep doesn't crash + ThreadHelpers::Sleep(); + // Test passes if no exception is thrown + SUCCEED(); } diff --git a/cpp/Platform.Threading/ConcurrentQueueExtensions.h b/cpp/Platform.Threading/ConcurrentQueueExtensions.h index a7337a4..b20ee51 100644 --- a/cpp/Platform.Threading/ConcurrentQueueExtensions.h +++ b/cpp/Platform.Threading/ConcurrentQueueExtensions.h @@ -1,23 +1,171 @@ -namespace Platform::Threading +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace Platform::Threading { + /// + /// Thread-safe queue implementation for concurrent operations + /// + template + class ConcurrentQueue + { + private: + mutable std::mutex mutex_; + std::queue queue_; + std::condition_variable condition_; + + public: + /// + /// Adds an item to the end of the queue + /// + void Enqueue(T item) + { + std::lock_guard lock(mutex_); + queue_.push(item); + condition_.notify_one(); + } + + /// + /// Attempts to remove and return the object at the beginning of the queue + /// + bool TryDequeue(T& result) + { + std::lock_guard lock(mutex_); + if (queue_.empty()) + { + return false; + } + result = queue_.front(); + queue_.pop(); + return true; + } + + /// + /// Removes and returns all objects from the queue + /// + std::vector DequeueAll() + { + std::lock_guard lock(mutex_); + std::vector result; + while (!queue_.empty()) + { + result.push_back(queue_.front()); + queue_.pop(); + } + return result; + } + + /// + /// Gets whether the queue is empty + /// + bool Empty() const + { + std::lock_guard lock(mutex_); + return queue_.empty(); + } + + /// + /// Gets the number of elements in the queue + /// + size_t Size() const + { + std::lock_guard lock(mutex_); + return queue_.size(); + } + }; + + /// + /// Provides a set of extension methods for ConcurrentQueue objects with std::future. + /// Предоставляет набор методов расширения для объектов ConcurrentQueue с std::future. + /// class ConcurrentQueueExtensions { - public: static async Task AwaitAll(ConcurrentQueue queue) + public: + /// + /// Waits for completion of all asynchronous operations in the queue. + /// Ожидает завершения всех асинхронных операций в очереди. + /// + /// The queue of asynchronous operations.Очередь асинхронных операций. + template + static void AwaitAll(ConcurrentQueue>& queue) + { + auto futures = queue.DequeueAll(); + for (auto& future : futures) + { + future.wait(); + } + } + + /// + /// Waits for completion of all asynchronous operations in the queue (void specialization). + /// Ожидает завершения всех асинхронных операций в очереди (специализация для void). + /// + /// The queue of asynchronous operations.Очередь асинхронных операций. + static void AwaitAll(ConcurrentQueue>& queue) { - foreach (auto item in queue.DequeueAll()) + auto futures = queue.DequeueAll(); + for (auto& future : futures) { - await item.ConfigureAwait(continueOnCapturedContext: false); + future.wait(); } } - public: static async Task AwaitOne(ConcurrentQueue queue) + /// + /// Waits for completion of the first asynchronous operation in the queue. + /// Ожидает завершения первой асинхронной операции в очереди. + /// + /// The queue of asynchronous operations.Очередь асинхронных операций. + template + static void AwaitOne(ConcurrentQueue>& queue) { - if (queue.TryDequeue(out Task item)) + std::future future; + if (queue.TryDequeue(future)) { - await item.ConfigureAwait(continueOnCapturedContext: false); + future.wait(); } } - public: static void EnqueueAsRunnedTask(ConcurrentQueue queue, std::function action) { queue.Enqueue(Task.Run(action)); } + /// + /// Waits for completion of the first asynchronous operation in the queue (void specialization). + /// Ожидает завершения первой асинхронной операции в очереди (специализация для void). + /// + /// The queue of asynchronous operations.Очередь асинхронных операций. + static void AwaitOne(ConcurrentQueue>& queue) + { + std::future future; + if (queue.TryDequeue(future)) + { + future.wait(); + } + } + + /// + /// Adds a function as async task to the end of the queue. + /// Добавляет функцию как асинхронную задачу в конец очереди. + /// + /// The queue of asynchronous operations.Очередь асинхронных операций. + /// The function to run asynchronously.Функция для асинхронного выполнения. + static void RunAndPush(ConcurrentQueue>& queue, std::function action) + { + queue.Enqueue(std::async(std::launch::async, action)); + } + + /// + /// Adds a function as async task to the end of the queue. + /// Добавляет функцию как асинхронную задачу в конец очереди. + /// + /// The queue of asynchronous operations.Очередь асинхронных операций. + /// The function to run asynchronously.Функция для асинхронного выполнения. + template + static void RunAndPush(ConcurrentQueue>& queue, std::function function) + { + queue.Enqueue(std::async(std::launch::async, function)); + } }; } diff --git a/cpp/Platform.Threading/Synchronization/ISynchronization.h b/cpp/Platform.Threading/Synchronization/ISynchronization.h index 7a66bb2..6e90f6b 100644 --- a/cpp/Platform.Threading/Synchronization/ISynchronization.h +++ b/cpp/Platform.Threading/Synchronization/ISynchronization.h @@ -1,14 +1,64 @@ -namespace Platform::Threading::Synchronization +#pragma once + +#include + +namespace Platform::Threading::Synchronization { + /// + /// Represents a synchronization object that supports read and write operations. + /// Представляет объект синхронизации с поддержкой операций чтения и записи. + /// class ISynchronization { public: - virtual void ExecuteReadOperation(std::function action) = 0; + virtual ~ISynchronization() = default; + + /// + /// Executes action in read access mode. + /// Выполняет действие в режиме доступа для чтения. + /// + /// The action.Действие. + virtual void DoRead(std::function action) = 0; - TResult ExecuteReadOperation(std::function function); + /// + /// Executes a function in read access mode and returns the function's result. + /// Выполняет функцию в режиме доступа для чтения и возвращает полученный из неё результат. + /// + /// Type of function's result.Тип результата функции. + /// The function.Функция. + /// The function's result.Результат функции. + template + TResult DoRead(std::function function) + { + TResult result; + DoRead([&function, &result]() { + result = function(); + }); + return result; + } - virtual void ExecuteWriteOperation(std::function action) = 0; + /// + /// Executes action in write access mode. + /// Выполняет действие в режиме доступа для записи. + /// + /// The action.Действие. + virtual void DoWrite(std::function action) = 0; - TResult ExecuteWriteOperation(std::function function); + /// + /// Executes a function in write access mode and returns the function's result. + /// Выполняет функцию в режиме доступа для записи и возвращает полученный из неё результат. + /// + /// Type of function's result.Тип результата функции. + /// The function.Функция. + /// The function's result.Результат функции. + template + TResult DoWrite(std::function function) + { + TResult result; + DoWrite([&function, &result]() { + result = function(); + }); + return result; + } }; } \ No newline at end of file diff --git a/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h b/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h index a1c8b17..0872631 100644 --- a/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h +++ b/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h @@ -1,37 +1,115 @@ -namespace Platform::Threading::Synchronization +#pragma once + +#include "ISynchronization.h" +#include + +namespace Platform::Threading::Synchronization { + /// + /// Contains extension methods for the ISynchronization interface. + /// Содержит методы расширения для интерфейса ISynchronization. + /// class ISynchronizationExtensions { - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam parameter, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { return function(parameter); } }); + public: + // Single parameter overloads + template + static TResult DoRead(ISynchronization& synchronization, TParam parameter, std::function function) + { + return synchronization.DoRead([&]() { return function(parameter); }); + } - public: template static void ExecuteReadOperation(ISynchronization &synchronization, TParam parameter, std::function action) { synchronization.ExecuteReadOperation([&]()-> auto { return action(parameter); }); } + template + static void DoRead(ISynchronization& synchronization, TParam parameter, std::function action) + { + synchronization.DoRead([&]() { action(parameter); }); + } - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam parameter, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { return function(parameter); } }); + template + static TResult DoWrite(ISynchronization& synchronization, TParam parameter, std::function function) + { + return synchronization.DoWrite([&]() { return function(parameter); }); + } - public: template static void ExecuteWriteOperation(ISynchronization &synchronization, TParam parameter, std::function action) { synchronization.ExecuteWriteOperation([&]()-> auto { return action(parameter); }); } + template + static void DoWrite(ISynchronization& synchronization, TParam parameter, std::function action) + { + synchronization.DoWrite([&]() { action(parameter); }); + } - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { return function(parameter1, parameter2); } }); + // Two parameter overloads + template + static TResult DoRead(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, std::function function) + { + return synchronization.DoRead([&]() { return function(parameter1, parameter2); }); + } - public: static void ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, std::function action) { return synchronization.ExecuteReadOperation([&]()-> auto { return action(parameter1, parameter2); } }); + template + static void DoRead(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, std::function action) + { + synchronization.DoRead([&]() { action(parameter1, parameter2); }); + } - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { return function(parameter1, parameter2); } }); + template + static TResult DoWrite(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, std::function function) + { + return synchronization.DoWrite([&]() { return function(parameter1, parameter2); }); + } - public: static void ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, std::function action) { return synchronization.ExecuteWriteOperation([&]()-> auto { return action(parameter1, parameter2); } }); + template + static void DoWrite(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, std::function action) + { + synchronization.DoWrite([&]() { action(parameter1, parameter2); }); + } - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { return function(parameter1, parameter2, parameter3); } }); + // Three parameter overloads + template + static TResult DoRead(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, std::function function) + { + return synchronization.DoRead([&]() { return function(parameter1, parameter2, parameter3); }); + } - public: static void ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, std::function action) { return synchronization.ExecuteReadOperation([&]()-> auto { return action(parameter1, parameter2, parameter3); } }); + template + static void DoRead(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, std::function action) + { + synchronization.DoRead([&]() { action(parameter1, parameter2, parameter3); }); + } - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { return function(parameter1, parameter2, parameter3); } }); + template + static TResult DoWrite(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, std::function function) + { + return synchronization.DoWrite([&]() { return function(parameter1, parameter2, parameter3); }); + } - public: static void ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, std::function action) { return synchronization.ExecuteWriteOperation([&]()-> auto { return action(parameter1, parameter2, parameter3); } }); + template + static void DoWrite(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, std::function action) + { + synchronization.DoWrite([&]() { action(parameter1, parameter2, parameter3); }); + } - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { return function(parameter1, parameter2, parameter3, parameter4); } }); + // Four parameter overloads + template + static TResult DoRead(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, std::function function) + { + return synchronization.DoRead([&]() { return function(parameter1, parameter2, parameter3, parameter4); }); + } - public: static void ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, std::function action) { return synchronization.ExecuteReadOperation([&]()-> auto { return action(parameter1, parameter2, parameter3, parameter4); } }); + template + static void DoRead(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, std::function action) + { + synchronization.DoRead([&]() { action(parameter1, parameter2, parameter3, parameter4); }); + } - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { return function(parameter1, parameter2, parameter3, parameter4); } }); + template + static TResult DoWrite(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, std::function function) + { + return synchronization.DoWrite([&]() { return function(parameter1, parameter2, parameter3, parameter4); }); + } - public: static void ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, std::function action) { return synchronization.ExecuteWriteOperation([&]()-> auto { return action(parameter1, parameter2, parameter3, parameter4); } }); + template + static void DoWrite(ISynchronization& synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, TParam4 parameter4, std::function action) + { + synchronization.DoWrite([&]() { action(parameter1, parameter2, parameter3, parameter4); }); + } }; -} +} \ No newline at end of file diff --git a/cpp/Platform.Threading/Synchronization/ISynchronized.h b/cpp/Platform.Threading/Synchronization/ISynchronized.h index 922527a..c6295a8 100644 --- a/cpp/Platform.Threading/Synchronization/ISynchronized.h +++ b/cpp/Platform.Threading/Synchronization/ISynchronized.h @@ -1,13 +1,57 @@ -namespace Platform::Threading::Synchronization +#pragma once + +#include "ISynchronization.h" +#include + +namespace Platform::Threading::Synchronization { - template class ISynchronized; - template class ISynchronized + /// + /// Represents extendable synchronized interface access gate. + /// Представляет расширяемый интерфейс шлюза синхронизированного доступа. + /// + /// Synchronized interface.Синхронизируемый интерфейс. + template + class ISynchronized { public: - const ISynchronization *SyncRoot; + virtual ~ISynchronized() = default; + + /// + /// Gets synchronization method. + /// Возвращает способ синхронизации. + /// + virtual std::shared_ptr GetSyncRoot() const = 0; + + /// + /// Get source version of TInterface, that does not guarantee thread safe access synchronization. + /// Возвращает исходную версию TInterface, которая не гарантирует потокобезопасную синхронизацию доступа. + /// + /// + /// It is unsafe to use it directly, unless compound context using SyncRoot is created. + /// Использовать напрямую небезопасно, за исключением ситуации когда создаётся составной контекст с использованием SyncRoot. + /// + virtual TInterface& GetUnsync() = 0; + + /// + /// Get wrapped/decorated version of TInterface, that does guarantee thread safe access synchronization. + /// Возвращает обернутую/декорированную версию TInterface, которая гарантирует потокобезопасную синхронизацию доступа. + /// + /// + /// It is safe to use it directly, because it must be thread safe implementation. + /// Безопасно использовать напрямую, так как реализация должна быть потокобезопасной. + /// + virtual TInterface& GetSync() = 0; - const TInterface Unsync; + /// + /// Get const source version of TInterface, that does not guarantee thread safe access synchronization. + /// Возвращает константную исходную версию TInterface, которая не гарантирует потокобезопасную синхронизацию доступа. + /// + virtual const TInterface& GetUnsync() const = 0; - const TInterface Sync; + /// + /// Get const wrapped/decorated version of TInterface, that does guarantee thread safe access synchronization. + /// Возвращает константную обернутую/декорированную версию TInterface, которая гарантирует потокобезопасную синхронизацию доступа. + /// + virtual const TInterface& GetSync() const = 0; }; } diff --git a/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h b/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h index e564c53..0814191 100644 --- a/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h +++ b/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h @@ -1,59 +1,61 @@ -namespace Platform::Threading::Synchronization +#pragma once + +#include "ISynchronization.h" +#include +#include + +namespace Platform::Threading::Synchronization { + /// + /// Implementation of ISynchronization based on std::shared_mutex. + /// Реализация ISynchronization на основе std::shared_mutex. + /// class ReaderWriterLockSynchronization : public ISynchronization { - private: readonly ReaderWriterLockSlim _rwLock = ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + private: + mutable std::shared_mutex rwLock_; - public: void ExecuteReadOperation(std::function action) + public: + /// + void DoRead(std::function action) override { - _rwLock.EnterReadLock(); - try - { - action(); - } - finally - { - _rwLock.ExitReadLock(); - } + std::shared_lock lock(rwLock_); + action(); } - public: TResult ExecuteReadOperation(std::function function) + /// + /// Executes a function in read access mode and returns the function's result. + /// Выполняет функцию в режиме доступа для чтения и возвращает полученный из неё результат. + /// + /// Type of function's result.Тип результата функции. + /// The function.Функция. + /// The function's result.Результат функции. + template + TResult DoRead(std::function function) { - _rwLock.EnterReadLock(); - try - { - return function(); - } - finally - { - _rwLock.ExitReadLock(); - } + std::shared_lock lock(rwLock_); + return function(); } - public: void ExecuteWriteOperation(std::function action) + /// + void DoWrite(std::function action) override { - _rwLock.EnterWriteLock(); - try - { - action(); - } - finally - { - _rwLock.ExitWriteLock(); - } + std::unique_lock lock(rwLock_); + action(); } - public: TResult ExecuteWriteOperation(std::function function) + /// + /// Executes a function in write access mode and returns the function's result. + /// Выполняет функцию в режиме доступа для записи и возвращает полученный из неё результат. + /// + /// Type of function's result.Тип результата функции. + /// The function.Функция. + /// The function's result.Результат функции. + template + TResult DoWrite(std::function function) { - _rwLock.EnterWriteLock(); - try - { - return function(); - } - finally - { - _rwLock.ExitWriteLock(); - } + std::unique_lock lock(rwLock_); + return function(); } }; } \ No newline at end of file diff --git a/cpp/Platform.Threading/Synchronization/Unsynchronization.h b/cpp/Platform.Threading/Synchronization/Unsynchronization.h index e67301f..21ee0a8 100644 --- a/cpp/Platform.Threading/Synchronization/Unsynchronization.h +++ b/cpp/Platform.Threading/Synchronization/Unsynchronization.h @@ -1,13 +1,53 @@ -namespace Platform::Threading::Synchronization +#pragma once + +#include "ISynchronization.h" +#include + +namespace Platform::Threading::Synchronization { + /// + /// Implementation of ISynchronization that makes no actual synchronization. + /// Реализация ISynchronization, которая не выполняет фактическую синхронизацию. + /// class Unsynchronization : public ISynchronization { - public: void ExecuteReadOperation(std::function action) { action(); } + public: + /// + void DoRead(std::function action) override + { + action(); + } - public: TResult ExecuteReadOperation(std::function function) { return function(); } + /// + /// Executes a function in read access mode and returns the function's result. + /// Выполняет функцию в режиме доступа для чтения и возвращает полученный из неё результат. + /// + /// Type of function's result.Тип результата функции. + /// The function.Функция. + /// The function's result.Результат функции. + template + TResult DoRead(std::function function) + { + return function(); + } - public: void ExecuteWriteOperation(std::function action) { action(); } + /// + void DoWrite(std::function action) override + { + action(); + } - public: TResult ExecuteWriteOperation(std::function function) { return function(); } + /// + /// Executes a function in write access mode and returns the function's result. + /// Выполняет функцию в режиме доступа для записи и возвращает полученный из неё результат. + /// + /// Type of function's result.Тип результата функции. + /// The function.Функция. + /// The function's result.Результат функции. + template + TResult DoWrite(std::function function) + { + return function(); + } }; } \ No newline at end of file diff --git a/cpp/Platform.Threading/TaskExtensions.h b/cpp/Platform.Threading/TaskExtensions.h index b78a7da..06dac64 100644 --- a/cpp/Platform.Threading/TaskExtensions.h +++ b/cpp/Platform.Threading/TaskExtensions.h @@ -1,7 +1,54 @@ -namespace Platform::Threading +#pragma once + +#include +#include + +namespace Platform::Threading { + /// + /// Provides a set of extension methods for std::future objects. + /// Предоставляет набор методов расширения для объектов std::future. + /// class TaskExtensions { - public: template static TReturn AwaitResult(Task task) { return task.GetAwaiter().GetResult(); } + public: + /// + /// Waits for completion of the asynchronous std::future and returns its result. + /// Ожидает завершения асинхронного std::future и возвращает её результат. + /// + /// The return value type.Тип возвращаемого значения. + /// The asynchronous std::future.Асинхронный std::future. + /// The result of completed std::future.Результат завершённого std::future. + template + static inline TReturn AwaitResult(std::future& future) + { + return future.get(); + } + + /// + /// Waits for completion of the asynchronous std::future and returns its result. + /// Ожидает завершения асинхронного std::future и возвращает её результат. + /// + /// The return value type.Тип возвращаемого значения. + /// The asynchronous std::future.Асинхронный std::future. + /// The result of completed std::future.Результат завершённого std::future. + template + static inline TReturn AwaitResult(std::future&& future) + { + return future.get(); + } + + /// + /// Waits for completion of the asynchronous std::shared_future and returns its result. + /// Ожидает завершения асинхронного std::shared_future и возвращает её результат. + /// + /// The return value type.Тип возвращаемого значения. + /// The asynchronous std::shared_future.Асинхронный std::shared_future. + /// The result of completed std::shared_future.Результат завершённого std::shared_future. + template + static inline TReturn AwaitResult(std::shared_future& future) + { + return future.get(); + } }; } diff --git a/cpp/Platform.Threading/ThreadHelpers.cpp b/cpp/Platform.Threading/ThreadHelpers.cpp new file mode 100644 index 0000000..4015d8d --- /dev/null +++ b/cpp/Platform.Threading/ThreadHelpers.cpp @@ -0,0 +1,7 @@ +#include "ThreadHelpers.h" + +namespace Platform::Threading +{ + // Initialize static member - use default thread stack size + std::int32_t ThreadHelpers::DefaultMaxStackSize = 0; // 0 means use system default +} \ No newline at end of file diff --git a/cpp/Platform.Threading/ThreadHelpers.h b/cpp/Platform.Threading/ThreadHelpers.h index 1b4f38c..cfa285d 100644 --- a/cpp/Platform.Threading/ThreadHelpers.h +++ b/cpp/Platform.Threading/ThreadHelpers.h @@ -1,39 +1,148 @@ -namespace Platform::Threading +#pragma once + +#include +#include +#include +#include +#include + +namespace Platform::Threading { + /// + /// Provides a set of helper methods for std::thread objects. + /// Предоставляет набор вспомогательных методов для объектов std::thread. + /// class ThreadHelpers { - public: static std::int32_t DefaultMaxStackSize; + public: + /// + /// Gets the maximum stack size in bytes by default. + /// Возвращает размер максимальный стека в байтах по умолчанию. + /// + static std::int32_t DefaultMaxStackSize; - public: inline static const std::int32_t DefaultExtendedMaxStackSize = 256 * 1024 * 1024; + /// + /// Gets the extended maximum stack size in bytes by default. + /// Возвращает расширенный максимальный размер стека в байтах по умолчанию. + /// + inline static const std::int32_t DefaultExtendedMaxStackSize = 256 * 1024 * 1024; - public: inline static const std::int32_t DefaultSleepInterval = 1; + /// + /// Returns the default time interval for transferring control to other threads in milliseconds + /// Возвращает интервал времени для передачи управления другим потокам в миллисекундах по умолчанию. + /// + inline static const std::int32_t DefaultSleepInterval = 1; - public: template static void InvokeWithModifiedMaxStackSize(T param, std::function action, std::int32_t maxStackSize) { StartNew(param, action, maxStackSize).Join(); } + /// + /// Invokes the function with modified maximum stack size. + /// Вызывает функцию с изменённым максимальным размером стека. + /// + /// The type of the parameter.Тип параметра. + /// The object containing data to be used by the invoked function.Объект, содержащий данные, которые будут использоваться вызываемой функцией. + /// The function delegate.Делагат функции. + /// The maximum stack size in bytes (Note: C++ std::thread doesn't support stack size without boost).Максимальный размер стека в байтах (Примечание: std::thread C++ не поддерживает размер стека без boost). + template + static inline void InvokeWithModifiedMaxStackSize(T param, std::function action, std::int32_t maxStackSize) + { + auto thread = StartNew(param, action, maxStackSize); + thread.join(); + } - public: template static void InvokeWithExtendedMaxStackSize(T param, std::function action) { InvokeWithModifiedMaxStackSize(param, action, DefaultExtendedMaxStackSize); } + /// + /// Invokes the function with extend maximum stack size. + /// Вызывает функцию с расширенным максимальным размером стека. + /// + /// The type of the parameter.Тип параметра. + /// The object containing data to be used by the invoked function.Объект, содержащий данные, которые будут использоваться вызываемой функцией. + /// The function delegate.Делагат функции. + template + static inline void InvokeWithExtendedMaxStackSize(T param, std::function action) + { + InvokeWithModifiedMaxStackSize(param, action, DefaultExtendedMaxStackSize); + } - public: static void InvokeWithModifiedMaxStackSize(std::function action, std::int32_t maxStackSize) { StartNew(action, maxStackSize).Join(); } + /// + /// Invokes the function with modified maximum stack size. + /// Вызывает функцию с изменённым максимальным размером стека. + /// + /// The function delegate.Делагат функции. + /// The maximum stack size in bytes (Note: C++ std::thread doesn't support stack size without boost).Максимальный размер стека в байтах (Примечание: std::thread C++ не поддерживает размер стека без boost). + static inline void InvokeWithModifiedMaxStackSize(std::function action, std::int32_t maxStackSize) + { + auto thread = StartNew(action, maxStackSize); + thread.join(); + } - public: static void InvokeWithExtendedMaxStackSize(std::function action) { InvokeWithModifiedMaxStackSize(action, DefaultExtendedMaxStackSize); } + /// + /// Invokes the function with extend maximum stack size. + /// Вызывает функцию с расширенным максимальным размером стека. + /// + /// The function delegate.Делагат функции. + static inline void InvokeWithExtendedMaxStackSize(std::function action) + { + InvokeWithModifiedMaxStackSize(action, DefaultExtendedMaxStackSize); + } - public: template static Thread StartNew(T param, std::function action, std::int32_t maxStackSize) + /// + /// Initializes a new instance of the std::jthread class, causes the operating system to start that thread and supplies an object containing data to be used by the method that thread executes. + /// Инициализирует новый экземпляр класса std::jthread, просит операционную систему запустить этот поток и предоставляет объект, содержащий данные, которые будут использоваться в методе, который выполняет этот поток. + /// + /// The type of the parameter.Тип параметра. + /// The object containing data to be used by the method that thread executes.Объект, содержащий данные, которые будут использоваться методом, выполняемым потоком. + /// The function delegate.Делагат функции. + /// The maximum stack size in bytes (Note: ignored as std::jthread doesn't support stack size without boost).Максимальный размер стека в байтах (Примечание: игнорируется, поскольку std::jthread не поддерживает размер стека без boost). + /// A new started std::jthread instance.Новый запущенный экземпляр std::jthread. + template + static inline std::jthread StartNew(T param, std::function action, std::int32_t maxStackSize) { - auto thread = Thread(ParameterizedThreadStart(action), maxStackSize); - thread.Start(param); - return thread; + return std::jthread([param, action]() { + action(param); + }); } - public: template static Thread StartNew(T param, std::function action) { return StartNew(param, action, DefaultMaxStackSize); } + /// + /// Initializes a new instance of the std::jthread class, causes the operating system to start that thread and supplies an object containing data to be used by the method that thread executes. + /// Инициализирует новый экземпляр класса std::jthread, просит операционную систему запустить этот поток и предоставляет объект, содержащий данные, которые будут использоваться в методе, который выполняет этот поток. + /// + /// The type of the parameter.Тип параметра. + /// The object containing data to be used by the method that thread executes.Объект, содержащий данные, которые будут использоваться методом, выполняемым потоком. + /// The function delegate.Делагат функции. + /// A new started std::jthread instance.Новый запущенный экземпляр std::jthread. + template + static inline std::jthread StartNew(T param, std::function action) + { + return StartNew(param, action, DefaultMaxStackSize); + } - public: static Thread StartNew(std::function action, std::int32_t maxStackSize) + /// + /// Initializes a new instance of the std::jthread class, causes the operating system to start that thread and supplies the method executed by that thread. + /// Инициализирует новый экземпляр класса std::jthread, просит операционную систему запустить этот поток и предоставляет метод, который выполняется этим потоком. + /// + /// The function delegate.Делагат функции. + /// The maximum stack size in bytes (Note: ignored as std::jthread doesn't support stack size without boost).Максимальный размер стека в байтах (Примечание: игнорируется, поскольку std::jthread не поддерживает размер стека без boost). + /// A new started std::jthread instance.Новый запущенный экземпляр std::jthread. + static inline std::jthread StartNew(std::function action, std::int32_t maxStackSize) { - auto thread = Thread(ThreadStart(action), maxStackSize); - thread.Start(); - return thread; + return std::jthread(action); } - public: static Thread StartNew(std::function action) { return StartNew(action, DefaultMaxStackSize); } + /// + /// Initializes a new instance of the std::jthread class, causes the operating system to start that thread and supplies the method executed by that thread. + /// Инициализирует новый экземпляр класса std::jthread, просит операционную систему запустить этот поток и предоставляет метод, который выполняется этим потоком. + /// + /// The function delegate.Делагат функции. + /// A new started std::jthread instance.Новый запущенный экземпляр std::jthread. + static inline std::jthread StartNew(std::function action) + { + return StartNew(action, DefaultMaxStackSize); + } - public: static void Sleep() { Thread.Sleep(DefaultSleepInterval); } + /// + /// Suspends the current thread for the DefaultSleepInterval. + /// + static inline void Sleep() + { + std::this_thread::sleep_for(std::chrono::milliseconds(DefaultSleepInterval)); + } }; } diff --git a/cpp/cmake/Platform.ThreadingConfig.cmake.in b/cpp/cmake/Platform.ThreadingConfig.cmake.in new file mode 100644 index 0000000..15f2275 --- /dev/null +++ b/cpp/cmake/Platform.ThreadingConfig.cmake.in @@ -0,0 +1,9 @@ +@PACKAGE_INIT@ + +include(CMakeFindDependencyMacro) + +find_dependency(Threads) + +include("${CMAKE_CURRENT_LIST_DIR}/Platform.ThreadingTargets.cmake") + +check_required_components(Platform.Threading) \ No newline at end of file