alloc.h: Added `deleter` (polymorphic, untyped base & typed template children), working on interface design...

Fortune for readpass's current commit: Middle blessing − 中吉
master
Avril 2 years ago
parent 8ac56c862f
commit 4542eaff93
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -6,6 +6,7 @@
#include <memory>
#endif
#include "constraints.hh"
#include "types.h"
#include "macros.h"
@ -28,13 +29,76 @@ namespace alloc {
struct _impl;
protected:
struct anon_raw_secmem;
/*struct anon_raw_secmem::deleter {
operator
};*/
// Untyped deleter
struct deleter {
friend class anon_raw_secmem;
constexpr deleter(const deleter&) noexcept = default;
constexpr deleter& operator=(const deleter&) noexcept = default;
constexpr deleter(deleter&&) noexcept = default;
constexpr deleter& operator=(deleter&&) noexcept = default;
constexpr void operator()(void* restrict p) const noexcept { return apply_delete(p); }
virtual ~deleter();
protected:
explicit deleter(std::shared_ptr<anon_raw_secmem>&& p);
template<where::is_polymorphic T>
constexpr static void* erase_type(T* ptr) noexcept { return dynamic_cast<void*>(ptr); }
template<where::is_polymorphic T>
constexpr static void const* erase_type(T const* ptr) noexcept { return dynamic_cast<void const*>(ptr); }
template<typename T>
constexpr static void* erase_type_unsafe(T* ptr) noexcept { return static_cast<void*>(ptr); }
template<typename T>
constexpr static T* add_type_unsafe(void* ptr) noexcept { return static_cast<T*>(ptr); }
template<typename T>
constexpr static void const* erase_type_unsafe(T const* ptr) noexcept { return static_cast<void const*>(ptr); }
template<typename T>
constexpr static T const* add_type_unsafe(void const* ptr) noexcept { return static_cast<T const*>(ptr); }
virtual void apply_delete(void* restrict, bool = true) const noexcept;
inline deleter* finalizer_group_id() const noexcept {
return m_group_ptr ?: static_cast<deleter*>(this);
}
// See dtor impl
void apply_finalizer_group(std::initializer_list<void*> = {}) noexcept;
void apply_finalizer_now(std::initializer_list<void*> = {}) noexcept;
// To prevent anon_raw_secmem being destroyed while there are still allocated values, the base class for the deleter for those values contains a refcount. e.g: `std::unique_ptr<T, deleter_for<T>>` where: `deleter_for<T> final : public deleter { virtual ~deleter_for(); ... };`, or `std::shared_ptr<T>`, where: `std::shared_ptr<value_with_deleter<T>>` aliases-ctor(`old, old->value_ptr()`) -> `std::shared_ptr<T>`
std::shared_ptr<anon_raw_secmem> m_manager_ref;
private:
// Used to sync `this`'s group across copies.
deleter* m_group_ptr;
};
struct alloc_info;
struct alloc_value;
public:
template<typename T>
struct deleter_for : virtual deleter {
inline deleter_for(std::shared_ptr<anon_raw_secmem>&& m)
: deleter(std::move(m)) {}
virtual ~deleter_for() = default; // This will use deleter's dtor to remove allocations.
inline void operator()(T* ptr) const noexcept { return apply_delete(deleter::erase_type_unsafe(ptr)); }
protected:
inline virtual void apply_delete_typed(T* ptr) const noexcept {
ptr->~T();
}
private:
inline void apply_delete(void* restrict up) const noexcept override final {
if constexpr(std::is_trivially_destructible_v<T>) {
deleter::apply_delete(up); // If the dtor is trivial, ignore it and use default behaviour.
} else {
deleter::apply_delete(up, false); // Unlock the memory, but do *not* add it to this deleter's finalizer group.
apply_delete_typed(static_cast<T*>(up)); // Apply the destructor for `T`
deleter::apply_finalizer_now({up}); // Get `anon_raw_secmem` to deallocate the memory *now*, instead of at destruction.
}
}
};
public:
FrozenAllocator(FrozenAllocator &&) noexcept;
FrozenAllocator& operator=(FrozenAllocator &&);
@ -43,8 +107,11 @@ namespace alloc {
virtual ~FrozenAllocator();
private:
std::unique_ptr<_impl> inner_;
/// Manages everything about the actual allocations
//std::shared_ptr<anon_raw_secmem> m_manager;
//std::map<alloc_info, std::unique_ptr<alloc_value>> m_values;
/// A map of values inside the allocator. This is destroyed in reverse order, meaning all the living values are destroyed *before* `m_manager`'d destruction deallocates them.
//std::map<alloc_info, std::unique_ptr<alloc_value, deleter>> m_values;
};
}
#endif

