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