Skip to content
This repository was archived by the owner on Mar 17, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/cmake.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ jobs:
spinlock:
uses: ./.github/workflows/spinlock.yaml
mutex:
uses: ./.github/workflows/mutex.yaml
uses: ./.github/workflows/mutex.yaml
queue_spsc:
uses: ./.github/workflows/queue_spsc.yaml
4 changes: 3 additions & 1 deletion .github/workflows/mutex.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Cmake

on:
workflow_call:
push:
branches:
- master

env:
BUILD_TYPE_DEBUG: Debug
Expand Down
111 changes: 111 additions & 0 deletions .github/workflows/queue_spsc.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
name: Cmake

on: [push]

env:
BUILD_TYPE_DEBUG: Debug
BUILD_TYPE_RELEASE: Release

CLANG_15: clang++-15

jobs:
ubuntu-clang-15-debug-address-leak-undefined-sanitize:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Install Dependencies
uses: actions/cache@v2
with:
path: ~/.conan/data
key: ${{ runner.os }}-conan-${{ hashFiles('conanfile.txt') }}-queue-spsc-address
- run: |
export BUILD_TYPE=${{env.BUILD_TYPE_DEBUG}}
./tools/install_deps.sh
- run: |
mkdir -p ${{github.workspace}}/build
cmake -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=/bin/${{env.CLANG_15}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE_DEBUG}} \
-DCHECK_SANITIZE=ON -DENABLE_SANITIZER_ADDRESS=True -DENABLE_SANITIZER_LEAK=True -DENABLE_SANITIZER_UNDEFINED_BEHAVIOR=True
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_DEBUG}} --target queue_spsc -- -j 2
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_DEBUG}} --target unit_test_queue_spsc -- -j 2
- run: ${{github.workspace}}/build/bin/unit_test_queue_spsc
--gtest_shuffle
--gtest_color=yes

ubuntu-clang-15-debug-memorysanitize:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Install Dependencies
uses: actions/cache@v2
with:
path: ~/.conan/data
key: ${{ runner.os }}-conan-${{ hashFiles('conanfile.txt') }}-queue-spsc-address
- run: |
export BUILD_TYPE=${{env.BUILD_TYPE_DEBUG}}
./tools/install_deps.sh
- run: |
mkdir -p ${{github.workspace}}/build
cmake -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=/bin/${{env.CLANG_15}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE_DEBUG}} \
-DCHECK_SANITIZE=ON -DENABLE_SANITIZER_MEMORY=True
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_DEBUG}} --target queue_spsc -- -j 2
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_DEBUG}} --target unit_test_queue_spsc -- -j 2
- run: ${{github.workspace}}/build/bin/unit_test_queue_spsc
--gtest_shuffle
--gtest_color=yes

ubuntu-clang-15-debug-thread-sanitize:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Install Dependencies
uses: actions/cache@v2
with:
path: ~/.conan/data
key: ${{ runner.os }}-conan-${{ hashFiles('conanfile.txt') }}-queue-spsc-thread
- run: |
export BUILD_TYPE=${{env.BUILD_TYPE_DEBUG}}
./tools/install_deps.sh
- run: |
mkdir -p ${{github.workspace}}/build
cmake -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=/bin/${{env.CLANG_15}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE_DEBUG}} \
-DCHECK_SANITIZE=ON -DENABLE_SANITIZER_THREAD=True
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_DEBUG}} --target queue_spsc -- -j 2
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_DEBUG}} --target unit_test_queue_spsc -- -j 2
- run: ${{github.workspace}}/build/bin/unit_test_queue_spsc
--gtest_shuffle
--gtest_color=yes

ubuntu-clang-15-release:
timeout-minutes: 15
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
submodules: true
- name: Install Dependencies
uses: actions/cache@v2
with:
path: ~/.conan/data
key: ${{ runner.os }}-conan-${{ hashFiles('conanfile.txt') }}-queue-spsc-release
- run: |
export BUILD_TYPE=${{env.BUILD_TYPE_RELEASE}}
./tools/install_deps.sh
- run: |
mkdir -p ${{github.workspace}}/build
cmake -B ${{github.workspace}}/build -DCMAKE_CXX_COMPILER=/bin/${{env.CLANG_15}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE_RELEASE}}
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_RELEASE}} --target queue_spsc -- -j 2
cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_RELEASE}} --target unit_test_queue_spsc -- -j 2
# cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE_RELEASE}} --target benchmark_stack_counting -- -j 2
- run: ${{github.workspace}}/build/bin/unit_test_queue_spsc
--gtest_shuffle
--gtest_color=yes
# - run: ${{github.workspace}}/build/bin/benchmark_sync
4 changes: 3 additions & 1 deletion .github/workflows/spinlock.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Cmake

on:
workflow_call:
push:
branches:
- master

env:
BUILD_TYPE_DEBUG: Debug
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/stack_counting.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Cmake

on:
workflow_call:
push:
branches:
- master

env:
BUILD_TYPE_DEBUG: Debug
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/stack_hazard_ptr.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
name: Cmake