@ -24,6 +24,9 @@ namespace where {
template<typename T>
using add_const_pointer_t = decltype(std::addressof(std::as_const(std::declval<T>())));
template<typename T>
concept is_polymorphic = requires(requires(T* ptr) { dynamic_cast<void const*>(ptr); });
static_assert(
std::is_lvalue_reference_v<add_const_reference_t<int>>
and std::is_lvalue_reference_v<add_const_reference_t<int const&>>

@ -9,16 +9,46 @@ extern "C" {
}
namespace alloc {
// Base class for all deallocations that happen within an `anon_raw_secmem` (managed, static, polymorphic, unmanaged, etc.) (TODO: See below, the base should be moved into the header so typed children can be templated...)
FrozenAllocator::deleter::~deleter() {
apply_finalizer_group();
}
FrozenAllocator::deleter::deleter(std::shared_ptr<anon_raw_secmem>&& p)
: m_manager_ref(std::move(p))
, m_gorup_ptr(static_cast<deleter*>(this)) {} //TODO:XXX: Do we actually need finalizer groups now? (see note below about `m_values`.
void FrozenAllocator::deleter::apply_finalizer_group(std::initializer_list<void*> ptrs) noexcept {
if(ptrs.empty()) {
//TODO: auto&& fgroup = std::move(m_manager_ref->get_finalizer_group_for(finalizer_group_id())); // The *whole* finalizer group, *moved* from `m_manager_ref`. (which leaves finalizer_group for `this` empty now.)
//TODO: `bzero_explicit()` the memory from the allocations, then tell `anon_raw_secmem` to mark is as free.
} else {
//TODO: auto& fgroup = m_manager_ref->get_finalizer_group_ref_for(finalizer_group_id()); // The whole finalizer group, *referenced* from `m_manager_ref`. (which leaves finalizer_group for `this` intact, removal of pointers from the group will have to happen below manually.)
for(void* p : ptrs) {
//TODO: Do the same as above, but only for pointers registered for finalizing in `ptrs`.
}
}
}
/// Instead of acting on pointers in `this`'s finalizer group, immediately get `anon_raw_secmem` to perform the deallocation and map-removal on these `ptrs`.
void apply_finalizer_now(std::initializer_list<void*> ptrs) noexcept {
for(void* p : ptrs) {
//TODO: `m_manager_ref->immediate_dealloc(p)`
}
}
void FrozenAllocator::deleter::apply_delete(void* restrict p, bool add_group) const noexcept {
// TODO: allow the allocation (the memory corresponding to `p` from `m_manager_ref`) to be mutable (`munlock()` it.)
// TODO: Then, again through `m_manager_ref`, add to sequence `->register_finalizer_grouped(this, p)`.
/// TODO: Unless `add_group` is `false`, in which case, `p` is left to dangle.
//(XXX: NOTE: `m_values` map removal *causes* this to be invoked, i.e: A value removed from the map calls `deleter::operator()(uniq_p)`, which then calls `apply_delete(uniq_p, true)`
}
// This is the *true manager* of the allocation arena, when it is destroyed, the memory is cleared
struct FrozenAllocator::anon_raw_secmem {
// Base class for all deallocations that happen within an `anon_raw_secmem` (managed, static, polymorphic, unmanaged, etc.) (TODO: See below, the base should be moved into the header so typed children can be templated...)
struct deleter { //XXX: TODO: How to define this type in the header file? It should be the virt base of all templated deleters,
//TODO: XXX: Rework this design, the deleter must be typed for the value it is deleting... This virtual base should be in the header, and should just contain the shared_ptr reference to anon_raw_secmem.
virtual ~deleter() = default;
protected:
// To prevent anon_raw_secmem being destroyed while there are still allocated values, the base class for the deleter for those values contains a refcount. e.g: `std::unique_ptr<T, deleter_for<T>>` where: `deleter_for<T> final : public deleter { virtual ~deleter_for(); ... };`, or `std::shared_ptr<T>`, where: `std::shared_ptr<value_with_deleter<T>>` aliases-ctor(`old, old->value_ptr()`) -> `std::shared_ptr<T>`
std::shared_ptr<anon_raw_secmem> m_manager_ref;
};
typedef FrozenAllocator::deleter deleter;
~anon_raw_secmem() {
//TODO: Clear and `munmap()` the used page(s)
//XXX: Due to how this is managed via `shared_ptr<>`, it is UB for this to be called *before* all actual allocation of the memory are unallocated (via `deleter`, which they must *all* be managed by.
@ -34,7 +64,7 @@ namespace alloc {
struct FrozenAllocator::_impl {
std::shared_ptr<anon_raw_secmem> m_manager;
std::map<alloc_info, std::unique_ptr<alloc_value>> m_values;
std::map<alloc_info, std::unique_ptr<alloc_value, deleter>> m_values;
};
#define $CLASS FrozenAllocator

Loading…
Cancel
Save