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_guarded(::MemoryContextAlloc)(_memory_context(), sizeof(T) * n));
18 }
19 template <typename T = void> void free(T *ptr) { ffi_guarded(::pfree)(ptr); }
20
21 void reset() { ffi_guarded(::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_guarded(::MemoryContextRegisterResetCallback)(_memory_context(), cb);
34 return cb;
35 }
36
37 void delete_context() { ffi_guarded(::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_guarded(::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_guarded(::AllocSetContextCreateInternal)(
98 ::CurrentMemoryContext, nullptr, ALLOCSET_DEFAULT_SIZES)) {}
101 ffi_guarded(::AllocSetContextCreateInternal)(ctx, nullptr, ALLOCSET_DEFAULT_SIZES)) {}
102};
103
104memory_context top_memory_context = memory_context(TopMemoryContext);
105
106template <typename C> requires std::derived_from<C, abstract_memory_context>
109 : ctx(context.ctx), counter(context.counter), cb(context.cb) {
110 cb->arg = this;
111 }
112
113 explicit tracking_memory_context(C ctx)
114 : ctx(ctx), counter(0),
115 cb(std::shared_ptr<::MemoryContextCallback>(
116 this->register_reset_callback(
117 [](void *i) { static_cast<struct tracking_memory_context<C> *>(i)->counter++; },
118 this),
119 /* custom deleter */
120 [](auto) {})) {}
121
123 : ctx(std::move(other.ctx)), counter(std::move(other.counter)), cb(std::move(other.cb)) {
124 other.cb = nullptr;
125 cb->arg = this;
126 }
127
129 : ctx(std::move(other.ctx)), counter(std::move(other.counter)), cb(std::move(other.cb)) {
130 cb->arg = this;
131 other.cb = nullptr;
132 }
133
134 tracking_memory_context &operator=(tracking_memory_context &&other) noexcept {
135 ctx = other.ctx;
136 cb = other.cb;
137 counter = other.counter;
138 cb->arg = this;
139 return *this;
140 }
141
143 if (cb != nullptr) {
144 if (cb.use_count() == 1) {
145 cb->func = [](void *) {};
146 }
147 }
148 }
149
150 uint64_t resets() const { return counter; }
151 C &get_memory_context() { return ctx; }
152
153private:
154 template <typename T> requires std::integral<T>
155 struct shared_counter {
156 T value;
157 constexpr explicit shared_counter(T init = 0) noexcept : value(init) {}
158
159 shared_counter &operator=(T v) noexcept {
160 value = v;
161 return *this;
162 }
163
164 shared_counter &operator++() noexcept {
165 ++value;
166 return *this;
167 }
168
169 T operator++(int) noexcept {
170 T old = value;
171 ++value;
172 return old;
173 }
174
175 constexpr operator T() const noexcept { return value; }
176 };
177 C ctx;
178 shared_counter<uint64_t> counter;
179 std::shared_ptr<::MemoryContextCallback> cb;
180
181protected:
182 ::MemoryContext _memory_context() override { return ctx._memory_context(); }
183};
184
185template <typename T>
187 std::derived_from<T, abstract_memory_context> && std::default_initializable<T>;
188
189template <a_memory_context Context> struct memory_context_scope {
190 explicit memory_context_scope(Context &ctx)
191 : previous(::CurrentMemoryContext), ctx(ctx.operator ::MemoryContext()) {
192 ::CurrentMemoryContext = ctx;
193 }
194 explicit memory_context_scope(Context &&ctx)
195 : previous(::CurrentMemoryContext), ctx(ctx.operator ::MemoryContext()) {
196 ::CurrentMemoryContext = ctx;
197 }
198
199 ~memory_context_scope() { ::CurrentMemoryContext = previous; }
200
201private:
202 ::MemoryContext previous;
203 ::MemoryContext ctx;
204};
205
206template <class T, a_memory_context Context = memory_context> struct memory_context_allocator {
207 using value_type = T;
208 memory_context_allocator() noexcept : context(Context()), explicit_deallocation(false) {}
209 memory_context_allocator(Context &&ctx, bool explicit_deallocation) noexcept
210 : context(std::move(ctx)), explicit_deallocation(explicit_deallocation) {}
211
212 constexpr memory_context_allocator(const memory_context_allocator<T> &c) noexcept
213 : context(c.context) {}
214
215 [[nodiscard]] T *allocate(std::size_t n) {
216 try {
217 return context.template alloc<T>(n);
218 } catch (pg_exception &e) {
219 throw std::bad_alloc();
220 }
221 }
222
223 void deallocate(T *p, std::size_t n) noexcept {
224 if (explicit_deallocation || context == top_memory_context) {
225 context.free(p);
226 }
227 }
228
229 bool operator==(const memory_context_allocator &c) { return context == c.context; }
230 bool operator!=(const memory_context_allocator &c) { return context != c.context; }
231
232 Context &memory_context() { return context; }
233
234private:
235 Context context;
236 bool explicit_deallocation;
237};
238
239struct pointer_gone_exception : public std::exception {
240 const char *what() const noexcept override {
241 return "pointer belongs to a MemoryContext that has been reset or deleted";
242 }
243};
244
245} // namespace cppgres
Definition: exception.hpp:7
Definition: memory.hpp:186
Definition: memory.hpp:14
Definition: memory.hpp:94
Definition: memory.hpp:206
Definition: memory.hpp:189
Definition: memory.hpp:61
Definition: memory.hpp:43
Definition: memory.hpp:239
Definition: memory.hpp:107