From 9cf034e48c4fa88424a187f88418f069f7bcaaad Mon Sep 17 00:00:00 2001 From: FritzFlorian Date: Wed, 12 Jun 2019 17:08:09 +0200 Subject: [PATCH] Unify interface from different deques --- lib/pls/CMakeLists.txt | 1 + lib/pls/include/pls/internal/data_structures/locking_deque.h | 76 ++++++++++++---------------------------------------------------------------- lib/pls/include/pls/internal/data_structures/locking_deque_impl.h | 89 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/pls/include/pls/internal/data_structures/work_stealing_deque.h | 5 ----- test/data_structures_test.cpp | 297 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------------------------------------------------------------------------------------- 5 files changed, 264 insertions(+), 204 deletions(-) create mode 100644 lib/pls/include/pls/internal/data_structures/locking_deque_impl.h diff --git a/lib/pls/CMakeLists.txt b/lib/pls/CMakeLists.txt index d242258..f054b45 100644 --- a/lib/pls/CMakeLists.txt +++ b/lib/pls/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(pls STATIC include/pls/internal/data_structures/aligned_stack.h src/internal/data_structures/aligned_stack.cpp include/pls/internal/data_structures/aligned_stack_impl.h include/pls/internal/data_structures/locking_deque.h + include/pls/internal/data_structures/locking_deque_impl.h include/pls/internal/data_structures/work_stealing_deque.h include/pls/internal/data_structures/work_stealing_deque_impl.h include/pls/internal/data_structures/stamped_integer.h diff --git a/lib/pls/include/pls/internal/data_structures/locking_deque.h b/lib/pls/include/pls/internal/data_structures/locking_deque.h index a5e53fb..98b75aa 100644 --- a/lib/pls/include/pls/internal/data_structures/locking_deque.h +++ b/lib/pls/include/pls/internal/data_structures/locking_deque.h @@ -28,7 +28,8 @@ struct locking_deque_container : public locking_deque_item { Content content_; public: - explicit locking_deque_container(const Content &content_) : content_{content_} {} + template + explicit locking_deque_container(ARGS &&... args) : content_{std::forward(args)...} {} }; /** @@ -52,73 +53,20 @@ class locking_deque { explicit locking_deque(aligned_stack *stack) : stack_{stack}, head_{nullptr}, tail_{nullptr}, lock_{} {} - template - T *push_tail(const T &new_item) { - static_assert(std::is_same::value || std::is_base_of::value, - "Must only push types of onto work_stealing_deque"); - - std::lock_guard lock{lock_}; - auto deque_item = stack_->push>(new_item); - deque_item->item_ = &deque_item->content_; - - if (tail_ != nullptr) { - tail_->next_ = deque_item; - } else { - head_ = deque_item; - } - deque_item->prev_ = tail_; - deque_item->next_ = nullptr; - tail_ = deque_item; - - return &deque_item->content_; - } - - Item *pop_tail() { - std::lock_guard lock{lock_}; - - if (tail_ == nullptr) { - return nullptr; - } - - auto result = tail_; - tail_ = tail_->prev_; - if (tail_ == nullptr) { - head_ = nullptr; - } else { - tail_->next_ = nullptr; - } - - return result->item_; - } - - Item *pop_head() { - std::lock_guard lock{lock_}; - - if (head_ == nullptr) { - return nullptr; - } - - auto result = head_; - head_ = head_->next_; - if (head_ == nullptr) { - tail_ = nullptr; - } else { - head_->prev_ = nullptr; - } - - return result->item_; - } - - void release_memory_until(state state) { - stack_->reset_state(state); - } - state save_state() { - return stack_->save_state(); - } + template + T *push_tail(ARGS &&... args); + template + T *push_tail_cb(const Function &after_creation, ARGS &&... args); + Item *pop_tail(); + Item *pop_head(); + + void release_memory_until(state state); + state save_state(); }; } } } +#include "locking_deque_impl.h" #endif //PLS_LOCKING_DEQUE_H diff --git a/lib/pls/include/pls/internal/data_structures/locking_deque_impl.h b/lib/pls/include/pls/internal/data_structures/locking_deque_impl.h new file mode 100644 index 0000000..51a4c65 --- /dev/null +++ b/lib/pls/include/pls/internal/data_structures/locking_deque_impl.h @@ -0,0 +1,89 @@ + +#ifndef PLS_LOCKING_DEQUE_IMPL_H_ +#define PLS_LOCKING_DEQUE_IMPL_H_ + +namespace pls { +namespace internal { +namespace data_structures { + +template +template +T *locking_deque::push_tail(ARGS &&... args) { + return push_tail_cb([](T *) {}, std::forward(args)...); +} + +template +template +T *locking_deque::push_tail_cb(const Function &after_creation, ARGS &&...args) { + static_assert(std::is_same::value || std::is_base_of::value, + "Must only push types of onto work_stealing_deque"); + + std::lock_guard lock{lock_}; + auto deque_item = stack_->push>(std::forward(args)...); + deque_item->item_ = &deque_item->content_; + after_creation(&deque_item->content_); + + if (tail_ != nullptr) { + tail_->next_ = deque_item; + } else { + head_ = deque_item; + } + deque_item->prev_ = tail_; + deque_item->next_ = nullptr; + tail_ = deque_item; + + return &deque_item->content_; +} + +template +Item *locking_deque::pop_tail() { + std::lock_guard lock{lock_}; + + if (tail_ == nullptr) { + return nullptr; + } + + auto result = tail_; + tail_ = tail_->prev_; + if (tail_ == nullptr) { + head_ = nullptr; + } else { + tail_->next_ = nullptr; + } + + return result->item_; +} + +template +Item *locking_deque::pop_head() { + std::lock_guard lock{lock_}; + + if (head_ == nullptr) { + return nullptr; + } + + auto result = head_; + head_ = head_->next_; + if (head_ == nullptr) { + tail_ = nullptr; + } else { + head_->prev_ = nullptr; + } + + return result->item_; +} + +template +void locking_deque::release_memory_until(state state) { + stack_->reset_state(state); +} +template +typename locking_deque::state locking_deque::save_state() { + return stack_->save_state(); +} + +} +} +} + +#endif //PLS_LOCKING_DEQUE_IMPL_H_ diff --git a/lib/pls/include/pls/internal/data_structures/work_stealing_deque.h b/lib/pls/include/pls/internal/data_structures/work_stealing_deque.h index d39496f..f6338f9 100644 --- a/lib/pls/include/pls/internal/data_structures/work_stealing_deque.h +++ b/lib/pls/include/pls/internal/data_structures/work_stealing_deque.h @@ -75,11 +75,6 @@ class work_stealing_deque { previous_tail_{0} { reset_base_pointer(); } - work_stealing_deque(const work_stealing_deque &other) : stack_{other.stack_}, - base_pointer_{other.base_pointer_}, - head_{other.head_.load()}, - tail_{other.tail_.load()}, - previous_tail_{other.previous_tail_} {} template T *push_tail(ARGS &&... args); diff --git a/test/data_structures_test.cpp b/test/data_structures_test.cpp index 962cb39..2327fb9 100644 --- a/test/data_structures_test.cpp +++ b/test/data_structures_test.cpp @@ -76,145 +76,172 @@ TEST_CASE("aligned stack stores objects correctly", "[internal/data_structures/a } } -TEST_CASE("deque stores objects correctly", "[internal/data_structures/deque.h]") { - class my_item { - - }; - - constexpr long data_size = 2 << 14; - char data[data_size]; - aligned_stack stack{data, data_size}; - - locking_deque deque{&stack}; - int one = 1, two = 2, three = 3; - +TEST_CASE("work_stealing_deque functions correctly", "[internal/data_structures/work_stealing_deque.h]") { SECTION("add and remove items form the tail") { - deque.push_tail(one); - deque.push_tail(two); - deque.push_tail(three); - - REQUIRE(*deque.pop_tail() == three); - REQUIRE(*deque.pop_tail() == two); - REQUIRE(*deque.pop_tail() == one); - } - - SECTION("handles getting empty by popping the tail correctly") { - deque.push_tail(one); - REQUIRE(*deque.pop_tail() == one); - - deque.push_tail(two); - REQUIRE(*deque.pop_tail() == two); - } - - SECTION("remove items form the head") { - deque.push_tail(one); - deque.push_tail(two); - deque.push_tail(three); - - REQUIRE(*deque.pop_head() == one); - REQUIRE(*deque.pop_head() == two); - REQUIRE(*deque.pop_head() == three); - } - - SECTION("handles getting empty by popping the head correctly") { - deque.push_tail(one); - REQUIRE(*deque.pop_head() == one); - - deque.push_tail(two); - REQUIRE(*deque.pop_head() == two); - } - - SECTION("handles getting empty by popping the head and tail correctly") { - deque.push_tail(one); - REQUIRE(*deque.pop_tail() == one); - - deque.push_tail(two); - REQUIRE(*deque.pop_head() == two); - - deque.push_tail(three); - REQUIRE(*deque.pop_tail() == three); + constexpr long data_size = 2 << 14; + char data[data_size]; + aligned_stack stack{data, data_size}; + work_stealing_deque deque{&stack}; + + int one = 1, two = 2, three = 3, four = 4; + + SECTION("add and remove items form the tail") { + deque.push_tail(one); + deque.push_tail(two); + deque.push_tail(three); + + REQUIRE(*deque.pop_tail() == three); + REQUIRE(*deque.pop_tail() == two); + REQUIRE(*deque.pop_tail() == one); + } + + SECTION("handles getting empty by popping the tail correctly") { + deque.push_tail(one); + REQUIRE(*deque.pop_tail() == one); + + deque.push_tail(two); + REQUIRE(*deque.pop_tail() == two); + } + + SECTION("remove items form the head") { + deque.push_tail(one); + deque.push_tail(two); + deque.push_tail(three); + + REQUIRE(*deque.pop_head() == one); + REQUIRE(*deque.pop_head() == two); + REQUIRE(*deque.pop_head() == three); + } + + SECTION("handles getting empty by popping the head correctly") { + deque.push_tail(one); + REQUIRE(*deque.pop_head() == one); + + deque.push_tail(two); + REQUIRE(*deque.pop_head() == two); + } + + SECTION("handles getting empty by popping the head and tail correctly") { + deque.push_tail(one); + REQUIRE(*deque.pop_tail() == one); + + deque.push_tail(two); + REQUIRE(*deque.pop_head() == two); + + deque.push_tail(three); + REQUIRE(*deque.pop_tail() == three); + } + + SECTION("handles jumps bigger 1 correctly") { + deque.push_tail(one); + deque.push_tail(two); + REQUIRE(*deque.pop_tail() == two); + + deque.push_tail(three); + deque.push_tail(four); + REQUIRE(*deque.pop_head() == one); + REQUIRE(*deque.pop_head() == three); + REQUIRE(*deque.pop_head() == four); + } + + SECTION("handles stack reset 1 correctly when emptied by tail") { + deque.push_tail(one); + auto state = deque.save_state(); + deque.push_tail(two); + REQUIRE(*deque.pop_tail() == two); + + deque.release_memory_until(state); + REQUIRE(*deque.pop_tail() == one); + + deque.push_tail(three); + deque.push_tail(four); + REQUIRE(*deque.pop_head() == three); + REQUIRE(*deque.pop_tail() == four); + } } } -TEST_CASE("work stealing deque stores objects correctly", "[internal/data_structures/aligned_stack.h]") { - constexpr long data_size = 2 << 14; - char data[data_size]; - aligned_stack stack{data, data_size}; - work_stealing_deque deque{&stack}; - - int one = 1, two = 2, three = 3, four = 4; - +TEST_CASE("locking_deque functions correctly", "[internal/data_structures/locking_deque.h]") { SECTION("add and remove items form the tail") { - deque.push_tail(one); - deque.push_tail(two); - deque.push_tail(three); - - REQUIRE(*deque.pop_tail() == three); - REQUIRE(*deque.pop_tail() == two); - REQUIRE(*deque.pop_tail() == one); - } - - SECTION("handles getting empty by popping the tail correctly") { - deque.push_tail(one); - REQUIRE(*deque.pop_tail() == one); - - deque.push_tail(two); - REQUIRE(*deque.pop_tail() == two); - } - - SECTION("remove items form the head") { - deque.push_tail(one); - deque.push_tail(two); - deque.push_tail(three); - - REQUIRE(*deque.pop_head() == one); - REQUIRE(*deque.pop_head() == two); - REQUIRE(*deque.pop_head() == three); - } - - SECTION("handles getting empty by popping the head correctly") { - deque.push_tail(one); - REQUIRE(*deque.pop_head() == one); - - deque.push_tail(two); - REQUIRE(*deque.pop_head() == two); - } - - SECTION("handles getting empty by popping the head and tail correctly") { - deque.push_tail(one); - REQUIRE(*deque.pop_tail() == one); - - deque.push_tail(two); - REQUIRE(*deque.pop_head() == two); - - deque.push_tail(three); - REQUIRE(*deque.pop_tail() == three); - } - - SECTION("handles jumps bigger 1 correctly") { - deque.push_tail(one); - deque.push_tail(two); - REQUIRE(*deque.pop_tail() == two); - - deque.push_tail(three); - deque.push_tail(four); - REQUIRE(*deque.pop_head() == one); - REQUIRE(*deque.pop_head() == three); - REQUIRE(*deque.pop_head() == four); - } - - SECTION("handles stack reset 1 correctly when emptied by tail") { - deque.push_tail(one); - auto state = deque.save_state(); - deque.push_tail(two); - REQUIRE(*deque.pop_tail() == two); - - deque.release_memory_until(state); - REQUIRE(*deque.pop_tail() == one); - - deque.push_tail(three); - deque.push_tail(four); - REQUIRE(*deque.pop_head() == three); - REQUIRE(*deque.pop_tail() == four); + constexpr long data_size = 2 << 14; + char data[data_size]; + aligned_stack stack{data, data_size}; + locking_deque deque{&stack}; + + int one = 1, two = 2, three = 3, four = 4; + + SECTION("add and remove items form the tail") { + deque.push_tail(one); + deque.push_tail(two); + deque.push_tail(three); + + REQUIRE(*deque.pop_tail() == three); + REQUIRE(*deque.pop_tail() == two); + REQUIRE(*deque.pop_tail() == one); + } + + SECTION("handles getting empty by popping the tail correctly") { + deque.push_tail(one); + REQUIRE(*deque.pop_tail() == one); + + deque.push_tail(two); + REQUIRE(*deque.pop_tail() == two); + } + + SECTION("remove items form the head") { + deque.push_tail(one); + deque.push_tail(two); + deque.push_tail(three); + + REQUIRE(*deque.pop_head() == one); + REQUIRE(*deque.pop_head() == two); + REQUIRE(*deque.pop_head() == three); + } + + SECTION("handles getting empty by popping the head correctly") { + deque.push_tail(one); + REQUIRE(*deque.pop_head() == one); + + deque.push_tail(two); + REQUIRE(*deque.pop_head() == two); + } + + SECTION("handles getting empty by popping the head and tail correctly") { + deque.push_tail(one); + REQUIRE(*deque.pop_tail() == one); + + deque.push_tail(two); + REQUIRE(*deque.pop_head() == two); + + deque.push_tail(three); + REQUIRE(*deque.pop_tail() == three); + } + + SECTION("handles jumps bigger 1 correctly") { + deque.push_tail(one); + deque.push_tail(two); + REQUIRE(*deque.pop_tail() == two); + + deque.push_tail(three); + deque.push_tail(four); + REQUIRE(*deque.pop_head() == one); + REQUIRE(*deque.pop_head() == three); + REQUIRE(*deque.pop_head() == four); + } + + SECTION("handles stack reset 1 correctly when emptied by tail") { + deque.push_tail(one); + auto state = deque.save_state(); + deque.push_tail(two); + REQUIRE(*deque.pop_tail() == two); + + deque.release_memory_until(state); + REQUIRE(*deque.pop_tail() == one); + + deque.push_tail(three); + deque.push_tail(four); + REQUIRE(*deque.pop_head() == three); + REQUIRE(*deque.pop_tail() == four); + } } } -- libgit2 0.26.0