Line data Source code
1 : #pragma once
2 :
3 : #include <jage/engine/memory/cacheline_size.hpp>
4 : #include <jage/engine/memory/cacheline_slot.hpp>
5 :
6 : #include <algorithm>
7 : #include <array>
8 : #include <atomic>
9 : #include <cstddef>
10 :
11 : namespace jage::engine::containers::spsc {
12 : template <class TEvent, std::size_t Capacity,
13 : template <class> class TAtomic = std::atomic>
14 : class alignas(memory::cacheline_size) queue {
15 : alignas(memory::cacheline_size) TAtomic<std::uint64_t> head_{0UZ};
16 : alignas(memory::cacheline_size) TAtomic<std::uint64_t> tail_{0UZ};
17 : alignas(memory::cacheline_size)
18 : std::array<memory::cacheline_slot<TEvent>, Capacity> buffer_{};
19 :
20 : public:
21 : using value_type = TEvent;
22 19 : [[nodiscard]] auto empty() const -> bool {
23 19 : return head_.load(std::memory_order::acquire) ==
24 19 : tail_.load(std::memory_order::acquire);
25 : }
26 :
27 19 : [[nodiscard]] auto size() const -> std::size_t {
28 38 : return std::min(tail_.load(std::memory_order::acquire) -
29 19 : head_.load(std::memory_order::acquire),
30 19 : Capacity);
31 : }
32 1 : [[nodiscard]] static constexpr auto capacity() -> std::size_t {
33 1 : return Capacity;
34 : }
35 :
36 19 : auto push(TEvent &&event) -> void {
37 19 : const auto tail_index = tail_.load(std::memory_order::acquire);
38 19 : if (auto head_index = head_.load(std::memory_order::acquire);
39 19 : tail_index - head_index >= Capacity) {
40 4 : const auto desired_head = head_index + 1UZ;
41 4 : while (not head_.compare_exchange_weak(head_index, desired_head,
42 : std::memory_order::release,
43 7 : std::memory_order::acquire) and
44 3 : head_index < desired_head) {
45 : }
46 : }
47 19 : buffer_[tail_index % Capacity] = std::forward<decltype(event)>(event);
48 19 : tail_.store(tail_index + 1, std::memory_order::release);
49 19 : }
50 :
51 18 : [[nodiscard]] auto front() const -> TEvent {
52 18 : return buffer_[head_.load(std::memory_order::acquire) % Capacity];
53 : }
54 :
55 13 : auto pop() -> void {
56 13 : if (const auto current_head = head_.load(std::memory_order::acquire);
57 13 : current_head == tail_.load(std::memory_order::acquire)) [[unlikely]] {
58 1 : return;
59 : } else {
60 12 : head_.store(current_head + 1, std::memory_order::release);
61 : }
62 : }
63 : };
64 :
65 : } // namespace jage::engine::containers::spsc
|