Skip to content
Merged
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
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ target_include_directories(spsc_ring_buffer INTERFACE

option(BUILD_TESTING "Build unit test" ON)
option(BUILD_BENCHMARKS "Build microbenchmarks" ON)
option(BUILD_COMPILE_FAIL_TESTS "Build compile failure tests" OFF)

if(BUILD_TESTING)
enable_testing()
Expand Down
15 changes: 13 additions & 2 deletions include/spsc/ring_buffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,22 @@
#include <new>
#include <cstring>
#include <algorithm>
#include <type_traits>

namespace shovy{

template<typename T>
class RingBuffer{
static_assert(std::is_trivially_copyable_v<T>, "RingBuffer only supports trivially copyable types");

static_assert(std::atomic<size_t>::is_always_lock_free, "RingBuffer requires lock-free atomic operations");

#if defined(__cpp_lib_hardware_interference_size)
static constexpr size_t CacheLineSize = std::hardware_destructive_interference_size;
#else
static constexpr size_t CacheLineSize = 64; // Fallback default for x86_64/ARM
#endif

public:
explicit RingBuffer(size_t capacity) : capacity_(capacity) {
if (capacity_ == 0 || (capacity_ & (capacity_ - 1)) != 0) {
Expand Down Expand Up @@ -99,8 +110,8 @@ class RingBuffer{
private:
T* buffer_;
size_t capacity_;
alignas(std::hardware_destructive_interference_size) std::atomic<size_t> write_idx{0};
alignas(std::hardware_destructive_interference_size) std::atomic<size_t> read_idx{0};
alignas(CacheLineSize) std::atomic<size_t> write_idx{0};
alignas(CacheLineSize) std::atomic<size_t> read_idx{0};
};

} // namespace shovy
3 changes: 3 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ FetchContent_Declare(
set(gtest_force_shared_ctr ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(googletest)

if(BUILD_COMPILE_FAIL_TESTS)
add_subdirectory(compile_fail)
endif()

add_executable(ring_buffer_test ring_buffer_test.cpp)

Expand Down
12 changes: 12 additions & 0 deletions tests/compile_fail/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
add_executable(strTest string_type.cpp)
add_executable(vecTest vector_type.cpp)

target_link_libraries(strTest
PRIVATE
spsc_ring_buffer
)

target_link_libraries(vecTest
PRIVATE
spsc_ring_buffer
)
7 changes: 7 additions & 0 deletions tests/compile_fail/string_type.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <spsc/ring_buffer.hpp>
#include <string>

int main()
{
shovy::RingBuffer<std::string> buffer(64);
}
7 changes: 7 additions & 0 deletions tests/compile_fail/vector_type.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include <spsc/ring_buffer.hpp>
#include <vector>

int main()
{
shovy::RingBuffer<std::vector<int>> buffer(64);
}
26 changes: 26 additions & 0 deletions tests/ring_buffer_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,29 @@ TEST(RingBufferTest, BatchPopPartialWhenEmpty){
EXPECT_EQ(buffer.pop_batch(output, 8), 4);
EXPECT_TRUE(buffer.empty());
}

TEST(RingBufferTest, SupportsTriviallyCopyableTypes){
shovy::RingBuffer<int> intBuffer(64);
shovy::RingBuffer<float> floatBuffer(64);
shovy::RingBuffer<size_t> sizeBuffer(64);

EXPECT_EQ(intBuffer.capacity(), 64);
EXPECT_EQ(floatBuffer.capacity(), 64);
EXPECT_EQ(sizeBuffer.capacity(), 64);
}

TEST(RingBufferTest, SupportsAudioFrame)
{
struct AudioFrame {
float left;
float right;
};

shovy::RingBuffer<AudioFrame> buffer(64);

EXPECT_EQ(buffer.capacity(), 64);

static_assert(std::is_trivially_copyable_v<AudioFrame>);
static_assert(!std::is_trivially_copyable_v<std::string>);
static_assert(!std::is_trivially_copyable_v<std::vector<int>>);
}
Loading