on:
workflow_call:
push:
branches:
- master

env:
BUILD_TYPE_DEBUG: Debug
Expand Down
3 changes: 2 additions & 1 deletion lockfree/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
cmake_minimum_required(VERSION 3.25)

add_subdirectory(stack/counting)
add_subdirectory(stack/hazard)
add_subdirectory(stack/hazard)
add_subdirectory(queue/spsc)
18 changes: 18 additions & 0 deletions lockfree/queue/spsc/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.25)

include_directories(.)
add_library(queue_spsc
queue.h
)

target_link_libraries(queue_spsc
atomic
project_sanitizers
project_warnings
)

target_include_directories(queue_spsc PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")

set_target_properties(queue_spsc PROPERTIES LINKER_LANGUAGE CXX)

add_subdirectory(test)
64 changes: 64 additions & 0 deletions lockfree/queue/spsc/queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// Created by focus on 9/13/23.
//

#include <atomic>
#include <memory>


namespace sync_cpp {

template<class T>
class SPSCQueue final {
struct Node final {
std::shared_ptr<T> data{};
Node* next{nullptr};
};

std::atomic<Node*> head_;
std::atomic<Node*> tail_;

Node* pop_head() {
Node* const old_head = head_.load();
if (old_head == tail_.load()) {
return nullptr;
}
head_.store(old_head->next);
return old_head;
}

public:
SPSCQueue() : head_(new Node), tail_(head_.load())
{}

SPSCQueue(const SPSCQueue&) = delete;
SPSCQueue(SPSCQueue&&) noexcept = delete;
~SPSCQueue() {
while (auto* const old_head = head_.load()) {
head_ = old_head->next;
delete old_head;
}
}

void push(T new_value) {
auto new_data = std::make_shared<T>(std::move(new_value));
Node* p = new Node;
Node* const old_tail = tail_.load();
old_tail->data.swap(new_data);
old_tail->next = p;
tail_.store(p);
}

std::shared_ptr<T> pop() {
Node* old_head = pop_head();
if (!old_head) {
return std::shared_ptr<T>();
}

std::shared_ptr<T> res(old_head->data);
delete old_head;
return res;
}
};

} // namespace sync_cpp
20 changes: 20 additions & 0 deletions lockfree/queue/spsc/test/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
cmake_minimum_required(VERSION 3.25)

include_directories(${CONAN_INCLUDE_DIRS_GTEST})
include_directories(${CONAN_INCLUDE_DIRS_BENCHMARK})

set(TEST_APP_NAME unit_test_queue_spsc)

add_executable(${TEST_APP_NAME}
main.cpp
unit-tests.cpp
)

target_link_libraries(${TEST_APP_NAME}
queue_spsc
${CONAN_LIBS_GTEST}
project_sanitizers
project_warnings
)

enable_testing()
11 changes: 11 additions & 0 deletions lockfree/queue/spsc/test/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Created by focus on 9/13/23.
//

#include "gtest/gtest.h"

int
main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
66 changes: 66 additions & 0 deletions lockfree/queue/spsc/test/unit-tests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// Created by focus on 9/13/23.
//

#include "gtest/gtest.h"

#include <thread>

#include <queue.h>

class TestSPSCQueue : public ::testing::Test {
public:
};

TEST_F(TestSPSCQueue, simple_push_pop) {
sync_cpp::SPSCQueue<int> q;

q.push(117);
auto item = q.pop();
ASSERT_TRUE(item);
ASSERT_EQ(*item, 117);

auto empty = q.pop();
ASSERT_FALSE(empty);
}

TEST_F(TestSPSCQueue, only_pop) {
sync_cpp::SPSCQueue<size_t> q;

auto empty = q.pop();
ASSERT_FALSE(empty);

empty = q.pop();
ASSERT_FALSE(empty);
}

TEST_F(TestSPSCQueue, 2_threads) {
sync_cpp::SPSCQueue<size_t> q;

auto th1 = std::jthread([&] {
for (size_t i=0; i<20'000; i++) {
q.push(i);
}
});

std::vector<size_t> result;
result.reserve(20'000);
size_t counter = 0;
while(true) {
auto data = q.pop();
if (!data) {
continue;
}
counter++;
result.push_back(*data);
if (counter == 20'000) {
break;
}
}

std::sort(result.begin(), result.end());

for(size_t i=0; i<20'000; i++) {
ASSERT_EQ(result[i], i);
}
}
28 changes: 28 additions & 0 deletions lockfree/stack/hazard/hp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// Created by focus on 9/12/23.
//

#pragma once

#include <atomic>
#include <exception>
#include <memory>
#include <thread>

namespace sync_cpp {

static constexpr size_t MaxHazardPointers = 100;

struct HazardPtr {
std::atomic<std::thread::id> id;
std::atomic<void *> ptr;
};

HazardPtr hazard_ptrs[MaxHazardPointers];

template<typename T>
void do_delete(void *p) {
delete static_cast<T *>(p);
}

} // namespace sync_cpp
Loading