Condy v1.8
C++ Asynchronous System Call Layer for Linux
Loading...
Searching...
No Matches
utils.hpp
Go to the documentation of this file.
1
5
6#pragma once
7
8#include <cassert>
9#include <cerrno>
10#include <cstddef>
11#include <cstdint>
12#include <cstdlib>
13#include <cstring>
14#include <exception>
15#include <format>
16#include <iostream>
17#include <limits>
18#include <new>
19#include <stack>
20#include <stdexcept>
21#include <string_view>
22#include <system_error>
23#include <tuple>
24#include <type_traits>
25#include <utility>
26#include <variant>
27
28// NOLINTBEGIN(bugprone-macro-parentheses)
29#define CONDY_DELETE_COPY(cls) \
30 cls(const cls &) = delete; \
31 cls &operator=(const cls &) = delete
32
33#define CONDY_DELETE_MOVE(cls) \
34 cls(cls &&) = delete; \
35 cls &operator=(cls &&) = delete
36// NOLINTEND(bugprone-macro-parentheses)
37
38#define CONDY_DELETE_COPY_MOVE(cls) \
39 CONDY_DELETE_COPY(cls); \
40 CONDY_DELETE_MOVE(cls)
41
42#if defined(__has_feature)
43#if __has_feature(thread_sanitizer)
44#define CONDY_DETAIL_HAS_TSAN
45#endif
46#endif
47
48#if defined(__SANITIZE_THREAD__)
49#define CONDY_DETAIL_HAS_TSAN
50#endif
51
52#if defined(CONDY_DETAIL_HAS_TSAN)
53extern "C" {
54void __tsan_acquire(void *addr); // NOLINT(bugprone-reserved-identifier)
55void __tsan_release(void *addr); // NOLINT(bugprone-reserved-identifier)
56}
57#endif
58
59namespace condy {
60namespace detail {
61
62inline void tsan_acquire([[maybe_unused]] void *addr) noexcept {
63#if defined(CONDY_DETAIL_HAS_TSAN)
64 __tsan_acquire(addr);
65#endif
66}
67
68inline void tsan_release([[maybe_unused]] void *addr) noexcept {
69#if defined(CONDY_DETAIL_HAS_TSAN)
70 __tsan_release(addr);
71#endif
72}
73
74template <typename Func> class [[nodiscard]] Defer {
75public:
76 Defer(Func func) : func_(std::move(func)) {}
77 ~Defer() {
78 if (active_)
79 func_();
80 }
81
82 CONDY_DELETE_COPY_MOVE(Defer);
83
84public:
85 void dismiss() noexcept { active_ = false; }
86
87private:
88 Func func_;
89 bool active_ = true;
90};
91
92template <typename Func> auto defer(Func &&func) {
93 return Defer<std::decay_t<Func>>(std::forward<Func>(func));
94}
95
96template <typename T, T From = 0, T To = std::numeric_limits<T>::max()>
97class IdPool {
98public:
99 static_assert(From < To, "Invalid ID range");
100
101 T allocate() {
102 if (!recycled_ids_.empty()) {
103 T id = recycled_ids_.top();
104 recycled_ids_.pop();
105 return id;
106 }
107 if (next_id_ < To) {
108 return next_id_++;
109 }
110 throw std::runtime_error("ID pool exhausted");
111 }
112
113 void recycle(T id) noexcept {
114 assert(From <= id && id < next_id_ && id < To);
115 recycled_ids_.push(id);
116 }
117
118 void reset() noexcept {
119 next_id_ = From;
120 while (!recycled_ids_.empty()) {
121 recycled_ids_.pop();
122 }
123 }
124
125private:
126 T next_id_ = From;
127 std::stack<T> recycled_ids_;
128};
129
130[[noreturn]] inline void panic_on(std::string_view msg) noexcept {
131 std::cerr << std::format("Panic: {}\n", msg);
132#ifndef CRASH_TEST
133 std::terminate();
134#else
135 // Ctest cannot handle SIGABRT, so we use exit here
136 std::exit(EXIT_FAILURE);
137#endif
138}
139
140template <typename T> class RawStorage {
141public:
142 template <typename Factory>
143 void accept(Factory &&factory) noexcept(
144 noexcept(T(std::forward<Factory>(factory)()))) {
145 new (&storage_) T(std::forward<Factory>(factory)());
146 }
147
148 template <typename... Args>
149 void construct(Args &&...args) noexcept(
150 std::is_nothrow_constructible_v<T, Args...>) {
151 accept([&]() { return T(std::forward<Args>(args)...); });
152 }
153
154 T &get() noexcept { return *std::launder(reinterpret_cast<T *>(storage_)); }
155
156 const T &get() const noexcept {
157 return *std::launder(reinterpret_cast<const T *>(storage_));
158 }
159
160 void destroy() noexcept { get().~T(); }
161
162private:
163 alignas(T) unsigned char storage_[sizeof(T)];
164};
165
166template <typename T, size_t N> class SmallArray {
167public:
168 SmallArray(size_t capacity) : capacity_(capacity) {
169 if (!is_small_()) {
170 large_ = new T[capacity];
171 }
172 }
173
174 ~SmallArray() {
175 if (!is_small_()) {
176 delete[] large_;
177 }
178 }
179
180 T &operator[](size_t index) noexcept {
181 return is_small_() ? small_[index] : large_[index];
182 }
183
184 const T &operator[](size_t index) const noexcept {
185 return is_small_() ? small_[index] : large_[index];
186 }
187
188 size_t capacity() const noexcept { return capacity_; }
189
190private:
191 bool is_small_() const noexcept { return capacity_ <= N; }
192
193private:
194 size_t capacity_;
195 union {
196 T small_[N];
197 T *large_;
198 };
199};
200
201inline auto make_system_error(std::string_view msg, int ec) {
202 return std::system_error(ec, std::generic_category(), std::string(msg));
203}
204
205inline auto make_system_error(std::string_view msg) {
206 return make_system_error(msg, errno);
207}
208
209#if __cplusplus >= 202302L
210[[noreturn]] inline void unreachable() { std::unreachable(); }
211#else
212[[noreturn]] inline void unreachable() { __builtin_unreachable(); }
213#endif
214
215template <size_t Idx = 0, typename... Ts>
216std::variant<Ts...> tuple_at(std::tuple<Ts...> &results, size_t idx) {
217 if constexpr (Idx < sizeof...(Ts)) {
218 if (idx == Idx) {
219 return std::variant<Ts...>{std::in_place_index<Idx>,
220 std::move(std::get<Idx>(results))};
221 } else {
222 return tuple_at<Idx + 1, Ts...>(results, idx);
223 }
224 } else {
225#ifdef __clang__
226 // Should not reach here, but clang can misoptimize this path if we
227 // mark it as unreachable. Confirmed fixed in clang 20.1.8, but the
228 // exact cause was not investigated.
229 assert(false && "Index out of bounds");
230 return std::variant<Ts...>{std::in_place_index<0>,
231 std::move(std::get<0>(results))};
232#else
233 panic_on("Index out of bounds in tuple_at");
234#endif
235 }
236}
237
238template <typename T> inline T align_up(T value, T alignment) noexcept {
239 // alignment must be a power of two
240 assert(alignment > 0 && (alignment & (alignment - 1)) == 0);
241 return (value + alignment - 1) & ~(alignment - 1);
242}
243
244} // namespace detail
245} // namespace condy
246
247#undef CONDY_DETAIL_HAS_TSAN
The main namespace for the Condy library.
Definition condy.hpp:37