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