HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
wfree_fifo.hpp
1// Copyright Take Vos 2019-2022.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "architecture.hpp"
8#include "counters.hpp"
9#include "memory.hpp"
10#include "polymorphic_optional.hpp"
11#include <type_traits>
12#include <concepts>
13#include <atomic>
14#include <memory>
15#include <array>
16
17namespace hi::inline v1 {
18
29template<typename T, std::size_t SlotSize>
30class alignas(SlotSize) wfree_fifo {
31public:
32 static_assert(std::has_single_bit(SlotSize), "Only power-of-two number of messages size allowed.");
33 static_assert(SlotSize < 65536);
34
35 using value_type = T;
37
38 static constexpr std::size_t fifo_size = 65536;
39 static constexpr std::size_t slot_size = SlotSize;
40 static constexpr std::size_t num_slots = fifo_size / slot_size;
41
42 constexpr wfree_fifo() noexcept = default;
43 wfree_fifo(wfree_fifo const&) = delete;
44 wfree_fifo(wfree_fifo&&) = delete;
45 wfree_fifo& operator=(wfree_fifo const&) = delete;
46 wfree_fifo& operator=(wfree_fifo&&) = delete;
47
52 [[nodiscard]] bool empty() const noexcept
53 {
54 return _head.load(std::memory_order::relaxed) == _tail;
55 }
56
64 template<typename Func>
65 auto take_one(Func&& func) noexcept
66 {
67 auto result = get_slot(_tail).invoke_and_reset(std::forward<Func>(func));
68 if (result) {
69 _tail += slot_size;
70 }
71 return result;
72 }
73
80 template<typename Operation>
81 void take_all(Operation const& operation) noexcept
82 {
83 while (take_one(operation)) {}
84 }
85
93 template<typename Message, typename Func, typename... Args>
94 hi_force_inline auto emplace_and_invoke(Func&& func, Args&&...args) noexcept
95 {
96 // We need a new offset.
97 // - The offset is a byte index into 64kByte of memory.
98 // - Increment offset by the slot_size and the _head will overflow naturally
99 // at the end of the fifo.
100 // - We don't care about memory ordering with other writer threads. as
101 // each slot has an atomic for handling read/writer contention.
102 // - We don't have to check full/empty, this is done on the slot itself.
103 hilet offset = _head.fetch_add(slot_size, std::memory_order::relaxed);
104 return get_slot(offset).wait_emplace_and_invoke<Message>(std::forward<Func>(func), std::forward<Args>(args)...);
105 }
106
107 template<typename Func, typename Object>
108 hi_force_inline auto insert_and_invoke(Func&& func, Object&& object) noexcept
109 {
110 return emplace_and_invoke<std::decay_t<Object>>(std::forward<Func>(func), std::forward<Object>(object));
111 }
112
113 template<typename Message, typename... Args>
114 hi_force_inline void emplace(Args&&...args) noexcept
115 {
116 return emplace_and_invoke<Message>([](Message&) -> void {}, std::forward<Args>(args)...);
117 }
118
119 template<typename Object>
120 hi_force_inline void insert(Object &&object) noexcept
121 {
122 return emplace<std::decay_t<Object>>(std::forward<Object>(object));
123 }
124
125private:
126 std::array<slot_type, num_slots> _slots = {}; // must be at offset 0
127 std::atomic<uint16_t> _head = 0;
129 uint16_t _tail = 0;
130
133 hi_force_inline slot_type& get_slot(uint16_t offset) noexcept
134 {
135 hi_axiom(offset % slot_size == 0);
136 // The head and tail are 16 bit offsets within the _slots, which are
137 return *std::launder(
138 std::assume_aligned<slot_size>(reinterpret_cast<slot_type *>(reinterpret_cast<char *>(this) + offset)));
139 }
140};
141
142} // namespace hi::inline v1
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
Functions and macros for handling architectural difference between compilers, CPUs and operating syst...
Polymorphic optional.
Definition polymorphic_optional.hpp:29
A wait-free multiple-producer/single-consumer fifo designed for absolute performance.
Definition wfree_fifo.hpp:30
auto take_one(Func &&func) noexcept
Take one message from the fifo slot.
Definition wfree_fifo.hpp:65
void take_all(Operation const &operation) noexcept
Take all message from the queue.
Definition wfree_fifo.hpp:81
bool empty() const noexcept
Check if fifo is empty.
Definition wfree_fifo.hpp:52
hi_force_inline auto emplace_and_invoke(Func &&func, Args &&...args) noexcept
Create an message in-place on the fifo.
Definition wfree_fifo.hpp:94