Cppgres
Build Postgres extensions in C++
Loading...
Searching...
No Matches
memory.hpp
Go to the documentation of this file.
1
4#pragma once
5
6#include <concepts>
7#include <memory>
8
9#include "guard.hpp"
10#include "imports.h"
11
12namespace cppgres {
13
15 virtual ~abstract_memory_context() = default;
16
17 template <typename T = std::byte> T *alloc(size_t n = 1) {
18 return static_cast<T *>(ffi_guard{::MemoryContextAlloc}(_memory_context(), sizeof(T) * n));
19 }
20 template <typename T = void> void free(T *ptr) { ffi_guard{::pfree}(ptr); }
21
22 void reset() { ffi_guard{::MemoryContextReset}(_memory_context()); }
23
24 bool operator==(abstract_memory_context &c) noexcept {
25 return _memory_context() == c._memory_context();
26 }
27 bool operator!=(abstract_memory_context &c) noexcept {
28 return _memory_context() != c._memory_context();
29 }
30
31 operator ::MemoryContext() { return _memory_context(); }
32
33 ::MemoryContextCallback *register_reset_callback(::MemoryContextCallbackFunction func,
34 void *arg) {
35 auto cb = alloc<::MemoryContextCallback>();
36 cb->func = func;
37 cb->arg = arg;
38 ffi_guard{::MemoryContextRegisterResetCallback}(_memory_context(), cb);
39 return cb;
40 }
41
42 void delete_context() { ffi_guard{::MemoryContextDelete}(_memory_context()); }
43
47 auto operator()(auto thunk) { return memory_context_execution(thunk, *this)(); }
48
49protected:
50 virtual ::MemoryContext _memory_context() = 0;
51
52 template <typename T> requires requires(T t) { t(); }
55 : _ctx(::CurrentMemoryContext), _thunk(thunk) {
56 ::CurrentMemoryContext = ctx;
57 }
58 ~memory_context_execution() { ::CurrentMemoryContext = _ctx; }
59
60 auto operator()() { return _thunk(); }
61
62 private:
63 MemoryContext _ctx;
64 T _thunk;
65 };
66};
67
69 friend struct memory_context;
70
71protected:
72 owned_memory_context(::MemoryContext context) : context(context), moved(false) {}
74 owned_memory_context &operator=(const owned_memory_context &) = delete;
76 : context(other.context), moved(other.moved) {
77 other.moved = true;
78 }
79 owned_memory_context &operator=(owned_memory_context &&other) {
80 if (this != &other) {
81 if (!moved) {
82 delete_context();
83 }
84 context = other.context;
85 moved = other.moved;
86 other.moved = true;
87 }
88 return *this;
89 }
90
92 if (!moved) {
93 delete_context();
94 }
95 }
96
97 ::MemoryContext context;
98 bool moved;
99
100 ::MemoryContext _memory_context() override { return context; }
101};
102
104
105 friend struct owned_memory_context;
106
107 explicit memory_context() : context(::CurrentMemoryContext) {}
108 explicit memory_context(::MemoryContext context) : context(context) {}
109 explicit memory_context(abstract_memory_context &&context) : context(context) {}
110
111 explicit memory_context(owned_memory_context &&ctx) : context(ctx) { ctx.moved = true; }
112
113 static memory_context for_pointer(void *ptr) {
114 if (ptr == nullptr || ptr != (void *)MAXALIGN(ptr)) {
115 throw std::runtime_error("invalid pointer");
116 }
117 return memory_context(ffi_guard{::GetMemoryChunkContext}(ptr));
118 }
119
120 template <typename C> requires std::derived_from<C, abstract_memory_context>
121 friend struct tracking_memory_context;
122
123protected:
124 ::MemoryContext context;
125
126 ::MemoryContext _memory_context() noexcept override { return context; }
127};
128
131
132protected:
133 ::MemoryContext _memory_context() override { return ::CurrentMemoryContext; }
134};
135
137 using owned_memory_context::owned_memory_context;
139 : owned_memory_context(ffi_guard{::AllocSetContextCreateInternal}(
140 ::CurrentMemoryContext, nullptr, ALLOCSET_DEFAULT_SIZES)) {}
143 ffi_guard{::AllocSetContextCreateInternal}(ctx, nullptr, ALLOCSET_DEFAULT_SIZES)) {}
144
147 ffi_guard{::AllocSetContextCreateInternal}(ctx, nullptr, ALLOCSET_DEFAULT_SIZES)) {}
148};
149
150inline memory_context top_memory_context() { return memory_context(TopMemoryContext); };
151
152template <typename C> requires std::derived_from<C, abstract_memory_context>
154private:
155 template <typename T> requires std::integral<T>
156 struct shared_counter {
157 T value;
158 constexpr explicit shared_counter(T init = 0) noexcept : value(init) {}
159
160 shared_counter &operator=(T v) noexcept {
161 value = v;
162 return *this;
163 }
164
165 shared_counter &operator++() noexcept {
166 ++value;
167 return *this;
168 }
169
170 T operator++(int) noexcept {
171 T old = value;
172 ++value;
173 return old;
174 }
175
176 constexpr operator T() const noexcept { return value; }
177 };
178
179 struct callback_state {
180 shared_counter<uint64_t> counter;
181 ::MemoryContextCallback *callback = nullptr;
182 };
183
184 static void track_reset(void *arg) {
185 auto *state = static_cast<callback_state *>(arg);
186 state->counter++;
187 state->callback = nullptr;
188 }
189
190public:
192 : ctx(other.ctx), state(other.state) {}
193
194 explicit tracking_memory_context(C ctx) : ctx(ctx), state(std::make_shared<callback_state>()) {
195 state->callback = this->register_reset_callback(track_reset, state.get());
196 }
197
199 : ctx(std::move(other.ctx)), state(std::move(other.state)) {}
200
201 tracking_memory_context &operator=(const tracking_memory_context &other) noexcept {
202 if (this != &other) {
203 ctx = other.ctx;
204 state = other.state;
205 }
206 return *this;
207 }
208
209 tracking_memory_context &operator=(tracking_memory_context &&other) noexcept {
210 if (this != &other) {
211 ctx = std::move(other.ctx);
212 state = std::move(other.state);
213 }
214 return *this;
215 }
216
218 if (state != nullptr && state.use_count() == 1 && state->callback != nullptr) {
219 state->callback->func = [](void *) {};
220 state->callback->arg = nullptr;
221 state->callback = nullptr;
222 }
223 }
224
225 uint64_t resets() const { return state == nullptr ? 0 : state->counter; }
226 C &get_memory_context() { return ctx; }
227
228private:
229 C ctx;
230 std::shared_ptr<callback_state> state;
231
232protected:
233 ::MemoryContext _memory_context() override { return ctx._memory_context(); }
234};
235
236template <typename T>
238 std::derived_from<T, abstract_memory_context> && std::default_initializable<T>;
239
240template <a_memory_context Context> struct memory_context_scope {
241 explicit memory_context_scope(Context &ctx)
242 : previous(::CurrentMemoryContext), ctx(ctx.operator ::MemoryContext()) {
243 ::CurrentMemoryContext = ctx;
244 }
245 explicit memory_context_scope(Context &&ctx)
246 : previous(::CurrentMemoryContext), ctx(ctx.operator ::MemoryContext()) {
247 ::CurrentMemoryContext = ctx;
248 }
249
250 ~memory_context_scope() { ::CurrentMemoryContext = previous; }
251
252private:
253 ::MemoryContext previous;
254 ::MemoryContext ctx;
255};
256
257template <class T, a_memory_context Context = memory_context> struct memory_context_allocator {
258 using value_type = T;
259 memory_context_allocator() noexcept : context(Context()), explicit_deallocation(false) {}
260 memory_context_allocator(Context &&ctx, bool explicit_deallocation) noexcept
261 : context(std::move(ctx)), explicit_deallocation(explicit_deallocation) {}
262
263 constexpr memory_context_allocator(const memory_context_allocator<T> &c) noexcept
264 : context(c.context) {}
265
266 [[nodiscard]] T *allocate(std::size_t n) {
267 try {
268 return context.template alloc<T>(n);
269 } catch (pg_exception &e) {
270 throw std::bad_alloc();
271 }
272 }
273
274 void deallocate(T *p, std::size_t n) noexcept {
275 if (explicit_deallocation || context == top_memory_context()) {
276 context.free(p);
277 }
278 }
279
280 bool operator==(const memory_context_allocator &c) { return context == c.context; }
281 bool operator!=(const memory_context_allocator &c) { return context != c.context; }
282
283 Context &memory_context() { return context; }
284
285private:
286 Context context;
287 bool explicit_deallocation;
288};
289
290struct pointer_gone_exception : public std::exception {
291 const char *what() const noexcept override {
292 return "pointer belongs to a MemoryContext that has been reset or deleted";
293 }
294};
295
296} // namespace cppgres
Definition: exception.hpp:7
Definition: memory.hpp:237
Definition: memory.hpp:14
auto operator()(auto thunk)
Definition: memory.hpp:47
Definition: memory.hpp:136
Definition: memory.hpp:129
Definition: guard.hpp:19
Definition: memory.hpp:257
Definition: memory.hpp:240
Definition: memory.hpp:103
Definition: memory.hpp:68
Definition: memory.hpp:290
Definition: memory.hpp:153
Definition: value.hpp:8