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