18 template <
typename T = std::
byte> T *alloc(
size_t n = 1) {
19 if constexpr (
alignof(T) > MAXIMUM_ALIGNOF) {
20#if PG_VERSION_NUM >= 160000
21 return static_cast<T *
>(
22 ffi_guard{::MemoryContextAllocAligned}(_memory_context(),
sizeof(T) * n,
alignof(T), 0));
24 static_assert(
alignof(T) <= MAXIMUM_ALIGNOF,
25 "types over-aligned beyond MAXIMUM_ALIGNOF require PostgreSQL 16 or later");
28 return static_cast<T *
>(
ffi_guard{::MemoryContextAlloc}(_memory_context(),
sizeof(T) * n));
31 template <
typename T =
void>
void free(T *ptr) {
ffi_guard{::pfree}(ptr); }
33 void reset() {
ffi_guard{::MemoryContextReset}(_memory_context()); }
36 return _memory_context() == c._memory_context();
39 return _memory_context() != c._memory_context();
42 operator ::MemoryContext() {
return _memory_context(); }
44 ::MemoryContextCallback *register_reset_callback(::MemoryContextCallbackFunction func,
46 auto cb = alloc<::MemoryContextCallback>();
49 ffi_guard{::MemoryContextRegisterResetCallback}(_memory_context(), cb);
60 template <
typename T,
typename... Args> T *
construct(Args &&...args) {
61 static_assert(std::is_nothrow_destructible_v<T>,
62 "type constructed in a memory context must be nothrow-destructible: its "
63 "destructor runs from a memory context reset callback where exceptions cannot "
65 auto *ptr = alloc<T>();
66 if constexpr (!std::is_trivially_destructible_v<T>) {
67 auto *cb = alloc<::MemoryContextCallback>();
68 std::construct_at(ptr, std::forward<Args>(args)...);
69 cb->func = [](
void *arg) { std::destroy_at(
static_cast<T *
>(arg)); };
71 ffi_guard{::MemoryContextRegisterResetCallback}(_memory_context(), cb);
73 std::construct_at(ptr, std::forward<Args>(args)...);
78 void delete_context() {
ffi_guard{::MemoryContextDelete}(_memory_context()); }
86 virtual ::MemoryContext _memory_context() = 0;
88 template <
typename T>
requires requires(T t) { t(); }
91 : _ctx(::CurrentMemoryContext), _thunk(thunk) {
92 ::CurrentMemoryContext = ctx;
96 auto operator()() {
return _thunk(); }
112 : context(other.context), moved(other.moved) {
116 if (
this != &other) {
120 context = other.context;
133 ::MemoryContext context;
136 ::MemoryContext _memory_context()
override {
return context; }
144 explicit memory_context(::MemoryContext context) : context(context) {}
150 if (ptr ==
nullptr || ptr != (
void *)MAXALIGN(ptr)) {
151 throw std::runtime_error(
"invalid pointer");
156 template <
typename C>
requires std::derived_from<C, abstract_memory_context>
160 ::MemoryContext context;
162 ::MemoryContext _memory_context()
noexcept override {
return context; }
169 ::MemoryContext _memory_context()
override { return ::CurrentMemoryContext; }
173 using owned_memory_context::owned_memory_context;
176 ::CurrentMemoryContext,
nullptr, ALLOCSET_DEFAULT_SIZES)) {}
179 ffi_guard{::AllocSetContextCreateInternal}(ctx,
nullptr, ALLOCSET_DEFAULT_SIZES)) {}
183 ffi_guard{::AllocSetContextCreateInternal}(ctx,
nullptr, ALLOCSET_DEFAULT_SIZES)) {}
188template <
typename C>
requires std::derived_from<C, abstract_memory_context>
191 template <
typename T>
requires std::integral<T>
192 struct shared_counter {
194 constexpr explicit shared_counter(T init = 0) noexcept :
value(init) {}
196 shared_counter &operator=(T v)
noexcept {
201 shared_counter &operator++()
noexcept {
206 T operator++(
int)
noexcept {
212 constexpr operator T()
const noexcept {
return value; }
215 struct callback_state {
216 shared_counter<uint64_t> counter;
217 ::MemoryContextCallback *callback =
nullptr;
220 static void track_reset(
void *arg) {
221 auto *state =
static_cast<callback_state *
>(arg);
223 state->callback =
nullptr;
228 : ctx(other.ctx), state(other.state) {}
231 state->callback = this->register_reset_callback(track_reset, state.get());
235 : ctx(std::move(other.ctx)), state(std::move(other.state)) {}
238 if (
this != &other) {
246 if (
this != &other) {
247 ctx = std::move(other.ctx);
248 state = std::move(other.state);
254 if (state !=
nullptr && state.use_count() == 1 && state->callback !=
nullptr) {
255 state->callback->func = [](
void *) {};
256 state->callback->arg =
nullptr;
257 state->callback =
nullptr;
261 uint64_t resets()
const {
return state ==
nullptr ? 0 : state->counter; }
262 C &get_memory_context() {
return ctx; }
266 std::shared_ptr<callback_state> state;
269 ::MemoryContext _memory_context()
override {
return ctx._memory_context(); }
274 std::derived_from<T, abstract_memory_context> && std::default_initializable<T>;
278 : previous(::CurrentMemoryContext), ctx(ctx.operator ::MemoryContext()) {
279 ::CurrentMemoryContext = ctx;
282 : previous(::CurrentMemoryContext), ctx(ctx.operator ::MemoryContext()) {
283 ::CurrentMemoryContext = ctx;
289 ::MemoryContext previous;
294 using value_type = T;
297 : context(std::move(ctx)), explicit_deallocation(explicit_deallocation) {}
300 : context(c.context) {}
302 [[nodiscard]] T *allocate(std::size_t n) {
304 return context.template alloc<T>(n);
306 throw std::bad_alloc();
310 void deallocate(T *p, std::size_t n)
noexcept {
311 if (explicit_deallocation || context == top_memory_context()) {
323 bool explicit_deallocation;
327 const char *what()
const noexcept override {
328 return "pointer belongs to a MemoryContext that has been reset or deleted";
Definition: exception.hpp:7
Definition: memory.hpp:273
Definition: memory.hpp:89
Definition: memory.hpp:15
T * construct(Args &&...args)
Definition: memory.hpp:60
auto operator()(auto thunk)
Definition: memory.hpp:83
Definition: memory.hpp:172
Definition: memory.hpp:165
Definition: memory.hpp:293
Definition: memory.hpp:276
Definition: memory.hpp:139
Definition: memory.hpp:104
Definition: memory.hpp:326
Definition: memory.hpp:189