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