Commit 0cffce1c by FritzFlorian

Working version of locking-work-stealing-deque.

parent f7e7c79b
...@@ -20,7 +20,7 @@ add_library(pls STATIC ...@@ -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.h src/internal/data_structures/aligned_stack.cpp
include/pls/internal/data_structures/aligned_stack_impl.h 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/work_stealing_deque.h include/pls/internal/data_structures/work_stealing_deque_impl.h
include/pls/internal/data_structures/stamped_integer.h include/pls/internal/data_structures/stamped_integer.h
...@@ -34,7 +34,7 @@ add_library(pls STATIC ...@@ -34,7 +34,7 @@ add_library(pls STATIC
include/pls/internal/scheduling/scheduler_impl.h include/pls/internal/scheduling/scheduler_impl.h
include/pls/internal/scheduling/task.h src/internal/scheduling/task.cpp 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/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 # Add everything in `./include` to be in the include path of this project
target_include_directories(pls target_include_directories(pls
......
#ifndef PLS_DEQUE_H #ifndef PLS_LOCKING_DEQUE_H
#define PLS_DEQUE_H #define PLS_LOCKING_DEQUE_H
#include <mutex>
#include "pls/internal/base/spin_lock.h" #include "pls/internal/base/spin_lock.h"
#include "pls/internal/data_structures/aligned_stack.h"
namespace pls { namespace pls {
namespace internal { namespace internal {
namespace data_structures { namespace data_structures {
/** /**
* Turns any object into deque item when inheriting from this. * Wraps any object into a deque item.
*/ */
class locking_deque_item { template<typename Item>
friend class locking_deque_internal; struct locking_deque_item {
Item *item_;
locking_deque_item *prev_; locking_deque_item *prev_;
locking_deque_item *next_; locking_deque_item *next_;
}; };
class locking_deque_internal { template<typename Item, typename Content>
protected: struct locking_deque_container : public locking_deque_item<Item> {
locking_deque_item *head_; Content content_;
locking_deque_item *tail_;
base::spin_lock lock_;
locking_deque_item *pop_head_internal(); public:
locking_deque_item *pop_tail_internal(); explicit locking_deque_container(const Content &content_) : content_{content_} {}
void push_tail_internal(locking_deque_item *new_item);
}; };
/** /**
...@@ -38,20 +38,82 @@ class locking_deque_internal { ...@@ -38,20 +38,82 @@ class locking_deque_internal {
* @tparam Item The type of items stored in this deque * @tparam Item The type of items stored in this deque
*/ */
template<typename Item> template<typename Item>
class locking_deque : locking_deque_internal { class locking_deque {
aligned_stack *stack_;
locking_deque_item<Item> *head_;
locking_deque_item<Item> *tail_;
base::spin_lock lock_;
public: 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<typename T>
T *push_tail(const T &new_item) {
static_assert(std::is_same<Item, T>::value || std::is_base_of<Item, T>::value,
"Must only push types of <Item> onto work_stealing_deque<Item>");
inline Item *pop_head() { std::lock_guard<base::spin_lock> lock{lock_};
return static_cast<Item *>(pop_head_internal()); auto deque_item = stack_->push(locking_deque_container<Item, T>{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() { Item *pop_tail() {
return static_cast<Item *>(pop_tail_internal()); std::lock_guard<base::spin_lock> 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) { Item *pop_head() {
push_tail_internal(new_item); std::lock_guard<base::spin_lock> 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 { ...@@ -59,4 +121,4 @@ class locking_deque : locking_deque_internal {
} }
} }
#endif //PLS_DEQUE_H #endif //PLS_LOCKING_DEQUE_H
...@@ -76,13 +76,6 @@ class work_stealing_deque { ...@@ -76,13 +76,6 @@ class work_stealing_deque {
tail_{other.tail_.load()}, tail_{other.tail_.load()},
previous_tail_{other.previous_tail_} {} previous_tail_{other.previous_tail_} {}
void reset_base_pointer();
work_stealing_deque_item *item_at(offset_t offset);
offset_t current_stack_offset();
template<typename T>
std::pair<work_stealing_deque_item, T> *allocate_item(const T &new_item);
template<typename T> template<typename T>
T *push_tail(const T &new_item); T *push_tail(const T &new_item);
Item *pop_tail(); Item *pop_tail();
...@@ -90,6 +83,14 @@ class work_stealing_deque { ...@@ -90,6 +83,14 @@ class work_stealing_deque {
void release_memory_until(state state); void release_memory_until(state state);
state save_state(); state save_state();
private:
void reset_base_pointer();
work_stealing_deque_item *item_at(offset_t offset);
offset_t current_stack_offset();
template<typename T>
std::pair<work_stealing_deque_item, T> *allocate_item(const T &new_item);
}; };
} }
......
...@@ -77,58 +77,62 @@ TEST_CASE("aligned stack stores objects correctly", "[internal/data_structures/a ...@@ -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]") { TEST_CASE("deque stores objects correctly", "[internal/data_structures/deque.h]") {
class my_item : public locking_deque_item { class my_item {
}; };
locking_deque<my_item> deque; constexpr long data_size = 2 << 14;
my_item one, two, three; char data[data_size];
aligned_stack stack{data, data_size};
locking_deque<int> deque{&stack};
int one = 1, two = 2, three = 3;
SECTION("add and remove items form the tail") { SECTION("add and remove items form the tail") {
deque.push_tail(&one); deque.push_tail(one);
deque.push_tail(&two); deque.push_tail(two);
deque.push_tail(&three); deque.push_tail(three);
REQUIRE(deque.pop_tail() == &three); REQUIRE(*deque.pop_tail() == three);
REQUIRE(deque.pop_tail() == &two); REQUIRE(*deque.pop_tail() == two);
REQUIRE(deque.pop_tail() == &one); REQUIRE(*deque.pop_tail() == one);
} }
SECTION("handles getting empty by popping the tail correctly") { SECTION("handles getting empty by popping the tail correctly") {
deque.push_tail(&one); deque.push_tail(one);
REQUIRE(deque.pop_tail() == &one); REQUIRE(*deque.pop_tail() == one);
deque.push_tail(&two); deque.push_tail(two);
REQUIRE(deque.pop_tail() == &two); REQUIRE(*deque.pop_tail() == two);
} }
SECTION("remove items form the head") { SECTION("remove items form the head") {
deque.push_tail(&one); deque.push_tail(one);
deque.push_tail(&two); deque.push_tail(two);
deque.push_tail(&three); deque.push_tail(three);
REQUIRE(deque.pop_head() == &one); REQUIRE(*deque.pop_head() == one);
REQUIRE(deque.pop_head() == &two); REQUIRE(*deque.pop_head() == two);
REQUIRE(deque.pop_head() == &three); REQUIRE(*deque.pop_head() == three);
} }
SECTION("handles getting empty by popping the head correctly") { SECTION("handles getting empty by popping the head correctly") {
deque.push_tail(&one); deque.push_tail(one);
REQUIRE(deque.pop_head() == &one); REQUIRE(*deque.pop_head() == one);
deque.push_tail(&two); deque.push_tail(two);
REQUIRE(deque.pop_head() == &two); REQUIRE(*deque.pop_head() == two);
} }
SECTION("handles getting empty by popping the head and tail correctly") { SECTION("handles getting empty by popping the head and tail correctly") {
deque.push_tail(&one); deque.push_tail(one);
REQUIRE(deque.pop_tail() == &one); REQUIRE(*deque.pop_tail() == one);
deque.push_tail(&two); deque.push_tail(two);
REQUIRE(deque.pop_head() == &two); REQUIRE(*deque.pop_head() == two);
deque.push_tail(&three); deque.push_tail(three);
REQUIRE(deque.pop_tail() == &three); REQUIRE(*deque.pop_tail() == three);
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment