/src/ocudu/external/rigtorp/MPMCQueue.h
Line | Count | Source |
1 | | /* |
2 | | Copyright (c) 2020 Erik Rigtorp <erik@rigtorp.se> |
3 | | |
4 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
5 | | of this software and associated documentation files (the "Software"), to deal |
6 | | in the Software without restriction, including without limitation the rights |
7 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
8 | | copies of the Software, and to permit persons to whom the Software is |
9 | | furnished to do so, subject to the following conditions: |
10 | | |
11 | | The above copyright notice and this permission notice shall be included in all |
12 | | copies or substantial portions of the Software. |
13 | | |
14 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
17 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
18 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
19 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
20 | | SOFTWARE. |
21 | | */ |
22 | | |
23 | | #pragma once |
24 | | |
25 | | #include <atomic> |
26 | | #include <cassert> |
27 | | #include <cstddef> // offsetof |
28 | | #include <limits> |
29 | | #include <memory> |
30 | | #include <new> // std::hardware_destructive_interference_size |
31 | | #include <stdexcept> |
32 | | |
33 | | #ifndef __cpp_aligned_new |
34 | | #ifdef _WIN32 |
35 | | #include <malloc.h> // _aligned_malloc |
36 | | #else |
37 | | #include <stdlib.h> // posix_memalign |
38 | | #endif |
39 | | #endif |
40 | | |
41 | | namespace rigtorp { |
42 | | namespace mpmc { |
43 | | static constexpr size_t hardwareInterferenceSize = 64; |
44 | | |
45 | | #if defined(__cpp_aligned_new) |
46 | | template <typename T> using AlignedAllocator = std::allocator<T>; |
47 | | #else |
48 | | template <typename T> struct AlignedAllocator { |
49 | | using value_type = T; |
50 | | |
51 | | T *allocate(std::size_t n) { |
52 | | if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) { |
53 | | throw std::bad_array_new_length(); |
54 | | } |
55 | | #ifdef _WIN32 |
56 | | auto *p = static_cast<T *>(_aligned_malloc(sizeof(T) * n, alignof(T))); |
57 | | if (p == nullptr) { |
58 | | throw std::bad_alloc(); |
59 | | } |
60 | | #else |
61 | | T *p; |
62 | | if (posix_memalign(reinterpret_cast<void **>(&p), alignof(T), |
63 | | sizeof(T) * n) != 0) { |
64 | | throw std::bad_alloc(); |
65 | | } |
66 | | #endif |
67 | | return p; |
68 | | } |
69 | | |
70 | | void deallocate(T *p, std::size_t) { |
71 | | #ifdef _WIN32 |
72 | | _aligned_free(p); |
73 | | #else |
74 | | free(p); |
75 | | #endif |
76 | | } |
77 | | }; |
78 | | #endif |
79 | | |
80 | | template <typename T> struct Slot { |
81 | 163k | ~Slot() noexcept { |
82 | 163k | if (turn & 1) { |
83 | 81.9k | destroy(); |
84 | 81.9k | } |
85 | 163k | } rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>::~Slot() Line | Count | Source | 81 | 81.9k | ~Slot() noexcept { | 82 | 81.9k | if (turn & 1) { | 83 | 65.5k | destroy(); | 84 | 65.5k | } | 85 | 81.9k | } |
rigtorp::mpmc::Slot<ocudulog::detail::log_entry>::~Slot() Line | Count | Source | 81 | 81.9k | ~Slot() noexcept { | 82 | 81.9k | if (turn & 1) { | 83 | 16.3k | destroy(); | 84 | 16.3k | } | 85 | 81.9k | } |
Unexecuted instantiation: rigtorp::mpmc::Slot<unsigned int>::~Slot() Unexecuted instantiation: rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> >::~Slot() |
86 | | |
87 | 98.3k | template <typename... Args> void construct(Args &&...args) noexcept { |
88 | 98.3k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, |
89 | 98.3k | "T must be nothrow constructible with Args&&..."); |
90 | 98.3k | new (&storage) T(std::forward<Args>(args)...); |
91 | 98.3k | } void rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>::construct<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&&) Line | Count | Source | 87 | 81.9k | template <typename... Args> void construct(Args &&...args) noexcept { | 88 | 81.9k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, | 89 | 81.9k | "T must be nothrow constructible with Args&&..."); | 90 | 81.9k | new (&storage) T(std::forward<Args>(args)...); | 91 | 81.9k | } |
void rigtorp::mpmc::Slot<ocudulog::detail::log_entry>::construct<ocudulog::detail::log_entry>(ocudulog::detail::log_entry&&) Line | Count | Source | 87 | 16.3k | template <typename... Args> void construct(Args &&...args) noexcept { | 88 | 16.3k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, | 89 | 16.3k | "T must be nothrow constructible with Args&&..."); | 90 | 16.3k | new (&storage) T(std::forward<Args>(args)...); | 91 | 16.3k | } |
Unexecuted instantiation: void rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>::construct<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&>(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&) Unexecuted instantiation: void rigtorp::mpmc::Slot<unsigned int>::construct<unsigned int const&>(unsigned int const&) Unexecuted instantiation: void rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> >::construct<ocudu::unique_function<void (), 64ul, false> >(ocudu::unique_function<void (), 64ul, false>&&) |
92 | | |
93 | 98.3k | void destroy() noexcept { |
94 | 98.3k | static_assert(std::is_nothrow_destructible<T>::value, |
95 | 98.3k | "T must be nothrow destructible"); |
96 | 98.3k | std::launder(reinterpret_cast<T *>(&storage))->~T(); |
97 | 98.3k | } rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>::destroy() Line | Count | Source | 93 | 81.9k | void destroy() noexcept { | 94 | 81.9k | static_assert(std::is_nothrow_destructible<T>::value, | 95 | 81.9k | "T must be nothrow destructible"); | 96 | 81.9k | std::launder(reinterpret_cast<T *>(&storage))->~T(); | 97 | 81.9k | } |
rigtorp::mpmc::Slot<ocudulog::detail::log_entry>::destroy() Line | Count | Source | 93 | 16.3k | void destroy() noexcept { | 94 | 16.3k | static_assert(std::is_nothrow_destructible<T>::value, | 95 | 16.3k | "T must be nothrow destructible"); | 96 | 16.3k | std::launder(reinterpret_cast<T *>(&storage))->~T(); | 97 | 16.3k | } |
Unexecuted instantiation: rigtorp::mpmc::Slot<unsigned int>::destroy() Unexecuted instantiation: rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> >::destroy() |
98 | | |
99 | 16.3k | T &&move() noexcept { |
100 | 16.3k | return std::move(*std::launder(reinterpret_cast<T *>(&storage))); |
101 | 16.3k | } rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>::move() Line | Count | Source | 99 | 16.3k | T &&move() noexcept { | 100 | 16.3k | return std::move(*std::launder(reinterpret_cast<T *>(&storage))); | 101 | 16.3k | } |
Unexecuted instantiation: rigtorp::mpmc::Slot<ocudulog::detail::log_entry>::move() Unexecuted instantiation: rigtorp::mpmc::Slot<unsigned int>::move() Unexecuted instantiation: rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> >::move() |
102 | | |
103 | | // Align to avoid false sharing between adjacent slots |
104 | | alignas(hardwareInterferenceSize) std::atomic<size_t> turn = {0}; |
105 | | typename std::aligned_storage<sizeof(T), alignof(T)>::type storage; |
106 | | }; |
107 | | |
108 | | template <typename T, typename Allocator = AlignedAllocator<Slot<T>>> |
109 | | class Queue { |
110 | | private: |
111 | | static_assert(std::is_nothrow_copy_assignable<T>::value || |
112 | | std::is_nothrow_move_assignable<T>::value, |
113 | | "T must be nothrow copy or move assignable"); |
114 | | |
115 | | static_assert(std::is_nothrow_destructible<T>::value, |
116 | | "T must be nothrow destructible"); |
117 | | |
118 | | public: |
119 | | explicit Queue(const size_t capacity, |
120 | | const Allocator &allocator = Allocator()) |
121 | 20 | : capacity_(capacity), allocator_(allocator), head_(0), tail_(0) { |
122 | 20 | if (capacity_ < 1) { |
123 | 0 | throw std::invalid_argument("capacity < 1"); |
124 | 0 | } |
125 | | // Allocate one extra slot to prevent false sharing on the last slot |
126 | 20 | slots_ = allocator_.allocate(capacity_ + 1); |
127 | | // Allocators are not required to honor alignment for over-aligned types |
128 | | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify |
129 | | // alignment here |
130 | 20 | if (reinterpret_cast<size_t>(slots_) % alignof(Slot<T>) != 0) { |
131 | 0 | allocator_.deallocate(slots_, capacity_ + 1); |
132 | 0 | throw std::bad_alloc(); |
133 | 0 | } |
134 | 163k | for (size_t i = 0; i < capacity_; ++i) { |
135 | 163k | new (&slots_[i]) Slot<T>(); |
136 | 163k | } |
137 | 20 | static_assert( |
138 | 20 | alignof(Slot<T>) == hardwareInterferenceSize, |
139 | 20 | "Slot must be aligned to cache line boundary to prevent false sharing"); |
140 | 20 | static_assert(sizeof(Slot<T>) % hardwareInterferenceSize == 0, |
141 | 20 | "Slot size must be a multiple of cache line size to prevent " |
142 | 20 | "false sharing between adjacent slots"); |
143 | 20 | static_assert(sizeof(Queue) % hardwareInterferenceSize == 0, |
144 | 20 | "Queue size must be a multiple of cache line size to " |
145 | 20 | "prevent false sharing between adjacent queues"); |
146 | 20 | static_assert( |
147 | 20 | offsetof(Queue, tail_) - offsetof(Queue, head_) == |
148 | 20 | static_cast<std::ptrdiff_t>(hardwareInterferenceSize), |
149 | 20 | "head and tail must be a cache line apart to prevent false sharing"); |
150 | 20 | } rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::Queue(unsigned long, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > const&) Line | Count | Source | 121 | 10 | : capacity_(capacity), allocator_(allocator), head_(0), tail_(0) { | 122 | 10 | if (capacity_ < 1) { | 123 | 0 | throw std::invalid_argument("capacity < 1"); | 124 | 0 | } | 125 | | // Allocate one extra slot to prevent false sharing on the last slot | 126 | 10 | slots_ = allocator_.allocate(capacity_ + 1); | 127 | | // Allocators are not required to honor alignment for over-aligned types | 128 | | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify | 129 | | // alignment here | 130 | 10 | if (reinterpret_cast<size_t>(slots_) % alignof(Slot<T>) != 0) { | 131 | 0 | allocator_.deallocate(slots_, capacity_ + 1); | 132 | 0 | throw std::bad_alloc(); | 133 | 0 | } | 134 | 81.9k | for (size_t i = 0; i < capacity_; ++i) { | 135 | 81.9k | new (&slots_[i]) Slot<T>(); | 136 | 81.9k | } | 137 | 10 | static_assert( | 138 | 10 | alignof(Slot<T>) == hardwareInterferenceSize, | 139 | 10 | "Slot must be aligned to cache line boundary to prevent false sharing"); | 140 | 10 | static_assert(sizeof(Slot<T>) % hardwareInterferenceSize == 0, | 141 | 10 | "Slot size must be a multiple of cache line size to prevent " | 142 | 10 | "false sharing between adjacent slots"); | 143 | 10 | static_assert(sizeof(Queue) % hardwareInterferenceSize == 0, | 144 | 10 | "Queue size must be a multiple of cache line size to " | 145 | 10 | "prevent false sharing between adjacent queues"); | 146 | 10 | static_assert( | 147 | 10 | offsetof(Queue, tail_) - offsetof(Queue, head_) == | 148 | 10 | static_cast<std::ptrdiff_t>(hardwareInterferenceSize), | 149 | 10 | "head and tail must be a cache line apart to prevent false sharing"); | 150 | 10 | } |
rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::Queue(unsigned long, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > const&) Line | Count | Source | 121 | 10 | : capacity_(capacity), allocator_(allocator), head_(0), tail_(0) { | 122 | 10 | if (capacity_ < 1) { | 123 | 0 | throw std::invalid_argument("capacity < 1"); | 124 | 0 | } | 125 | | // Allocate one extra slot to prevent false sharing on the last slot | 126 | 10 | slots_ = allocator_.allocate(capacity_ + 1); | 127 | | // Allocators are not required to honor alignment for over-aligned types | 128 | | // (see http://eel.is/c++draft/allocator.requirements#10) so we verify | 129 | | // alignment here | 130 | 10 | if (reinterpret_cast<size_t>(slots_) % alignof(Slot<T>) != 0) { | 131 | 0 | allocator_.deallocate(slots_, capacity_ + 1); | 132 | 0 | throw std::bad_alloc(); | 133 | 0 | } | 134 | 81.9k | for (size_t i = 0; i < capacity_; ++i) { | 135 | 81.9k | new (&slots_[i]) Slot<T>(); | 136 | 81.9k | } | 137 | 10 | static_assert( | 138 | 10 | alignof(Slot<T>) == hardwareInterferenceSize, | 139 | 10 | "Slot must be aligned to cache line boundary to prevent false sharing"); | 140 | 10 | static_assert(sizeof(Slot<T>) % hardwareInterferenceSize == 0, | 141 | 10 | "Slot size must be a multiple of cache line size to prevent " | 142 | 10 | "false sharing between adjacent slots"); | 143 | 10 | static_assert(sizeof(Queue) % hardwareInterferenceSize == 0, | 144 | 10 | "Queue size must be a multiple of cache line size to " | 145 | 10 | "prevent false sharing between adjacent queues"); | 146 | 10 | static_assert( | 147 | 10 | offsetof(Queue, tail_) - offsetof(Queue, head_) == | 148 | 10 | static_cast<std::ptrdiff_t>(hardwareInterferenceSize), | 149 | 10 | "head and tail must be a cache line apart to prevent false sharing"); | 150 | 10 | } |
Unexecuted instantiation: rigtorp::mpmc::Queue<unsigned int, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > >::Queue(unsigned long, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > const&) |
151 | | |
152 | 20 | ~Queue() noexcept { |
153 | 163k | for (size_t i = 0; i < capacity_; ++i) { |
154 | 163k | slots_[i].~Slot(); |
155 | 163k | } |
156 | 20 | allocator_.deallocate(slots_, capacity_ + 1); |
157 | 20 | } rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::~Queue() Line | Count | Source | 152 | 10 | ~Queue() noexcept { | 153 | 81.9k | for (size_t i = 0; i < capacity_; ++i) { | 154 | 81.9k | slots_[i].~Slot(); | 155 | 81.9k | } | 156 | 10 | allocator_.deallocate(slots_, capacity_ + 1); | 157 | 10 | } |
rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::~Queue() Line | Count | Source | 152 | 10 | ~Queue() noexcept { | 153 | 81.9k | for (size_t i = 0; i < capacity_; ++i) { | 154 | 81.9k | slots_[i].~Slot(); | 155 | 81.9k | } | 156 | 10 | allocator_.deallocate(slots_, capacity_ + 1); | 157 | 10 | } |
Unexecuted instantiation: rigtorp::mpmc::Queue<unsigned int, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > >::~Queue() Unexecuted instantiation: rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::~Queue() |
158 | | |
159 | | // non-copyable and non-movable |
160 | | Queue(const Queue &) = delete; |
161 | | Queue &operator=(const Queue &) = delete; |
162 | | |
163 | 81.9k | template <typename... Args> void emplace(Args &&...args) noexcept { |
164 | 81.9k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, |
165 | 81.9k | "T must be nothrow constructible with Args&&..."); |
166 | 81.9k | auto const head = head_.fetch_add(1); |
167 | 81.9k | auto &slot = slots_[idx(head)]; |
168 | 81.9k | while (turn(head) * 2 != slot.turn.load(std::memory_order_acquire)) |
169 | 0 | ; |
170 | 81.9k | slot.construct(std::forward<Args>(args)...); |
171 | 81.9k | slot.turn.store(turn(head) * 2 + 1, std::memory_order_release); |
172 | 81.9k | } void rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::emplace<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*>(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&&) Line | Count | Source | 163 | 81.9k | template <typename... Args> void emplace(Args &&...args) noexcept { | 164 | 81.9k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, | 165 | 81.9k | "T must be nothrow constructible with Args&&..."); | 166 | 81.9k | auto const head = head_.fetch_add(1); | 167 | 81.9k | auto &slot = slots_[idx(head)]; | 168 | 81.9k | while (turn(head) * 2 != slot.turn.load(std::memory_order_acquire)) | 169 | 0 | ; | 170 | 81.9k | slot.construct(std::forward<Args>(args)...); | 171 | 81.9k | slot.turn.store(turn(head) * 2 + 1, std::memory_order_release); | 172 | 81.9k | } |
Unexecuted instantiation: void rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::emplace<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&>(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&) |
173 | | |
174 | 16.3k | template <typename... Args> bool try_emplace(Args &&...args) noexcept { |
175 | 16.3k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, |
176 | 16.3k | "T must be nothrow constructible with Args&&..."); |
177 | 16.3k | auto head = head_.load(std::memory_order_acquire); |
178 | 16.3k | for (;;) { |
179 | 16.3k | auto &slot = slots_[idx(head)]; |
180 | 16.3k | if (turn(head) * 2 == slot.turn.load(std::memory_order_acquire)) { |
181 | 16.3k | if (head_.compare_exchange_strong(head, head + 1)) { |
182 | 16.3k | slot.construct(std::forward<Args>(args)...); |
183 | 16.3k | slot.turn.store(turn(head) * 2 + 1, std::memory_order_release); |
184 | 16.3k | return true; |
185 | 16.3k | } |
186 | 16.3k | } else { |
187 | 0 | auto const prevHead = head; |
188 | 0 | head = head_.load(std::memory_order_acquire); |
189 | 0 | if (head == prevHead) { |
190 | 0 | return false; |
191 | 0 | } |
192 | 0 | } |
193 | 16.3k | } |
194 | 16.3k | } bool rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::try_emplace<ocudulog::detail::log_entry>(ocudulog::detail::log_entry&&) Line | Count | Source | 174 | 16.3k | template <typename... Args> bool try_emplace(Args &&...args) noexcept { | 175 | 16.3k | static_assert(std::is_nothrow_constructible<T, Args &&...>::value, | 176 | 16.3k | "T must be nothrow constructible with Args&&..."); | 177 | 16.3k | auto head = head_.load(std::memory_order_acquire); | 178 | 16.3k | for (;;) { | 179 | 16.3k | auto &slot = slots_[idx(head)]; | 180 | 16.3k | if (turn(head) * 2 == slot.turn.load(std::memory_order_acquire)) { | 181 | 16.3k | if (head_.compare_exchange_strong(head, head + 1)) { | 182 | 16.3k | slot.construct(std::forward<Args>(args)...); | 183 | 16.3k | slot.turn.store(turn(head) * 2 + 1, std::memory_order_release); | 184 | 16.3k | return true; | 185 | 16.3k | } | 186 | 16.3k | } else { | 187 | 0 | auto const prevHead = head; | 188 | 0 | head = head_.load(std::memory_order_acquire); | 189 | 0 | if (head == prevHead) { | 190 | 0 | return false; | 191 | 0 | } | 192 | 0 | } | 193 | 16.3k | } | 194 | 16.3k | } |
Unexecuted instantiation: bool rigtorp::mpmc::Queue<unsigned int, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > >::try_emplace<unsigned int const&>(unsigned int const&) Unexecuted instantiation: bool rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::try_emplace<ocudu::unique_function<void (), 64ul, false> >(ocudu::unique_function<void (), 64ul, false>&&) |
195 | | |
196 | | void push(const T &v) noexcept { |
197 | | static_assert(std::is_nothrow_copy_constructible<T>::value, |
198 | | "T must be nothrow copy constructible"); |
199 | | emplace(v); |
200 | | } |
201 | | |
202 | | template <typename P, |
203 | | typename = typename std::enable_if< |
204 | | std::is_nothrow_constructible<T, P &&>::value>::type> |
205 | 81.9k | void push(P &&v) noexcept { |
206 | 81.9k | emplace(std::forward<P>(v)); |
207 | 81.9k | } void rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::push<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, void>(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&&) Line | Count | Source | 205 | 81.9k | void push(P &&v) noexcept { | 206 | 81.9k | emplace(std::forward<P>(v)); | 207 | 81.9k | } |
Unexecuted instantiation: void rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::push<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&, void>(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&) |
208 | | |
209 | 0 | bool try_push(const T &v) noexcept { |
210 | 0 | static_assert(std::is_nothrow_copy_constructible<T>::value, |
211 | 0 | "T must be nothrow copy constructible"); |
212 | 0 | return try_emplace(v); |
213 | 0 | } |
214 | | |
215 | | template <typename P, |
216 | | typename = typename std::enable_if< |
217 | | std::is_nothrow_constructible<T, P &&>::value>::type> |
218 | 16.3k | bool try_push(P &&v) noexcept { |
219 | 16.3k | return try_emplace(std::forward<P>(v)); |
220 | 16.3k | } bool rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::try_push<ocudulog::detail::log_entry, void>(ocudulog::detail::log_entry&&) Line | Count | Source | 218 | 16.3k | bool try_push(P &&v) noexcept { | 219 | 16.3k | return try_emplace(std::forward<P>(v)); | 220 | 16.3k | } |
Unexecuted instantiation: bool rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::try_push<ocudu::unique_function<void (), 64ul, false>, void>(ocudu::unique_function<void (), 64ul, false>&&) |
221 | | |
222 | | void pop(T &v) noexcept { |
223 | | auto const tail = tail_.fetch_add(1); |
224 | | auto &slot = slots_[idx(tail)]; |
225 | | while (turn(tail) * 2 + 1 != slot.turn.load(std::memory_order_acquire)) |
226 | | ; |
227 | | v = slot.move(); |
228 | | slot.destroy(); |
229 | | slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release); |
230 | | } |
231 | | |
232 | 16.3k | bool try_pop(T &v) noexcept { |
233 | 16.3k | auto tail = tail_.load(std::memory_order_acquire); |
234 | 16.3k | for (;;) { |
235 | 16.3k | auto &slot = slots_[idx(tail)]; |
236 | 16.3k | if (turn(tail) * 2 + 1 == slot.turn.load(std::memory_order_acquire)) { |
237 | 16.3k | if (tail_.compare_exchange_strong(tail, tail + 1)) { |
238 | 16.3k | v = slot.move(); |
239 | 16.3k | slot.destroy(); |
240 | 16.3k | slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release); |
241 | 16.3k | return true; |
242 | 16.3k | } |
243 | 16.3k | } else { |
244 | 0 | auto const prevTail = tail; |
245 | 0 | tail = tail_.load(std::memory_order_acquire); |
246 | 0 | if (tail == prevTail) { |
247 | 0 | return false; |
248 | 0 | } |
249 | 0 | } |
250 | 16.3k | } |
251 | 16.3k | } rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::try_pop(fmt::v11::dynamic_format_arg_store<fmt::v11::context>*&) Line | Count | Source | 232 | 16.3k | bool try_pop(T &v) noexcept { | 233 | 16.3k | auto tail = tail_.load(std::memory_order_acquire); | 234 | 16.3k | for (;;) { | 235 | 16.3k | auto &slot = slots_[idx(tail)]; | 236 | 16.3k | if (turn(tail) * 2 + 1 == slot.turn.load(std::memory_order_acquire)) { | 237 | 16.3k | if (tail_.compare_exchange_strong(tail, tail + 1)) { | 238 | 16.3k | v = slot.move(); | 239 | 16.3k | slot.destroy(); | 240 | 16.3k | slot.turn.store(turn(tail) * 2 + 2, std::memory_order_release); | 241 | 16.3k | return true; | 242 | 16.3k | } | 243 | 16.3k | } else { | 244 | 0 | auto const prevTail = tail; | 245 | 0 | tail = tail_.load(std::memory_order_acquire); | 246 | 0 | if (tail == prevTail) { | 247 | 0 | return false; | 248 | 0 | } | 249 | 0 | } | 250 | 16.3k | } | 251 | 16.3k | } |
Unexecuted instantiation: rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::try_pop(ocudulog::detail::log_entry&) Unexecuted instantiation: rigtorp::mpmc::Queue<unsigned int, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > >::try_pop(unsigned int&) Unexecuted instantiation: rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::try_pop(ocudu::unique_function<void (), 64ul, false>&) |
252 | | |
253 | | /// Returns the number of elements in the queue. |
254 | | /// The size can be negative when the queue is empty and there is at least one |
255 | | /// reader waiting. Since this is a concurrent queue the size is only a best |
256 | | /// effort guess until all reader and writer threads have been joined. |
257 | 431k | ptrdiff_t size() const noexcept { |
258 | | // TODO: How can we deal with wrapped queue on 32bit? |
259 | 431k | return static_cast<ptrdiff_t>(head_.load(std::memory_order_relaxed) - |
260 | 431k | tail_.load(std::memory_order_relaxed)); |
261 | 431k | } rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::size() const Line | Count | Source | 257 | 431k | ptrdiff_t size() const noexcept { | 258 | | // TODO: How can we deal with wrapped queue on 32bit? | 259 | 431k | return static_cast<ptrdiff_t>(head_.load(std::memory_order_relaxed) - | 260 | 431k | tail_.load(std::memory_order_relaxed)); | 261 | 431k | } |
Unexecuted instantiation: rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::size() const Unexecuted instantiation: rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::size() const |
262 | | |
263 | | /// Returns true if the queue is empty. |
264 | | /// Since this is a concurrent queue this is only a best effort guess |
265 | | /// until all reader and writer threads have been joined. |
266 | 431k | bool empty() const noexcept { return size() <= 0; } |
267 | | |
268 | 0 | size_t capacity() const noexcept { return capacity_; } |
269 | | |
270 | | private: |
271 | 114k | constexpr size_t idx(size_t i) const noexcept { return i % capacity_; }rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::idx(unsigned long) const Line | Count | Source | 271 | 98.3k | constexpr size_t idx(size_t i) const noexcept { return i % capacity_; } |
rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::idx(unsigned long) const Line | Count | Source | 271 | 16.3k | constexpr size_t idx(size_t i) const noexcept { return i % capacity_; } |
Unexecuted instantiation: rigtorp::mpmc::Queue<unsigned int, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > >::idx(unsigned long) const Unexecuted instantiation: rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::idx(unsigned long) const |
272 | | |
273 | 229k | constexpr size_t turn(size_t i) const noexcept { return i / capacity_; }rigtorp::mpmc::Queue<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*, std::__1::allocator<rigtorp::mpmc::Slot<fmt::v11::dynamic_format_arg_store<fmt::v11::context>*> > >::turn(unsigned long) const Line | Count | Source | 273 | 196k | constexpr size_t turn(size_t i) const noexcept { return i / capacity_; } |
rigtorp::mpmc::Queue<ocudulog::detail::log_entry, std::__1::allocator<rigtorp::mpmc::Slot<ocudulog::detail::log_entry> > >::turn(unsigned long) const Line | Count | Source | 273 | 32.7k | constexpr size_t turn(size_t i) const noexcept { return i / capacity_; } |
Unexecuted instantiation: rigtorp::mpmc::Queue<unsigned int, std::__1::allocator<rigtorp::mpmc::Slot<unsigned int> > >::turn(unsigned long) const Unexecuted instantiation: rigtorp::mpmc::Queue<ocudu::unique_function<void (), 64ul, false>, std::__1::allocator<rigtorp::mpmc::Slot<ocudu::unique_function<void (), 64ul, false> > > >::turn(unsigned long) const |
274 | | |
275 | | private: |
276 | | const size_t capacity_; |
277 | | Slot<T> *slots_; |
278 | | #if defined(__has_cpp_attribute) && __has_cpp_attribute(no_unique_address) |
279 | | Allocator allocator_ [[no_unique_address]]; |
280 | | #else |
281 | | Allocator allocator_; |
282 | | #endif |
283 | | |
284 | | // Align to avoid false sharing between head_ and tail_ |
285 | | alignas(hardwareInterferenceSize) std::atomic<size_t> head_; |
286 | | alignas(hardwareInterferenceSize) std::atomic<size_t> tail_; |
287 | | }; |
288 | | } // namespace mpmc |
289 | | |
290 | | template <typename T, |
291 | | typename Allocator = mpmc::AlignedAllocator<mpmc::Slot<T>>> |
292 | | using MPMCQueue = mpmc::Queue<T, Allocator>; |
293 | | |
294 | | } // namespace rigtorp |