HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
wfree_fifo.hpp
1// Copyright Take Vos 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 "polymorphic_optional.hpp"
8#include "../utility/utility.hpp"
9#include "../macros.hpp"
10#include <type_traits>
11#include <concepts>
12#include <atomic>
13#include <memory>
14#include <array>
15#include <bit>
16#include <chrono>
17#include <format>
18
19hi_export_module(hikogui.container.wfree_fifo);
20
21hi_warning_push();
22// C26490: Don't use reinterpret_cast (type.1).
23// Implementing a container.
24hi_warning_ignore_msvc(26490);
25
26hi_export namespace hi::inline v1 {
27
38template<typename T, std::size_t SlotSize>
39class alignas(SlotSize) wfree_fifo {
40public:
41 static_assert(std::has_single_bit(SlotSize), "Only power-of-two number of messages size allowed.");
42 static_assert(SlotSize < 65536);
43
44 using value_type = T;
46
47 constexpr static std::size_t fifo_size = 65536;
48 constexpr static std::size_t slot_size = SlotSize;
49 constexpr static std::size_t num_slots = fifo_size / slot_size;
50
51 constexpr wfree_fifo() noexcept = default;
52 wfree_fifo(wfree_fifo const&) = delete;
53 wfree_fifo(wfree_fifo&&) = delete;
54 wfree_fifo& operator=(wfree_fifo const&) = delete;
55 wfree_fifo& operator=(wfree_fifo&&) = delete;
56
61 [[nodiscard]] bool empty() const noexcept
62 {
63 return _head.load(std::memory_order::relaxed) == _tail;
64 }
65
73 template<typename Func>
74 auto take_one(Func&& func) noexcept
75 {
76 auto result = get_slot(_tail).invoke_and_reset(std::forward<Func>(func));
77 if (result) {
78 _tail += slot_size;
79 }
80 return result;
81 }
82
89 template<typename Operation>
90 void take_all(Operation const& operation) noexcept
91 {
92 while (take_one(operation)) {}
93 }
94
102 template<typename Message, typename Func, typename... Args>
103 hi_force_inline auto emplace_and_invoke(Func&& func, Args&&...args) noexcept
104 {
105 // We need a new offset.
106 // - The offset is a byte index into 64kByte of memory.
107 // - Increment offset by the slot_size and the _head will overflow naturally
108 // at the end of the fifo.
109 // - We don't care about memory ordering with other writer threads. as
110 // each slot has an atomic for handling read/writer contention.
111 // - We don't have to check full/empty, this is done on the slot itself.
112 auto const offset = _head.fetch_add(slot_size, std::memory_order::relaxed);
113 return get_slot(offset).template wait_emplace_and_invoke<Message>(std::forward<Func>(func), std::forward<Args>(args)...);
114 }
115
116 template<typename Func, typename Object>
117 hi_force_inline auto insert_and_invoke(Func&& func, Object&& object) noexcept
118 {
119 return emplace_and_invoke<std::decay_t<Object>>(std::forward<Func>(func), std::forward<Object>(object));
120 }
121
122 template<typename Message, typename... Args>
123 hi_force_inline void emplace(Args&&...args) noexcept
124 {
125 return emplace_and_invoke<Message>([](Message&) -> void {}, std::forward<Args>(args)...);
126 }
127
128 template<typename Object>
129 hi_force_inline void insert(Object &&object) noexcept
130 {
131 return emplace<std::decay_t<Object>>(std::forward<Object>(object));
132 }
133
134private:
135#if defined(__cpp_lib_hardware_interference_size)
136 constexpr static size_t destructive_interference_size = std::hardware_destructive_interference_size;
137#else
138 constexpr static size_t destructive_interference_size = 128;
139#endif
140
141 std::array<slot_type, num_slots> _slots = {}; // must be at offset 0
142 std::atomic<uint16_t> _head = 0;
144 uint16_t _tail = 0;
145
148 hi_force_inline slot_type& get_slot(uint16_t offset) noexcept
149 {
150 hi_axiom(offset % slot_size == 0);
151 // The head and tail are 16 bit offsets within the _slots, which are
152 return *std::launder(
153 std::assume_aligned<slot_size>(reinterpret_cast<slot_type *>(reinterpret_cast<char *>(this) + offset)));
154 }
155};
156
157} // namespace hi::inline v1
158
159hi_warning_pop();
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
Polymorphic optional.
Definition polymorphic_optional.hpp:43
A wait-free multiple-producer/single-consumer fifo designed for absolute performance.
Definition wfree_fifo.hpp:39
auto take_one(Func &&func) noexcept
Take one message from the fifo slot.
Definition wfree_fifo.hpp:74
void take_all(Operation const &operation) noexcept
Take all message from the queue.
Definition wfree_fifo.hpp:90
bool empty() const noexcept
Check if fifo is empty.
Definition wfree_fifo.hpp:61
hi_force_inline auto emplace_and_invoke(Func &&func, Args &&...args) noexcept
Create an message in-place on the fifo.
Definition wfree_fifo.hpp:103