Condy v1.7.0
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 {
60
61inline void tsan_acquire([[maybe_unused]] void *addr) noexcept {
62#if defined(CONDY_DETAIL_HAS_TSAN)
63 __tsan_acquire(addr);
64#endif
65}
66
67inline void tsan_release([[maybe_unused]] void *addr) noexcept {
68#if defined(CONDY_DETAIL_HAS_TSAN)
69 __tsan_release(addr);
70#endif
71}
72
73} // namespace condy
74
75#undef CONDY_DETAIL_HAS_TSAN
76
77namespace condy {
78
79template <typename Func> class [[nodiscard]] Defer {
80public:
81 Defer(Func func) : func_(std::move(func)) {}
82 ~Defer() {
83 if (active_)
84 func_();
85 }
86
87 CONDY_DELETE_COPY_MOVE(Defer);
88
89public:
90 void dismiss() noexcept { active_ = false; }
91
92private:
93 Func func_;
94 bool active_ = true;
95};
96
103template <typename Func> auto defer(Func &&func) {
104 return Defer<std::decay_t<Func>>(std::forward<Func>(func));
105}
106
107[[noreturn]] inline void panic_on(std::string_view msg) noexcept {
108 std::cerr << std::format("Panic: {}\n", msg);
109#ifndef CRASH_TEST
110 std::terminate();
111#else
112 // Ctest cannot handle SIGABRT, so we use exit here
113 std::exit(EXIT_FAILURE);
114#endif
115}
116
117template <typename T> class RawStorage {
118public:
119 template <typename Factory>
120 void accept(Factory &&factory) noexcept(
121 noexcept(T(std::forward<Factory>(factory)()))) {
122 new (&storage_) T(std::forward<Factory>(factory)());
123 }
124
125 template <typename... Args>
126 void construct(Args &&...args) noexcept(
127 std::is_nothrow_constructible_v<T, Args...>) {
128 accept([&]() { return T(std::forward<Args>(args)...); });
129 }
130
131 T &get() noexcept { return *std::launder(reinterpret_cast<T *>(storage_)); }
132
133 const T &get() const noexcept {
134 return *std::launder(reinterpret_cast<const T *>(storage_));
135 }
136
137 void destroy() noexcept { get().~T(); }
138
139private:
140 alignas(T) unsigned char storage_[sizeof(T)];
141};
142
143template <typename T, size_t N> class SmallArray {
144public:
145 SmallArray(size_t capacity) : capacity_(capacity) {
146 if (!is_small_()) {
147 large_ = new T[capacity];
148 }
149 }
150
151 ~SmallArray() {
152 if (!is_small_()) {
153 delete[] large_;
154 }
155 }
156
157 T &operator[](size_t index) noexcept {
158 return is_small_() ? small_[index] : large_[index];
159 }
160
161 const T &operator[](size_t index) const noexcept {
162 return is_small_() ? small_[index] : large_[index];
163 }
164
165 size_t capacity() const noexcept { return capacity_; }
166
167private:
168 bool is_small_() const noexcept { return capacity_ <= N; }
169
170private:
171 size_t capacity_;
172 union {
173 T small_[N];
174 T *large_;
175 };
176};
177
178inline auto make_system_error(std::string_view msg, int ec) {
179 return std::system_error(ec, std::generic_category(), std::string(msg));
180}
181
182inline auto make_system_error(std::string_view msg) {
183 return make_system_error(msg, errno);
184}
185
186template <typename M, typename T>
187constexpr ptrdiff_t offset_of(M T::*member) noexcept {
188 constexpr T *dummy = nullptr;
189 return reinterpret_cast<ptrdiff_t>(&(dummy->*member));
190}
191
192template <typename M, typename T>
193T *container_of(M T::*member, M *ptr) noexcept {
194 auto offset = offset_of(member);
195 // NOLINTNEXTLINE(performance-no-int-to-ptr)
196 return reinterpret_cast<T *>(reinterpret_cast<uintptr_t>(ptr) - offset);
197}
198
199template <typename T, T From = 0, T To = std::numeric_limits<T>::max()>
200class IdPool {
201public:
202 static_assert(From < To, "Invalid ID range");
203
204 T allocate() {
205 if (!recycled_ids_.empty()) {
206 T id = recycled_ids_.top();
207 recycled_ids_.pop();
208 return id;
209 }
210 if (next_id_ < To) {
211 return next_id_++;
212 }
213 throw std::runtime_error("ID pool exhausted");
214 }
215
216 void recycle(T id) noexcept {
217 assert(From <= id && id < next_id_ && id < To);
218 recycled_ids_.push(id);
219 }
220
221 void reset() noexcept {
222 next_id_ = From;
223 while (!recycled_ids_.empty()) {
224 recycled_ids_.pop();
225 }
226 }
227
228private:
229 T next_id_ = From;
230 std::stack<T> recycled_ids_;
231};
232
233#if __cplusplus >= 202302L
234[[noreturn]] inline void unreachable() { std::unreachable(); }
235#else
236[[noreturn]] inline void unreachable() { __builtin_unreachable(); }
237#endif
238
239template <size_t Idx = 0, typename... Ts>
240std::variant<Ts...> tuple_at(std::tuple<Ts...> &results, size_t idx) {
241 if constexpr (Idx < sizeof...(Ts)) {
242 if (idx == Idx) {
243 return std::variant<Ts...>{std::in_place_index<Idx>,
244 std::move(std::get<Idx>(results))};
245 } else {
246 return tuple_at<Idx + 1, Ts...>(results, idx);
247 }
248 } else {
249#ifdef __clang__
250 // Should not reach here, but clang can misoptimize this path if we
251 // mark it as unreachable. Confirmed fixed in clang 20.1.8, but the
252 // exact cause was not investigated.
253 assert(false && "Index out of bounds");
254 return std::variant<Ts...>{std::in_place_index<0>,
255 std::move(std::get<0>(results))};
256#else
257 panic_on("Index out of bounds in tuple_at");
258#endif
259 }
260}
261
262template <typename T> inline T align_up(T value, T alignment) noexcept {
263 // alignment must be a power of two
264 assert(alignment > 0 && (alignment & (alignment - 1)) == 0);
265 return (value + alignment - 1) & ~(alignment - 1);
266}
267
268} // namespace condy
The main namespace for the Condy library.
Definition condy.hpp:31
auto defer(Func &&func)
Defer the execution of a function until the current scope ends.
Definition utils.hpp:103