Condy v1.5.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 <cstddef>
11#include <cstdint>
12#include <cstdlib>
13#include <cstring>
14#include <exception>
15#include <format>
16#include <functional>
17#include <iostream>
18#include <limits>
19#include <stack>
20#include <stdexcept>
21#include <string_view>
22#include <system_error>
23#include <utility>
24
25#if defined(__has_feature)
26#if __has_feature(thread_sanitizer)
27#include <sanitizer/tsan_interface.h>
28#define tsan_acquire(addr) __tsan_acquire(addr)
29#define tsan_release(addr) __tsan_release(addr)
30#else
31#define tsan_acquire(addr) static_cast<void>(0)
32#define tsan_release(addr) static_cast<void>(0)
33#endif
34#else
35#define tsan_acquire(addr) static_cast<void>(0)
36#define tsan_release(addr) static_cast<void>(0)
37#endif
38
39namespace condy {
40
41class [[nodiscard]] Defer {
42public:
43 template <typename Func>
44 Defer(Func &&func) : func_(std::forward<Func>(func)) {}
45 ~Defer() { func_(); }
46
47 Defer(const Defer &) = delete;
48 Defer &operator=(const Defer &) = delete;
49 Defer(Defer &&) = delete;
50 Defer &operator=(Defer &&) = delete;
51
52private:
53 std::function<void()> func_;
54};
55
62template <typename Func> Defer defer(Func &&func) {
63 return Defer(std::forward<Func>(func));
64}
65
66template <typename BaseMutex> class MaybeMutex : public BaseMutex {
67public:
68 using Base = BaseMutex;
69 using Base::Base;
70
71 void lock() noexcept {
72 if (use_mutex_) {
73 Base::lock();
74 }
75 }
76
77 void unlock() noexcept {
78 if (use_mutex_) {
79 Base::unlock();
80 }
81 }
82
83 bool try_lock() noexcept {
84 if (use_mutex_) {
85 return Base::try_lock();
86 }
87 return true;
88 }
89
90 void set_use_mutex(bool use_mutex) noexcept { use_mutex_ = use_mutex; }
91
92private:
93 bool use_mutex_ = false;
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... Args> void construct(Args &&...args) {
109 new (&storage_) T(std::forward<Args>(args)...);
110 }
111
112 T &get() { return *reinterpret_cast<T *>(&storage_); }
113
114 const T &get() const { return *reinterpret_cast<const T *>(&storage_); }
115
116 void destroy() { get().~T(); }
117
118private:
119 alignas(T) unsigned char storage_[sizeof(T)];
120};
121
122template <typename T, size_t N> class SmallArray {
123public:
124 SmallArray(size_t capacity) : capacity_(capacity) {
125 if (!is_small_()) {
126 large_ = new T[capacity];
127 }
128 }
129
130 ~SmallArray() {
131 if (!is_small_()) {
132 delete[] large_;
133 }
134 }
135
136 T &operator[](size_t index) {
137 return is_small_() ? small_[index] : large_[index];
138 }
139
140 const T &operator[](size_t index) const {
141 return is_small_() ? small_[index] : large_[index];
142 }
143
144 size_t capacity() const { return capacity_; }
145
146private:
147 bool is_small_() const { return capacity_ <= N; }
148
149private:
150 size_t capacity_;
151 union {
152 T small_[N];
153 T *large_;
154 };
155};
156
157inline auto make_system_error(std::string_view msg, int ec) {
158 return std::system_error(ec, std::generic_category(), std::string(msg));
159}
160
161template <typename M, typename T> constexpr ptrdiff_t offset_of(M T::*member) {
162 constexpr T *dummy = nullptr;
163 return reinterpret_cast<ptrdiff_t>(&(dummy->*member));
164}
165
166template <typename M, typename T> T *container_of(M T::*member, M *ptr) {
167 auto offset = offset_of(member);
168 // NOLINTNEXTLINE(performance-no-int-to-ptr)
169 return reinterpret_cast<T *>(reinterpret_cast<intptr_t>(ptr) - offset);
170}
171
172template <typename T, T From = 0, T To = std::numeric_limits<T>::max()>
173class IdPool {
174public:
175 static_assert(From < To, "Invalid ID range");
176
177 T allocate() {
178 if (!recycled_ids_.empty()) {
179 T id = recycled_ids_.top();
180 recycled_ids_.pop();
181 return id;
182 }
183 if (next_id_ < To) {
184 return next_id_++;
185 }
186 throw std::runtime_error("ID pool exhausted");
187 }
188
189 void recycle(T id) {
190 assert(From <= id && id < next_id_ && id < To);
191 recycled_ids_.push(id);
192 }
193
194 void reset() {
195 next_id_ = From;
196 while (!recycled_ids_.empty()) {
197 recycled_ids_.pop();
198 }
199 }
200
201private:
202 T next_id_ = From;
203 std::stack<T> recycled_ids_;
204};
205
206class AtomicMutex {
207public:
208 void lock() noexcept {
209 bool expected = false;
210 while (!lock_.compare_exchange_weak(expected, true,
211 std::memory_order_acquire,
212 std::memory_order_relaxed)) {
213 expected = false;
214 lock_.wait(true, std::memory_order_relaxed);
215 }
216 }
217
218 void unlock() noexcept {
219 lock_.store(false, std::memory_order_release);
220 lock_.notify_one();
221 }
222
223private:
224 std::atomic_bool lock_ = false;
225};
226
227} // namespace condy
The main namespace for the Condy library.
Definition condy.hpp:28
Defer defer(Func &&func)
Defer the execution of a function until the current scope ends.
Definition utils.hpp:62