From c754067e30ce09eed9590c09f90e2d422172a6b3 Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 17:20:46 +0300 Subject: [PATCH 01/10] Start work --- cpp/CMakeLists.txt | 22 ++++++++ cpp/Platform.Threading.Tests/AllTests.cpp | 28 +++++++++++ .../ThreadHelpersTests.cpp | 23 ++++----- .../ConcurrentQueueExtensions.h | 33 ++++++++---- cpp/Platform.Threading/Platform.Threading.h | 19 +++++++ .../Synchronization/sync/sync.h | 50 +++++++++++++++++++ cpp/conanfile.txt | 6 +++ 7 files changed, 157 insertions(+), 24 deletions(-) create mode 100644 cpp/CMakeLists.txt create mode 100644 cpp/Platform.Threading.Tests/AllTests.cpp create mode 100644 cpp/Platform.Threading/Platform.Threading.h create mode 100644 cpp/Platform.Threading/Synchronization/sync/sync.h create mode 100644 cpp/conanfile.txt diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt new file mode 100644 index 0000000..956f743 --- /dev/null +++ b/cpp/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.13) + +set(LINKS_PLATFORM_TESTS OFF CACHE BOOL "Whether to compile tests") +set(LINKS_PLATFORM_EXTRA_FLAGS "" CACHE STRING "Extra compiler flags") + +project(Platform.Threading CXX) +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup(TARGETS) + +list(APPEND LINKS_PLATFORM_EXTRA_FLAGS ${CONAN_USER_PLATFORM.HASHING_suggested_flags}) + +add_library(${PROJECT_NAME}.Library INTERFACE) +target_include_directories(${PROJECT_NAME}.Library INTERFACE ${PROJECT_NAME}) +target_link_libraries(${PROJECT_NAME}.Library INTERFACE CONAN_PKG::platform.collections) +target_compile_options(${PROJECT_NAME}.Library INTERFACE ${LINKS_PLATFORM_EXTRA_FLAGS}) + +if(${LINKS_PLATFORM_TESTS}) + add_executable(${PROJECT_NAME}.Tests ${PROJECT_NAME}.Tests/AllTests.cpp) + set_target_properties(${PROJECT_NAME}.Tests PROPERTIES CXX_STANDARD 20) + target_link_libraries(${PROJECT_NAME}.Tests PRIVATE CONAN_PKG::gtest) + target_link_libraries(${PROJECT_NAME}.Tests PRIVATE ${PROJECT_NAME}.Library) +endif() \ No newline at end of file diff --git a/cpp/Platform.Threading.Tests/AllTests.cpp b/cpp/Platform.Threading.Tests/AllTests.cpp new file mode 100644 index 0000000..5cb3bde --- /dev/null +++ b/cpp/Platform.Threading.Tests/AllTests.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Platform.Threading.h" + +using namespace std::chrono_literals; +using namespace Platform::Threading; +using namespace Platform::Threading::Synchronization; + + +auto main() -> int { + auto queue = std::queue>(); + queue.push(std::async([]() { std::this_thread::sleep_for(50ms); std::cout << "I like C++\n"; })); + queue.push(std::async([]() { std::cout << "C# it's Go\n"; })); + queue.push(std::async([]() { std::this_thread::sleep_for(100ms); std::cout << "vno\n"; })); + queue.push(std::async([]() { std::cout << "async programming is super\n"; })); + + auto sync_queue = sync(std::move(queue)); + AwaitOne(sync_queue).wait(); + auto queue_two = Drop(std::move(sync_queue)); + std::cout << "not waited count: " << queue_two.size() << std::endl; +} \ No newline at end of file diff --git a/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp b/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp index 3f9115c..b475ff9 100644 --- a/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp +++ b/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp @@ -1,18 +1,15 @@ namespace Platform::Threading::Tests { - TEST_CLASS(ThreadHelpersTests) + TEST(ThreadHelpersTests, Invoke) { - 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); - } + 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); }; } diff --git a/cpp/Platform.Threading/ConcurrentQueueExtensions.h b/cpp/Platform.Threading/ConcurrentQueueExtensions.h index a7337a4..71b5ec6 100644 --- a/cpp/Platform.Threading/ConcurrentQueueExtensions.h +++ b/cpp/Platform.Threading/ConcurrentQueueExtensions.h @@ -1,23 +1,34 @@ namespace Platform::Threading { - class ConcurrentQueueExtensions + template + auto AwaitAll(Synchronization::sync>>& queue) { - public: static async Task AwaitAll(ConcurrentQueue queue) + auto lambda = [&queue] { - foreach (auto item in queue.DequeueAll()) + while (!queue->empty()) { - await item.ConfigureAwait(continueOnCapturedContext: false); + auto& item = queue->front(); + item.wait(); + queue->pop(); } - } + }; + return std::async(lambda); + } - public: static async Task AwaitOne(ConcurrentQueue queue) + template + auto AwaitOne(Synchronization::sync>>& queue) + { + auto lambda = [&queue] { - if (queue.TryDequeue(out Task item)) + if (!queue->empty()) { - await item.ConfigureAwait(continueOnCapturedContext: false); + auto& item = queue->front(); + item.wait(); + queue->pop(); } - } + }; + return std::async(lambda); + } - public: static void EnqueueAsRunnedTask(ConcurrentQueue queue, std::function action) { queue.Enqueue(Task.Run(action)); } - }; + // public: static void EnqueueAsRunnedTask(ConcurrentQueue queue, std::function action) { queue.Enqueue(Task.Run(action)); } } diff --git a/cpp/Platform.Threading/Platform.Threading.h b/cpp/Platform.Threading/Platform.Threading.h new file mode 100644 index 0000000..929b72e --- /dev/null +++ b/cpp/Platform.Threading/Platform.Threading.h @@ -0,0 +1,19 @@ +#ifndef PLATFORM_THREADING +#define PLATFORM_THREADING + +#include + +#include +#include +#include +#include + +namespace Platform::Threading::Synchronization +{ + #include "Synchronization/sync/sync.h" +} + +#include "ConcurrentQueueExtensions.h" + + +#endif diff --git a/cpp/Platform.Threading/Synchronization/sync/sync.h b/cpp/Platform.Threading/Synchronization/sync/sync.h new file mode 100644 index 0000000..85f5f68 --- /dev/null +++ b/cpp/Platform.Threading/Synchronization/sync/sync.h @@ -0,0 +1,50 @@ +template, + typename lock_t = std::unique_lock> +class sync +{ + mutable T data; + mutable mutex_t mutex; + + template + struct locked_caller + { + borrow_lock lock; + T* const ptr; + + public: + locked_caller(T* const ptr, mutex_t& mutex) : ptr(ptr), lock(mutex) {} + + T* operator->() && { return ptr; } + const T* operator->() const && { return ptr; } + }; + +public: + sync() = default; + + sync(auto&&... args) requires requires { decltype(data)(std::forward(args)...); } + : data(std::forward(args)...), + mutex() {} + + sync(const sync& other) + : data(other.data), + mutex() {} + + sync(sync&&) noexcept = default; + + auto operator->() { return locked_caller(&data, mutex); } + auto operator->() const { return locked_caller(&data, mutex); } + + /// friends + template + friend auto&& Drop(sync&& self); +}; + +template +sync(T) -> sync; + +template +auto&& Drop(sync&& self) { + return std::move(self.data); +} diff --git a/cpp/conanfile.txt b/cpp/conanfile.txt new file mode 100644 index 0000000..56ce160 --- /dev/null +++ b/cpp/conanfile.txt @@ -0,0 +1,6 @@ +[requires] +gtest/cci.20210126 +platform.collections/0.1.0 + +[generators] +cmake From ec5dccae14f7e60429d002b95061ce0cdbd88601 Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 17:22:03 +0300 Subject: [PATCH 02/10] Unused headers --- cpp/Platform.Threading.Tests/AllTests.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/cpp/Platform.Threading.Tests/AllTests.cpp b/cpp/Platform.Threading.Tests/AllTests.cpp index 5cb3bde..2ba07e7 100644 --- a/cpp/Platform.Threading.Tests/AllTests.cpp +++ b/cpp/Platform.Threading.Tests/AllTests.cpp @@ -1,12 +1,3 @@ -#include -#include -#include -#include -#include -#include -#include -#include - #include "Platform.Threading.h" using namespace std::chrono_literals; From b19dc33994242282bd63bc561d65beca254ac18a Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 17:22:51 +0300 Subject: [PATCH 03/10] Unused headers --- cpp/Platform.Threading/Platform.Threading.h | 1 + 1 file changed, 1 insertion(+) diff --git a/cpp/Platform.Threading/Platform.Threading.h b/cpp/Platform.Threading/Platform.Threading.h index 929b72e..ac62de8 100644 --- a/cpp/Platform.Threading/Platform.Threading.h +++ b/cpp/Platform.Threading/Platform.Threading.h @@ -3,6 +3,7 @@ #include +#include #include #include #include From c12b48382fe7862591d57d52648b01792834b689 Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 17:23:20 +0300 Subject: [PATCH 04/10] Unused headers --- cpp/Platform.Threading/Platform.Threading.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp/Platform.Threading/Platform.Threading.h b/cpp/Platform.Threading/Platform.Threading.h index ac62de8..7d2a620 100644 --- a/cpp/Platform.Threading/Platform.Threading.h +++ b/cpp/Platform.Threading/Platform.Threading.h @@ -1,9 +1,11 @@ #ifndef PLATFORM_THREADING #define PLATFORM_THREADING +#include // TODO: in Collections + #include -#include + #include #include #include From e71ccb4f17482daf30e29548c58e028f74c2f351 Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 17:33:22 +0300 Subject: [PATCH 05/10] sync -> Sync --- cpp/Platform.Threading.Tests/AllTests.cpp | 2 +- .../Synchronization/sync/sync.h | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cpp/Platform.Threading.Tests/AllTests.cpp b/cpp/Platform.Threading.Tests/AllTests.cpp index 2ba07e7..02bf213 100644 --- a/cpp/Platform.Threading.Tests/AllTests.cpp +++ b/cpp/Platform.Threading.Tests/AllTests.cpp @@ -12,7 +12,7 @@ auto main() -> int { queue.push(std::async([]() { std::this_thread::sleep_for(100ms); std::cout << "vno\n"; })); queue.push(std::async([]() { std::cout << "async programming is super\n"; })); - auto sync_queue = sync(std::move(queue)); + auto sync_queue = Sync(std::move(queue)); AwaitOne(sync_queue).wait(); auto queue_two = Drop(std::move(sync_queue)); std::cout << "not waited count: " << queue_two.size() << std::endl; diff --git a/cpp/Platform.Threading/Synchronization/sync/sync.h b/cpp/Platform.Threading/Synchronization/sync/sync.h index 85f5f68..d1c9b99 100644 --- a/cpp/Platform.Threading/Synchronization/sync/sync.h +++ b/cpp/Platform.Threading/Synchronization/sync/sync.h @@ -2,7 +2,7 @@ template, typename lock_t = std::unique_lock> -class sync +class Sync { mutable T data; mutable mutex_t mutex; @@ -21,30 +21,30 @@ class sync }; public: - sync() = default; + Sync() = default; - sync(auto&&... args) requires requires { decltype(data)(std::forward(args)...); } + Sync(auto&&... args) requires requires { decltype(data)(std::forward(args)...); } : data(std::forward(args)...), mutex() {} - sync(const sync& other) + Sync(const Sync& other) : data(other.data), mutex() {} - sync(sync&&) noexcept = default; + Sync(Sync&&) noexcept = default; auto operator->() { return locked_caller(&data, mutex); } auto operator->() const { return locked_caller(&data, mutex); } /// friends template - friend auto&& Drop(sync&& self); + friend auto&& Drop(Sync&& self); }; template -sync(T) -> sync; +Sync(T) -> Sync; template -auto&& Drop(sync&& self) { +auto&& Drop(Sync&& self) { return std::move(self.data); } From 177500899b713f7b9731d97b300f1318fde4a48c Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 20:06:09 +0300 Subject: [PATCH 06/10] Full write bib in one day --- cpp/Platform.Threading.Tests/AllTests.cpp | 20 +----- cpp/Platform.Threading.Tests/Await.cpp | 19 +++++ cpp/Platform.Threading.Tests/Sync.cpp | 34 +++++++++ .../ThreadHelpersTests.cpp | 15 ---- .../ConcurrentQueueExtensions.h | 4 +- cpp/Platform.Threading/Platform.Threading.h | 3 +- .../Synchronization/ISynchronization.h | 14 ---- .../ISynchronizationExtensions.h | 37 ---------- .../Synchronization/ISynchronized.h | 13 ---- .../ReaderWriterLockSynchronization.h | 71 ++++++------------- .../{sync/sync.h => Sync/Sync.h} | 0 .../Synchronization/Unsynchronization.h | 13 ---- cpp/Platform.Threading/TaskExtensions.h | 7 -- cpp/Platform.Threading/ThreadHelpers.h | 39 ---------- 14 files changed, 81 insertions(+), 208 deletions(-) create mode 100644 cpp/Platform.Threading.Tests/Await.cpp create mode 100644 cpp/Platform.Threading.Tests/Sync.cpp delete mode 100644 cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp delete mode 100644 cpp/Platform.Threading/Synchronization/ISynchronization.h delete mode 100644 cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h delete mode 100644 cpp/Platform.Threading/Synchronization/ISynchronized.h rename cpp/Platform.Threading/Synchronization/{sync/sync.h => Sync/Sync.h} (100%) delete mode 100644 cpp/Platform.Threading/Synchronization/Unsynchronization.h delete mode 100644 cpp/Platform.Threading/TaskExtensions.h delete mode 100644 cpp/Platform.Threading/ThreadHelpers.h diff --git a/cpp/Platform.Threading.Tests/AllTests.cpp b/cpp/Platform.Threading.Tests/AllTests.cpp index 02bf213..f57ede8 100644 --- a/cpp/Platform.Threading.Tests/AllTests.cpp +++ b/cpp/Platform.Threading.Tests/AllTests.cpp @@ -1,19 +1,5 @@ +#include #include "Platform.Threading.h" -using namespace std::chrono_literals; -using namespace Platform::Threading; -using namespace Platform::Threading::Synchronization; - - -auto main() -> int { - auto queue = std::queue>(); - queue.push(std::async([]() { std::this_thread::sleep_for(50ms); std::cout << "I like C++\n"; })); - queue.push(std::async([]() { std::cout << "C# it's Go\n"; })); - queue.push(std::async([]() { std::this_thread::sleep_for(100ms); std::cout << "vno\n"; })); - queue.push(std::async([]() { std::cout << "async programming is super\n"; })); - - auto sync_queue = Sync(std::move(queue)); - AwaitOne(sync_queue).wait(); - auto queue_two = Drop(std::move(sync_queue)); - std::cout << "not waited count: " << queue_two.size() << std::endl; -} \ No newline at end of file +#include "Await.cpp" +#include "Sync.cpp" diff --git a/cpp/Platform.Threading.Tests/Await.cpp b/cpp/Platform.Threading.Tests/Await.cpp new file mode 100644 index 0000000..6e8d546 --- /dev/null +++ b/cpp/Platform.Threading.Tests/Await.cpp @@ -0,0 +1,19 @@ +namespace Platform::Threading::Tests +{ + TEST(Await, One) + { + using namespace Synchronization; + using namespace std::chrono_literals; + + auto queue = std::queue>(); + queue.push(std::async([]() { std::this_thread::sleep_for(50ms); std::cout << "I like C++\n"; })); + queue.push(std::async([]() { std::cout << "C# it's Go\n"; })); + queue.push(std::async([]() { std::this_thread::sleep_for(100ms); std::cout << "lang\n"; })); + queue.push(std::async([]() { std::cout << "async programming is super\n"; })); + + auto sync_queue = Sync(std::move(queue)); + AwaitOne(sync_queue).wait(); + auto queue_two = Drop(std::move(sync_queue)); + std::cout << "not waited count: " << queue_two.size() << std::endl; + } +} \ No newline at end of file diff --git a/cpp/Platform.Threading.Tests/Sync.cpp b/cpp/Platform.Threading.Tests/Sync.cpp new file mode 100644 index 0000000..f364c18 --- /dev/null +++ b/cpp/Platform.Threading.Tests/Sync.cpp @@ -0,0 +1,34 @@ +namespace Platform::Threading::Tests +{ + TEST(Sync, Basic) + { + using namespace Synchronization; + + auto threads_count = 1000; + auto map_operations_count = 100; + Sync> dict{}; + + auto work = [&] + { + for (int i = 0; i < map_operations_count; i++) + { + dict->operator[](std::to_string(i))++; + } + }; + + std::vector threads; + + for (int i = 0; i < threads_count; i++) + { + threads.push_back(std::thread(work)); + } + + for (auto& thread : threads) { + thread.join(); + } + + for (auto&& [key, value] : Drop(std::move(dict))) { + ASSERT_EQ(value, threads_count); + } + } +} \ No newline at end of file diff --git a/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp b/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp deleted file mode 100644 index b475ff9..0000000 --- a/cpp/Platform.Threading.Tests/ThreadHelpersTests.cpp +++ /dev/null @@ -1,15 +0,0 @@ -namespace Platform::Threading::Tests -{ - TEST(ThreadHelpersTests, Invoke) - { - 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); - }; -} diff --git a/cpp/Platform.Threading/ConcurrentQueueExtensions.h b/cpp/Platform.Threading/ConcurrentQueueExtensions.h index 71b5ec6..e6df014 100644 --- a/cpp/Platform.Threading/ConcurrentQueueExtensions.h +++ b/cpp/Platform.Threading/ConcurrentQueueExtensions.h @@ -1,7 +1,7 @@ namespace Platform::Threading { template - auto AwaitAll(Synchronization::sync>>& queue) + auto AwaitAll(Synchronization::Sync>>& queue) { auto lambda = [&queue] { @@ -16,7 +16,7 @@ } template - auto AwaitOne(Synchronization::sync>>& queue) + auto AwaitOne(Synchronization::Sync>>& queue) { auto lambda = [&queue] { diff --git a/cpp/Platform.Threading/Platform.Threading.h b/cpp/Platform.Threading/Platform.Threading.h index 7d2a620..caaa969 100644 --- a/cpp/Platform.Threading/Platform.Threading.h +++ b/cpp/Platform.Threading/Platform.Threading.h @@ -13,9 +13,10 @@ namespace Platform::Threading::Synchronization { - #include "Synchronization/sync/sync.h" + #include "Synchronization/sync/Sync.h" } +#include "Synchronization/ReaderWriterLockSynchronization.h" #include "ConcurrentQueueExtensions.h" diff --git a/cpp/Platform.Threading/Synchronization/ISynchronization.h b/cpp/Platform.Threading/Synchronization/ISynchronization.h deleted file mode 100644 index 7a66bb2..0000000 --- a/cpp/Platform.Threading/Synchronization/ISynchronization.h +++ /dev/null @@ -1,14 +0,0 @@ -namespace Platform::Threading::Synchronization -{ - class ISynchronization - { - public: - virtual void ExecuteReadOperation(std::function action) = 0; - - TResult ExecuteReadOperation(std::function function); - - virtual void ExecuteWriteOperation(std::function action) = 0; - - TResult ExecuteWriteOperation(std::function function); - }; -} \ No newline at end of file diff --git a/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h b/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h deleted file mode 100644 index a1c8b17..0000000 --- a/cpp/Platform.Threading/Synchronization/ISynchronizationExtensions.h +++ /dev/null @@ -1,37 +0,0 @@ -namespace Platform::Threading::Synchronization -{ - class ISynchronizationExtensions - { - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam parameter, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { return function(parameter); } }); - - public: template static void ExecuteReadOperation(ISynchronization &synchronization, TParam parameter, std::function action) { synchronization.ExecuteReadOperation([&]()-> auto { return action(parameter); }); } - - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam parameter, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { return function(parameter); } }); - - public: template static void ExecuteWriteOperation(ISynchronization &synchronization, TParam parameter, std::function action) { synchronization.ExecuteWriteOperation([&]()-> auto { return action(parameter); }); } - - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { 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); } }); - - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { 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); } }); - - public: static TResult ExecuteReadOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, Func function) { return synchronization.ExecuteReadOperation([&]()-> auto { 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); } }); - - public: static TResult ExecuteWriteOperation(ISynchronization &synchronization, TParam1 parameter1, TParam2 parameter2, TParam3 parameter3, Func function) { return synchronization.ExecuteWriteOperation([&]()-> auto { 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); } }); - - 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); } }); - - 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); } }); - - 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); } }); - - 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); } }); - }; -} diff --git a/cpp/Platform.Threading/Synchronization/ISynchronized.h b/cpp/Platform.Threading/Synchronization/ISynchronized.h deleted file mode 100644 index 922527a..0000000 --- a/cpp/Platform.Threading/Synchronization/ISynchronized.h +++ /dev/null @@ -1,13 +0,0 @@ -namespace Platform::Threading::Synchronization -{ - template class ISynchronized; - template class ISynchronized - { - public: - const ISynchronization *SyncRoot; - - const TInterface Unsync; - - const TInterface Sync; - }; -} diff --git a/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h b/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h index e564c53..88eb62d 100644 --- a/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h +++ b/cpp/Platform.Threading/Synchronization/ReaderWriterLockSynchronization.h @@ -1,59 +1,30 @@ namespace Platform::Threading::Synchronization { - class ReaderWriterLockSynchronization : public ISynchronization + template< + typename mutex_t = std::recursive_mutex, + template typename lock_t = std::unique_lock> + void ExecuteReadOperation(auto&& action, auto&&... args) { - private: readonly ReaderWriterLockSlim _rwLock = ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + static mutex_t mutex; - public: void ExecuteReadOperation(std::function action) + lock_t lock(mutex); + try { - _rwLock.EnterReadLock(); - try - { - action(); - } - finally - { - _rwLock.ExitReadLock(); - } - } + action(std::forward(args)...); + } catch(...) {} + } - public: TResult ExecuteReadOperation(std::function function) - { - _rwLock.EnterReadLock(); - try - { - return function(); - } - finally - { - _rwLock.ExitReadLock(); - } - } - - public: void ExecuteWriteOperation(std::function action) - { - _rwLock.EnterWriteLock(); - try - { - action(); - } - finally - { - _rwLock.ExitWriteLock(); - } - } + template< + typename mutex_t = std::recursive_mutex, + template typename lock_t = std::unique_lock> + void ExecuteWriteOperation(auto&& action, auto&&... args) + { + static mutex_t mutex; - public: TResult ExecuteWriteOperation(std::function function) + lock_t lock(mutex); + try { - _rwLock.EnterWriteLock(); - try - { - return function(); - } - finally - { - _rwLock.ExitWriteLock(); - } - } - }; + action(std::forward(args)...); + } catch(...) {} + } } \ No newline at end of file diff --git a/cpp/Platform.Threading/Synchronization/sync/sync.h b/cpp/Platform.Threading/Synchronization/Sync/Sync.h similarity index 100% rename from cpp/Platform.Threading/Synchronization/sync/sync.h rename to cpp/Platform.Threading/Synchronization/Sync/Sync.h diff --git a/cpp/Platform.Threading/Synchronization/Unsynchronization.h b/cpp/Platform.Threading/Synchronization/Unsynchronization.h deleted file mode 100644 index e67301f..0000000 --- a/cpp/Platform.Threading/Synchronization/Unsynchronization.h +++ /dev/null @@ -1,13 +0,0 @@ -namespace Platform::Threading::Synchronization -{ - class Unsynchronization : public ISynchronization - { - public: void ExecuteReadOperation(std::function action) { action(); } - - public: TResult ExecuteReadOperation(std::function function) { return function(); } - - public: void ExecuteWriteOperation(std::function action) { action(); } - - public: TResult ExecuteWriteOperation(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 deleted file mode 100644 index b78a7da..0000000 --- a/cpp/Platform.Threading/TaskExtensions.h +++ /dev/null @@ -1,7 +0,0 @@ -namespace Platform::Threading -{ - class TaskExtensions - { - public: template static TReturn AwaitResult(Task task) { return task.GetAwaiter().GetResult(); } - }; -} diff --git a/cpp/Platform.Threading/ThreadHelpers.h b/cpp/Platform.Threading/ThreadHelpers.h deleted file mode 100644 index 1b4f38c..0000000 --- a/cpp/Platform.Threading/ThreadHelpers.h +++ /dev/null @@ -1,39 +0,0 @@ -namespace Platform::Threading -{ - class ThreadHelpers - { - public: static std::int32_t DefaultMaxStackSize; - - public: inline static const std::int32_t DefaultExtendedMaxStackSize = 256 * 1024 * 1024; - - public: 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(); } - - public: template static 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(); } - - public: static void InvokeWithExtendedMaxStackSize(std::function action) { InvokeWithModifiedMaxStackSize(action, DefaultExtendedMaxStackSize); } - - public: template static Thread StartNew(T param, std::function action, std::int32_t maxStackSize) - { - auto thread = Thread(ParameterizedThreadStart(action), maxStackSize); - thread.Start(param); - return thread; - } - - public: template static Thread StartNew(T param, std::function action) { return StartNew(param, action, DefaultMaxStackSize); } - - public: static Thread StartNew(std::function action, std::int32_t maxStackSize) - { - auto thread = Thread(ThreadStart(action), maxStackSize); - thread.Start(); - return thread; - } - - public: static Thread StartNew(std::function action) { return StartNew(action, DefaultMaxStackSize); } - - public: static void Sleep() { Thread.Sleep(DefaultSleepInterval); } - }; -} From 50936db2e41e3e5727d6fe76c317a651c434032e Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 20:13:27 +0300 Subject: [PATCH 07/10] Add test --- cpp/Platform.Threading.Tests/Sync.cpp | 35 ++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/cpp/Platform.Threading.Tests/Sync.cpp b/cpp/Platform.Threading.Tests/Sync.cpp index f364c18..d9caddd 100644 --- a/cpp/Platform.Threading.Tests/Sync.cpp +++ b/cpp/Platform.Threading.Tests/Sync.cpp @@ -1,6 +1,6 @@ namespace Platform::Threading::Tests { - TEST(Sync, Basic) + TEST(Synchronization, Sync) { using namespace Synchronization; @@ -31,4 +31,37 @@ namespace Platform::Threading::Tests ASSERT_EQ(value, threads_count); } } + + TEST(Synchronization, Explicit) + { + using namespace Synchronization; + + auto threads_count = 1000; + auto map_operations_count = 100; + + std::map dict{}; + + auto work = [&] + { + for (int i = 0; i < map_operations_count; i++) + { + dict.operator[](std::to_string(i))++; + } + }; + + std::vector threads; + + for (int i = 0; i < threads_count; i++) + { + threads.push_back(std::thread([&] { ExecuteWriteOperation(work); })); + } + + for (auto& thread : threads) { + thread.join(); + } + + for (auto&& [key, value] : dict) { + ASSERT_EQ(value, threads_count); + } + } } \ No newline at end of file From bc166a96dd57d91be783ada46ca3b27b310f7ed7 Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 23:29:10 +0300 Subject: [PATCH 08/10] Real safe --- .../ConcurrentQueueExtensions.h | 24 +++++++++++-------- .../Synchronization/Sync/Sync.h | 15 ++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/cpp/Platform.Threading/ConcurrentQueueExtensions.h b/cpp/Platform.Threading/ConcurrentQueueExtensions.h index e6df014..db10f16 100644 --- a/cpp/Platform.Threading/ConcurrentQueueExtensions.h +++ b/cpp/Platform.Threading/ConcurrentQueueExtensions.h @@ -1,30 +1,34 @@ namespace Platform::Threading { template - auto AwaitAll(Synchronization::Sync>>& queue) + auto AwaitAll(Synchronization::Sync>>& unsafe_queue) { - auto lambda = [&queue] + auto lambda = [&unsafe_queue] { - while (!queue->empty()) + auto locked_queue = *unsafe_queue; + auto& queue = *locked_queue; + while (!queue.empty()) { - auto& item = queue->front(); + auto& item = queue.front(); item.wait(); - queue->pop(); + queue.pop(); } }; return std::async(lambda); } template - auto AwaitOne(Synchronization::Sync>>& queue) + auto AwaitOne(Synchronization::Sync>>& unsafe_queue) { - auto lambda = [&queue] + auto lambda = [&unsafe_queue] { - if (!queue->empty()) + auto locked_queue = *unsafe_queue; + auto& queue = *locked_queue; + if (!queue.empty()) { - auto& item = queue->front(); + auto& item = queue.front(); item.wait(); - queue->pop(); + queue.pop(); } }; return std::async(lambda); diff --git a/cpp/Platform.Threading/Synchronization/Sync/Sync.h b/cpp/Platform.Threading/Synchronization/Sync/Sync.h index d1c9b99..e3152b3 100644 --- a/cpp/Platform.Threading/Synchronization/Sync/Sync.h +++ b/cpp/Platform.Threading/Synchronization/Sync/Sync.h @@ -20,6 +20,18 @@ class Sync const T* operator->() const && { return ptr; } }; + template> + struct locked_ref : private base + { + locked_ref(T* const ptr, mutex_t& mutex) : base(ptr, mutex) {} + + T& operator*() & { return *base::ptr; } + const T& operator*() const & { return *base::ptr; } + + T* operator->() & { return base::ptr; } + const T* operator->() const & { return base::ptr; } + }; + public: Sync() = default; @@ -33,6 +45,9 @@ class Sync Sync(Sync&&) noexcept = default; + auto operator*() { return locked_ref(&data, mutex); } + auto operator*() const { return locked_ref(&data, mutex); } + auto operator->() { return locked_caller(&data, mutex); } auto operator->() const { return locked_caller(&data, mutex); } From 24dfd412617c40b96b80a0598546213364f7c034 Mon Sep 17 00:00:00 2001 From: uselessgoddess Date: Tue, 21 Sep 2021 23:39:31 +0300 Subject: [PATCH 09/10] count update --- cpp/Platform.Threading.Tests/Sync.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cpp/Platform.Threading.Tests/Sync.cpp b/cpp/Platform.Threading.Tests/Sync.cpp index d9caddd..24c98a9 100644 --- a/cpp/Platform.Threading.Tests/Sync.cpp +++ b/cpp/Platform.Threading.Tests/Sync.cpp @@ -1,11 +1,11 @@ namespace Platform::Threading::Tests { - TEST(Synchronization, Sync) + TEST(Synchronization, Sync_Stupid) { using namespace Synchronization; auto threads_count = 1000; - auto map_operations_count = 100; + auto map_operations_count = 1000; Sync> dict{}; auto work = [&] @@ -37,7 +37,7 @@ namespace Platform::Threading::Tests using namespace Synchronization; auto threads_count = 1000; - auto map_operations_count = 100; + auto map_operations_count = 1000; std::map dict{}; From e8903142ade21f0e84bd2e5d4e51bd881db446aa Mon Sep 17 00:00:00 2001 From: FreePhoenix888 Date: Wed, 24 Nov 2021 21:04:51 +0600 Subject: [PATCH 10/10] Fix namespace `sync` => `Sync` --- cpp/Platform.Threading/Platform.Threading.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp/Platform.Threading/Platform.Threading.h b/cpp/Platform.Threading/Platform.Threading.h index caaa969..87af370 100644 --- a/cpp/Platform.Threading/Platform.Threading.h +++ b/cpp/Platform.Threading/Platform.Threading.h @@ -13,7 +13,7 @@ namespace Platform::Threading::Synchronization { - #include "Synchronization/sync/Sync.h" + #include "Synchronization/Sync/Sync.h" } #include "Synchronization/ReaderWriterLockSynchronization.h"