diff --git a/lib/pls/CMakeLists.txt b/lib/pls/CMakeLists.txt index c777def..f30cd59 100644 --- a/lib/pls/CMakeLists.txt +++ b/lib/pls/CMakeLists.txt @@ -20,7 +20,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 src/internal/data_structures/locking_deque.cpp + include/pls/internal/data_structures/locking_deque.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 @@ -34,7 +34,7 @@ add_library(pls STATIC include/pls/internal/scheduling/scheduler_impl.h include/pls/internal/scheduling/task.h src/internal/scheduling/task.cpp include/pls/internal/scheduling/scheduler_memory.h src/internal/scheduling/scheduler_memory.cpp - include/pls/internal/scheduling/lambda_task.h) + include/pls/internal/scheduling/lambda_task.h include/pls/internal/data_structures/deque.h) # Add everything in `./include` to be in the include path of this project target_include_directories(pls 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 fb19071..36051ce 100644 --- a/lib/pls/include/pls/internal/data_structures/locking_deque.h +++ b/lib/pls/include/pls/internal/data_structures/locking_deque.h @@ -1,34 +1,34 @@ -#ifndef PLS_DEQUE_H -#define PLS_DEQUE_H +#ifndef PLS_LOCKING_DEQUE_H +#define PLS_LOCKING_DEQUE_H + +#include #include "pls/internal/base/spin_lock.h" +#include "pls/internal/data_structures/aligned_stack.h" namespace pls { namespace internal { namespace data_structures { /** - * Turns any object into deque item when inheriting from this. + * Wraps any object into a deque item. */ -class locking_deque_item { - friend class locking_deque_internal; +template +struct locking_deque_item { + Item *item_; locking_deque_item *prev_; locking_deque_item *next_; }; -class locking_deque_internal { - protected: - locking_deque_item *head_; - locking_deque_item *tail_; - - base::spin_lock lock_; +template +struct locking_deque_container : public locking_deque_item { + Content content_; - locking_deque_item *pop_head_internal(); - locking_deque_item *pop_tail_internal(); - void push_tail_internal(locking_deque_item *new_item); + public: + explicit locking_deque_container(const Content &content_) : content_{content_} {} }; /** @@ -38,20 +38,82 @@ class locking_deque_internal { * @tparam Item The type of items stored in this deque */ template -class locking_deque : locking_deque_internal { +class locking_deque { + aligned_stack *stack_; + + locking_deque_item *head_; + locking_deque_item *tail_; + + base::spin_lock lock_; + public: - explicit locking_deque() : locking_deque_internal{} {} + using state = aligned_stack::state; + + 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"); - inline Item *pop_head() { - return static_cast(pop_head_internal()); + std::lock_guard lock{lock_}; + auto deque_item = stack_->push(locking_deque_container{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->item_; } - inline Item *pop_tail() { - return static_cast(pop_tail_internal()); + 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_; } - inline void push_tail(Item *new_item) { - push_tail_internal(new_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(); } }; @@ -59,4 +121,4 @@ class locking_deque : locking_deque_internal { } } -#endif //PLS_DEQUE_H +#endif //PLS_LOCKING_DEQUE_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 ffdf77f..9c542e9 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 @@ -76,13 +76,6 @@ class work_stealing_deque { tail_{other.tail_.load()}, previous_tail_{other.previous_tail_} {} - void reset_base_pointer(); - work_stealing_deque_item *item_at(offset_t offset); - offset_t current_stack_offset(); - - template - std::pair *allocate_item(const T &new_item); - template T *push_tail(const T &new_item); Item *pop_tail(); @@ -90,6 +83,14 @@ class work_stealing_deque { void release_memory_until(state state); state save_state(); + + private: + void reset_base_pointer(); + work_stealing_deque_item *item_at(offset_t offset); + offset_t current_stack_offset(); + + template + std::pair *allocate_item(const T &new_item); }; } diff --git a/test/data_structures_test.cpp b/test/data_structures_test.cpp index add0e4b..0b27ee0 100644 --- a/test/data_structures_test.cpp +++ b/test/data_structures_test.cpp @@ -77,58 +77,62 @@ 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 : public locking_deque_item { + class my_item { }; - locking_deque deque; - my_item one, two, three; + 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; SECTION("add and remove items form the tail") { - deque.push_tail(&one); - deque.push_tail(&two); - deque.push_tail(&three); + 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); + 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(one); + REQUIRE(*deque.pop_tail() == one); - deque.push_tail(&two); - REQUIRE(deque.pop_tail() == &two); + 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); + 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); + 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(one); + REQUIRE(*deque.pop_head() == one); - deque.push_tail(&two); - REQUIRE(deque.pop_head() == &two); + 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(one); + REQUIRE(*deque.pop_tail() == one); - deque.push_tail(&two); - REQUIRE(deque.pop_head() == &two); + deque.push_tail(two); + REQUIRE(*deque.pop_head() == two); - deque.push_tail(&three); - REQUIRE(deque.pop_tail() == &three); + deque.push_tail(three); + REQUIRE(*deque.pop_tail() == three); } }