Skip to content

Commit

Permalink
Merge pull request #13 from boki1/TEL8-HarrisLinkedList
Browse files Browse the repository at this point in the history
Tel8: Harris linked list
  • Loading branch information
boki1 authored Jul 14, 2021
2 parents 45f7a6c + 7067f8d commit d63a6ba
Show file tree
Hide file tree
Showing 16 changed files with 1,240 additions and 117 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-20.04]
cxx: [clang++]
cxx: [g++]

steps:
- uses: actions/checkout@v2
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ include(FetchContent)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_BUILD_TYPE debug)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20 -fconcepts-diagnostics-depth=3")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++2a")

enable_testing()

Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ target_include_directories(loguru PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/extern")
add_subdirectory(telamon)
target_include_directories(telamon PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

add_subdirectory(example_client)
target_include_directories(linked_list PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

add_subdirectory(tests)

6 changes: 6 additions & 0 deletions src/example_client/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
add_library(linked_list list/NormalizedLinkedList.hh list/LockFreeLinkedList.hh)

set_target_properties(linked_list PROPERTIES LINKER_LANGUAGE CXX)
target_link_libraries(linked_list PRIVATE expected_lite)
target_link_libraries(linked_list PRIVATE telamon)

172 changes: 172 additions & 0 deletions src/example_client/list/LockFreeLinkedList.hh
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
#ifndef HARRIS_LINKED_LIST_TELAMON_CLIENT_H
#define HARRIS_LINKED_LIST_TELAMON_CLIENT_H

#include <atomic>
#include <utility>
#include <optional>
#include <concepts>
#include <ranges>
#include <limits>
#include <array>

#include <extern/expected_lite/expected.hpp>

#include <telamon/WaitFreeSimulator.hh>
#include <telamon/Versioning.hh>
namespace tsim = telamon_simulator;

namespace harrislinkedlist {

/// \brief Implementation of Harris' Linked list
/// \details This is the original paper https://www.microsoft.com/en-us/research/wp-content/uploads/2001/10/2001-disc.pdf
/// \tparam T Has to be either an integral type or a floating type
template<typename T> requires std::integral<T> || std::floating_point<T>
class LinkedList {
public:
class Node {
public:
explicit Node (const T &value, bool marked = false, Node *next = nullptr) : m_value{value}, m_mark{marked}, m_next{next} {}
Node (const Node &rhs) : m_value{rhs.m_value}, m_next{rhs.m_next.load()}, m_mark{rhs.m_mark.load()} {}

public:
[[nodiscard]] bool is_removed () const noexcept { return m_mark.load(); }
[[nodiscard]] auto value () const noexcept -> T { return m_value; }
[[nodiscard]]auto next_atomic () noexcept -> std::atomic<Node *> & { return m_next; }
[[nodiscard]] auto next () const noexcept -> Node * { return m_next.load(); }
void mark (bool t_mark = true) noexcept { return m_mark.store(t_mark); } // TODO: CasDescriptor?
void set_next (Node *t_next) noexcept { m_next.store(t_next); }

private:
T m_value;
std::atomic<Node *> m_next;
std::atomic<bool> m_mark;//< Marks whether the node has been \e logically deleted
};

public:
LinkedList () // TODO: Hazptr
: m_head{new Node{std::numeric_limits<T>::min()}},
m_tail{new Node{std::numeric_limits<T>::max()}},
m_size{0} {
head()->set_next(tail());
}

public:
/// \brief Insert an element into the linked list
/// \param value The value to be inserted
auto insert (T value) -> bool {
auto *new_node = new Node{value};
while (true) {
auto[left, right] = search(value);
auto right_ptr = &right;
if (right_ptr != tail() && right.value() == value) {
if (right.is_removed()) {
right.mark(false);
break;
}
return false; //< Already present
}
new_node->set_next(right_ptr);
std::atomic<Node *> &left_next_atom = left.next_atomic();
if (left_next_atom.compare_exchange_strong(right_ptr, new_node)) {
m_size.fetch_add(1);
break;
}
}

return true;
}

/// \brief Check whether an element appears in the linked list
/// \param desired The value that is looked for
auto appears (T desired) -> bool {
const auto *tail_ = tail();
for (auto *it = head()->next(); it != tail_; it = it->next()) {
if (is_removed(it)) { continue; }
auto actual = it->value();
if (actual > desired) { break; }
if (actual == desired) { return true; }
}
return false;
}

/// \brief Remove an element from the linked list
/// \param value The value to be removed
auto remove (T value) -> bool {
while (true) {
auto[left, right] = search(value);
if (&right == tail() || right.value() != value) {
return false;
}
Node *right_ptr = &right;
if (is_removed(right_ptr)) {
// Already logically removed
break;
}
auto *updated_right_ptr = new Node{right_ptr->value(), true, right_ptr->next()};

if (left.next_atomic().compare_exchange_strong(right_ptr, updated_right_ptr)) {
// Successful CAS
break;
}
}

m_size.fetch_sub(1);
return true;
}

auto search (T value) -> std::pair<Node &, Node &> {
Node *left_ptr{nullptr};
Node *left_next{nullptr};
while (true) {
Node *right_ptr{nullptr};
Node *current = head();
Node *next = head()->next();

/// 1. Find left and right pointers
for (auto marked = is_removed(next);
marked || current->value() < value;
next = current->next()) {
if (!marked) {
left_ptr = current;
left_next = next;
}
current = next;
if (current == tail()) {
break;
}
}
right_ptr = current;

/// 2. Check nodes are adjacent
if (left_next == right_ptr) {
if (right_ptr != tail() && right_ptr->next()->is_removed()) continue;
return std::pair<Node &, Node &>{*left_ptr, *right_ptr};
}

/// 3. Remove marked nodes
if (left_ptr->next_atomic().compare_exchange_strong(left_next, right_ptr)) {
if (right_ptr != m_tail && right_ptr->next()->is_removed()) continue;
return std::pair<Node &, Node &>{*left_ptr, *right_ptr};
}
}
}

public:
[[nodiscard]] auto tail () const noexcept -> Node * { return m_tail.load(); }
[[nodiscard]] auto head () const noexcept -> Node * { return m_head.load(); }
[[nodiscard]] auto size () const noexcept -> std::size_t { return m_size.load(); }

static bool is_removed (Node *const node) noexcept {
if (!node) return false;
return node->is_removed();
}

private:
std::atomic<Node *> m_head;
std::atomic<Node *> m_tail;
std::atomic<std::size_t> m_size;
};

}// namespace linkedlist

#endif// HARRIS_LINKED_LIST_TELAMON_CLIENT_H
Loading

0 comments on commit d63a6ba

Please sign in to comment.