diff --git a/include/alloc.h b/include/alloc.h index 16e7d6c..a1e7cd8 100644 --- a/include/alloc.h +++ b/include/alloc.h @@ -6,6 +6,7 @@ #include #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&& p); + + template + constexpr static void* erase_type(T* ptr) noexcept { return dynamic_cast(ptr); } + template + constexpr static void const* erase_type(T const* ptr) noexcept { return dynamic_cast(ptr); } + template + constexpr static void* erase_type_unsafe(T* ptr) noexcept { return static_cast(ptr); } + template + constexpr static T* add_type_unsafe(void* ptr) noexcept { return static_cast(ptr); } + template + constexpr static void const* erase_type_unsafe(T const* ptr) noexcept { return static_cast(ptr); } + template + constexpr static T const* add_type_unsafe(void const* ptr) noexcept { return static_cast(ptr); } + + virtual void apply_delete(void* restrict, bool = true) const noexcept; + + inline deleter* finalizer_group_id() const noexcept { + return m_group_ptr ?: static_cast(this); + } + // See dtor impl + void apply_finalizer_group(std::initializer_list = {}) noexcept; + void apply_finalizer_now(std::initializer_list = {}) 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>` where: `deleter_for final : public deleter { virtual ~deleter_for(); ... };`, or `std::shared_ptr`, where: `std::shared_ptr>` aliases-ctor(`old, old->value_ptr()`) -> `std::shared_ptr` + std::shared_ptr m_manager_ref; + private: + // Used to sync `this`'s group across copies. + deleter* m_group_ptr; + }; struct alloc_info; struct alloc_value; - public: + template + struct deleter_for : virtual deleter { + inline deleter_for(std::shared_ptr&& 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) { + 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(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 m_manager; - //std::map> 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> m_values; }; } #endif diff --git a/include/constraints.hh b/include/constraints.hh index ce30d3c..3eec0bd 100644 --- a/include/constraints.hh +++ b/include/constraints.hh @@ -24,6 +24,9 @@ namespace where { template using add_const_pointer_t = decltype(std::addressof(std::as_const(std::declval()))); + template + concept is_polymorphic = requires(requires(T* ptr) { dynamic_cast(ptr); }); + static_assert( std::is_lvalue_reference_v> and std::is_lvalue_reference_v> diff --git a/src/alloc.cpp b/src/alloc.cpp index 1e56466..904ebe1 100644 --- a/src/alloc.cpp +++ b/src/alloc.cpp @@ -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&& p) + : m_manager_ref(std::move(p)) + , m_gorup_ptr(static_cast(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 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 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>` where: `deleter_for final : public deleter { virtual ~deleter_for(); ... };`, or `std::shared_ptr`, where: `std::shared_ptr>` aliases-ctor(`old, old->value_ptr()`) -> `std::shared_ptr` - std::shared_ptr 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 m_manager; - std::map> m_values; + std::map> m_values; }; #define $CLASS FrozenAllocator