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