diff --git a/CMakeLists.txt b/CMakeLists.txt index 08592a5..26162ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.20) include(cmake/color.cmake) -set(BLT_VERSION 0.18.7) +set(BLT_VERSION 0.18.8) set(BLT_TEST_VERSION 0.0.1) set(BLT_TARGET BLT) diff --git a/include/blt/std/thread.h b/include/blt/std/thread.h index d1108c5..3720490 100644 --- a/include/blt/std/thread.h +++ b/include/blt/std/thread.h @@ -19,20 +19,91 @@ #ifndef BLT_THREAD_H #define BLT_THREAD_H +#include #include #include #include #include #include +#include #include #include #include #include #include #include +#include namespace blt { + + class barrier + { + public: + explicit barrier(blt::size_t threads, std::optional> exit_cond = {}): + thread_count(threads), threads_waiting(0), use_count(0), exit_cond(exit_cond), count_mutex(), cv() + { + if (threads == 0) + throw std::runtime_error("Barrier thread count cannot be 0"); + } + + barrier(const barrier& copy) = delete; + + barrier(barrier&& move) = delete; + + barrier& operator=(const barrier& copy) = delete; + + barrier& operator=(barrier&& move) = delete; + + ~barrier() = default; + + void wait() + { + // (unique_lock acquires lock) + std::unique_lock lock(count_mutex); + std::size_t current_uses = use_count; + + if (++threads_waiting == thread_count) + { + threads_waiting = 0; + use_count++; + cv.notify_all(); + } else + { + if constexpr (BUSY_LOOP_WAIT > 0) // NOLINT + { + lock.unlock(); + for (blt::size_t i = 0; i < BUSY_LOOP_WAIT; i++) + { + if (use_count != current_uses || (exit_cond && exit_cond->get())) + return; + } + lock.lock(); + } + cv.wait(lock, [this, ¤t_uses]() { + return (use_count != current_uses || (exit_cond && exit_cond->get())); + }); + } + } + + void notify_all() + { + cv.notify_all(); + } + + private: + blt::size_t thread_count; + blt::size_t threads_waiting; + blt::size_t use_count; + std::optional> exit_cond; + std::mutex count_mutex; + std::condition_variable cv; + + // improves performance by not blocking the thread for n iterations of the loop. + // If the condition is not met by the end of this loop we can block the thread. + static constexpr blt::size_t BUSY_LOOP_WAIT = 200; + }; + /** * @tparam queue should we use a queue or execute the same function over and over? */ diff --git a/libraries/parallel-hashmap b/libraries/parallel-hashmap index d88c5e1..8a889d3 160000 --- a/libraries/parallel-hashmap +++ b/libraries/parallel-hashmap @@ -1 +1 @@ -Subproject commit d88c5e15079047777b418132ece5879e7c9aaa2b +Subproject commit 8a889d3699b3c09ade435641fb034427f3fd12b6