Line data Source code
1 : #pragma once
2 : #include <jage/engine/memory/cacheline_size.hpp>
3 :
4 : #include <jage/engine/concurrency/internal/concepts/buffer.hpp>
5 :
6 : #include <array>
7 : #include <atomic>
8 : #include <cstddef>
9 :
10 : #if defined(JAGE_ENABLE_SANITY_CHECKS) and JAGE_ENABLE_SANITY_CHECKS == 1
11 : #include <stdexcept>
12 : #endif
13 :
14 : namespace jage::engine::containers::spmc::internal {
15 : template <class TEvent, std::size_t Capacity, template <class> class TAtomic,
16 : template <class, template <class> class> class TBuffer>
17 : requires(concurrency::internal::concepts::buffer<TBuffer<TEvent, TAtomic>>)
18 : class ring_buffer {
19 : alignas(memory::cacheline_size)
20 : std::array<TBuffer<TEvent, TAtomic>, Capacity> buffer_;
21 : alignas(memory::cacheline_size) TAtomic<std::size_t> write_head_;
22 :
23 : public:
24 2 : [[nodiscard]] static constexpr auto capacity() -> std::size_t {
25 2 : return Capacity;
26 : }
27 :
28 4 : [[nodiscard]] constexpr auto write_head() const -> std::size_t {
29 4 : return write_head_.load(std::memory_order::acquire);
30 : }
31 :
32 10 : constexpr auto push(const TEvent &event) -> void {
33 10 : const auto head = write_head_.load(std::memory_order::relaxed);
34 10 : buffer_[head % Capacity].write(event);
35 10 : write_head_.store(head + 1, std::memory_order::release);
36 10 : }
37 :
38 7 : [[nodiscard]] auto read(const std::size_t index) const -> TEvent {
39 : #if defined(JAGE_ENABLE_SANITY_CHECKS) and JAGE_ENABLE_SANITY_CHECKS == 1
40 : if (index >= Capacity) {
41 : throw std::invalid_argument{
42 : "Index is greater than capacity of ring buffer"};
43 : }
44 : #endif
45 7 : return buffer_[index].read();
46 : }
47 : };
48 : } // namespace jage::engine::containers::spmc::internal
|