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 <atomic>
9#include <cassert>
10#include <cerrno>
11#include <cstddef>
12#include <cstdint>
13#include <cstdlib>
14#include <cstring>
15#include <exception>
16#include <format>
17#include <iostream>
18#include <limits>
19#include <stack>
20#include <stdexcept>
21#include <string_view>
22#include <system_error>
23#include <type_traits>
24#include <utility>
25
26#if defined(__has_feature)
27#if __has_feature(thread_sanitizer)
28#include <sanitizer/tsan_interface.h>
29#define tsan_acquire(addr) __tsan_acquire(addr)
30#define tsan_release(addr) __tsan_release(addr)
31#else
32#define tsan_acquire(addr) static_cast<void>(0)
33#define tsan_release(addr) static_cast<void>(0)
34#endif
35#else
36#define tsan_acquire(addr) static_cast<void>(0)
37#define tsan_release(addr) static_cast<void>(0)
38#endif
39
40namespace condy {
41
42template <typename Func> class [[nodiscard]] Defer {
43public:
44 Defer(Func func) : func_(std::move(func)) {}
45 ~Defer() {
46 if (active_)
47 func_();
48 }
49
50 Defer(const Defer &) = delete;
51 Defer &operator=(const Defer &) = delete;
52 Defer(Defer &&) = delete;
53 Defer &operator=(Defer &&) = delete;
54
55public:
56 void dismiss() noexcept { active_ = false; }
57
58private:
59 Func func_;
60 bool active_ = true;
61};
62
69template <typename Func> auto defer(Func &&func) {
70 return Defer<std::decay_t<Func>>(std::forward<Func>(func));
71}
72
73[[noreturn]] inline void panic_on(std::string_view msg) noexcept {
74 std::cerr << std::format("Panic: {}\n", msg);
75#ifndef CRASH_TEST
76 std::terminate();
77#else
78 // Ctest cannot handle SIGABRT, so we use exit here
79 std::exit(EXIT_FAILURE);
80#endif
81}
82
83template <typename T> class RawStorage {
84public:
85 template <typename... Args>
86 void construct(Args &&...args) noexcept(
87 std::is_nothrow_constructible_v<T, Args...>) {
88 new (&storage_) T(std::forward<Args>(args)...);
89 }
90
91 T &get() noexcept { return *reinterpret_cast<T *>(&storage_); }
92
93 const T &get() const noexcept {
94 return *reinterpret_cast<const T *>(&storage_);
95 }
96
97 void destroy() noexcept { get().~T(); }
98
99private:
100 alignas(T) unsigned char storage_[sizeof(T)];
101};
102
103template <typename T, size_t N> class SmallArray {
104public:
105 SmallArray(size_t capacity) : capacity_(capacity) {
106 if (!is_small_()) {
107 large_ = new T[capacity];
108 }
109 }
110
111 ~SmallArray() {
112 if (!is_small_()) {
113 delete[] large_;
114 }
115 }
116
117 T &operator[](size_t index) noexcept {
118 return is_small_() ? small_[index] : large_[index];
119 }
120
121 const T &operator[](size_t index) const noexcept {
122 return is_small_() ? small_[index] : large_[index];
123 }
124
125 size_t capacity() const noexcept { return capacity_; }
126
127private:
128 bool is_small_() const noexcept { return capacity_ <= N; }
129
130private:
131 size_t capacity_;
132 union {
133 T small_[N];
134 T *large_;
135 };
136};
137
138inline auto make_system_error(std::string_view msg, int ec) {
139 return std::system_error(ec, std::generic_category(), std::string(msg));
140}
141
142inline auto make_system_error(std::string_view msg) {
143 return make_system_error(msg, errno);
144}
145
146template <typename M, typename T>
147constexpr ptrdiff_t offset_of(M T::*member) noexcept {
148 constexpr T *dummy = nullptr;
149 return reinterpret_cast<ptrdiff_t>(&(dummy->*member));
150}
151
152template <typename M, typename T>
153T *container_of(M T::*member, M *ptr) noexcept {
154 auto offset = offset_of(member);
155 // NOLINTNEXTLINE(performance-no-int-to-ptr)
156 return reinterpret_cast<T *>(reinterpret_cast<intptr_t>(ptr) - offset);
157}
158
159template <typename T, T From = 0, T To = std::numeric_limits<T>::max()>
160class IdPool {
161public:
162 static_assert(From < To, "Invalid ID range");
163
164 T allocate() {
165 if (!recycled_ids_.empty()) {
166 T id = recycled_ids_.top();
167 recycled_ids_.pop();
168 return id;
169 }
170 if (next_id_ < To) {
171 return next_id_++;
172 }
173 throw std::runtime_error("ID pool exhausted");
174 }
175
176 void recycle(T id) noexcept {
177 assert(From <= id && id < next_id_ && id < To);
178 recycled_ids_.push(id);
179 }
180
181 void reset() noexcept {
182 next_id_ = From;
183 while (!recycled_ids_.empty()) {
184 recycled_ids_.pop();
185 }
186 }
187
188private:
189 T next_id_ = From;
190 std::stack<T> recycled_ids_;
191};
192
193class AtomicMutex {
194public:
195 void lock() noexcept {
196 bool expected = false;
197 while (!lock_.compare_exchange_weak(expected, true,
198 std::memory_order_acquire,
199 std::memory_order_relaxed)) {
200 expected = false;
201 lock_.wait(true, std::memory_order_relaxed);
202 }
203 }
204
205 void unlock() noexcept {
206 lock_.store(false, std::memory_order_release);
207 lock_.notify_one();
208 }
209
210private:
211 std::atomic_bool lock_ = false;
212};
213
214#if __cplusplus >= 202302L
215[[noreturn]] inline void unreachable() { std::unreachable(); }
216#else
217[[noreturn]] inline void unreachable() { __builtin_unreachable(); }
218#endif
219
220} // 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:69