Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions changelog.d/20260420_184500_forest_domain_protocol.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
bump: patch
---

### Changed
- Made `ForestDomainViewOps`/`ForestDomainOps` the canonical read-only/mutable protocol surfaces for
AVL-backed forest domains and moved `pmap` onto them.
- Routed symbol and legacy root access through the canonical forest-domain root-index helpers.
110 changes: 75 additions & 35 deletions include/pmm/avl_tree_mixin.h
Original file line number Diff line number Diff line change
Expand Up @@ -512,48 +512,95 @@ static void avl_insert( PPtr new_node, IndexType& root_idx, GoLeftFn&& go_left,
avl_rebalance_up( parent, root_idx, update_node );
}

// ─── Forest-domain descriptor/policy seam ────────────────────────────────────
// ─── Forest-domain protocol ──────────────────────────────────────────────────

template <typename Domain, typename Key>
concept ForestDomainDescriptorForKey = requires( typename Domain::node_pptr p, const Key& key ) {
template <typename Domain>
concept ForestDomainViewDescriptor = requires( const Domain domain, typename Domain::node_pptr p ) {
typename Domain::index_type;
typename Domain::node_type;
typename Domain::node_pptr;
{ Domain::name() } -> std::convertible_to<const char*>;
{ Domain::root_index() } -> std::convertible_to<typename Domain::index_type>;
{ Domain::root_index_ptr() } -> std::same_as<typename Domain::index_type*>;
{ Domain::resolve_node( p ) } -> std::convertible_to<typename Domain::node_type*>;
{ Domain::compare_key( key, p ) } -> std::convertible_to<int>;
{ Domain::less_node( p, p ) } -> std::convertible_to<bool>;
{ domain.name() } -> std::convertible_to<const char*>;
{ domain.root_index() } -> std::convertible_to<typename Domain::index_type>;
{ domain.resolve_node( p ) } -> std::convertible_to<typename Domain::node_type*>;
};

template <typename Domain> static bool forest_domain_validate_node( typename Domain::node_pptr p ) noexcept
template <typename Domain>
concept ForestDomainDescriptor =
ForestDomainViewDescriptor<Domain> && requires( Domain domain, typename Domain::node_pptr p ) {
{ domain.root_index_ptr() } -> std::same_as<typename Domain::index_type*>;
{ domain.less_node( p, p ) } -> std::convertible_to<bool>;
};

template <typename Domain, typename Key>
concept ForestDomainDescriptorForKey = ForestDomainViewDescriptor<Domain> &&
requires( const Domain domain, typename Domain::node_pptr p, const Key& key ) {
{ domain.compare_key( key, p ) } -> std::convertible_to<int>;
};

template <typename Domain>
static bool forest_domain_validate_node( const Domain& domain, typename Domain::node_pptr p ) noexcept
{
if constexpr ( requires {
{ Domain::validate_node( p ) } -> std::convertible_to<bool>;
{ domain.validate_node( p ) } -> std::convertible_to<bool>;
} )
return Domain::validate_node( p );
return domain.validate_node( p );
else
return true;
}

/**
* @brief Generic AVL-backed forest-domain operations for a concrete descriptor.
* @brief Generic read-only AVL-backed forest-domain operations for a concrete descriptor.
*
* The descriptor owns domain identity, root binding, node resolution, ordering,
* and optional node validation. This wrapper keeps the AVL substrate reusable
* without forcing allocator and non-allocator domains into the same runtime type.
* The view descriptor supplies domain identity, read-only root binding, node
* resolution, and optional external-key comparison.
*/
template <typename Domain> struct ForestDomainOps
template <ForestDomainViewDescriptor Domain> struct ForestDomainViewOps
{
using index_type = typename Domain::index_type;
using node_type = typename Domain::node_type;
using node_pptr = typename Domain::node_pptr;

static constexpr const char* name() noexcept { return Domain::name(); }
static index_type root_index() noexcept { return Domain::root_index(); }
static index_type* root_index_ptr() noexcept { return Domain::root_index_ptr(); }
Domain domain;

static bool reset_root() noexcept
constexpr explicit ForestDomainViewOps( Domain d = Domain{} ) noexcept : domain( d ) {}

const char* name() const noexcept { return domain.name(); }
index_type root_index() const noexcept { return domain.root_index(); }

template <typename Key>
requires ForestDomainDescriptorForKey<Domain, Key>
node_pptr find( const Key& key ) const noexcept
{
return avl_find<node_pptr>(
domain.root_index(), [&]( node_pptr cur ) -> int { return domain.compare_key( key, cur ); },
[this]( node_pptr p ) -> node_type* { return domain.resolve_node( p ); } );
}
};

/**
* @brief Generic mutable AVL-backed forest-domain operations for a concrete descriptor.
*
* The mutable descriptor adds root-slot access and node ordering. Mutation is
* intentionally kept off the const surface: callers that only have a const
* handle can read identity/root state and perform keyed lookup, but cannot
* obtain or rewrite the root slot.
*/
template <ForestDomainDescriptor Domain> struct ForestDomainOps : ForestDomainViewOps<Domain>
{
using view_base = ForestDomainViewOps<Domain>;
using index_type = typename view_base::index_type;
using node_type = typename view_base::node_type;
using node_pptr = typename view_base::node_pptr;

using view_base::find;
using view_base::name;
using view_base::root_index;

constexpr explicit ForestDomainOps( Domain d = Domain{} ) noexcept : view_base( d ) {}

index_type* root_index_ptr() noexcept { return this->domain.root_index_ptr(); }

bool reset_root() noexcept
{
index_type* root = root_index_ptr();
if ( root == nullptr )
Expand All @@ -562,25 +609,18 @@ template <typename Domain> struct ForestDomainOps
return true;
}

template <typename Key>
requires ForestDomainDescriptorForKey<Domain, Key>
static node_pptr find( const Key& key ) noexcept
{
return avl_find<node_pptr>(
Domain::root_index(), [&]( node_pptr cur ) -> int { return Domain::compare_key( key, cur ); },
[]( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } );
}

static void insert( node_pptr new_node ) noexcept
void insert( node_pptr new_node ) noexcept
{
index_type* root = Domain::root_index_ptr();
index_type* root = this->domain.root_index_ptr();
if ( root == nullptr || new_node.is_null() )
return;
if ( Domain::resolve_node( new_node ) == nullptr || !forest_domain_validate_node<Domain>( new_node ) )
if ( this->domain.resolve_node( new_node ) == nullptr ||
!forest_domain_validate_node( this->domain, new_node ) )
return;
avl_insert(
new_node, *root, [new_node]( node_pptr cur ) -> bool { return Domain::less_node( new_node, cur ); },
[]( node_pptr p ) -> typename Domain::node_type* { return Domain::resolve_node( p ); } );
new_node, *root,
[this, new_node]( node_pptr cur ) -> bool { return this->domain.less_node( new_node, cur ); },
[this]( node_pptr p ) -> node_type* { return this->domain.resolve_node( p ); } );
}
};

Expand Down
45 changes: 21 additions & 24 deletions include/pmm/forest_domain_mixin.inc
Original file line number Diff line number Diff line change
Expand Up @@ -85,44 +85,40 @@ static forest_domain* find_domain_by_symbol_unlocked( pptr<pstringview> symbol )
return nullptr;
}

static index_type domain_root_offset_unlocked( const forest_domain* rec,
const detail::ManagerHeader<address_traits>* hdr ) noexcept
static index_type forest_domain_root_index_unlocked( const forest_domain* rec ) noexcept
{
const detail::ManagerHeader<address_traits>* hdr =
( _backend.base_ptr() != nullptr ) ? get_header_c( _backend.base_ptr() ) : nullptr;
if ( rec == nullptr || hdr == nullptr )
return 0;
if ( rec->binding_kind == detail::kForestBindingFreeTree )
return ( hdr->free_tree_root == address_traits::no_block ) ? static_cast<index_type>( 0 ) : hdr->free_tree_root;
return rec->root_offset;
}

// ─── Legacy root helpers ──────────────────────────────────────────────────────

static index_type get_legacy_root_offset_unlocked() noexcept
static index_type* forest_domain_root_index_ptr_unlocked( forest_domain* rec ) noexcept
{
const forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot );
return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) );
if ( rec == nullptr || rec->binding_kind != detail::kForestBindingDirectRoot )
return nullptr;
return &rec->root_offset;
}

static void set_legacy_root_offset_unlocked( index_type off ) noexcept
static bool set_forest_domain_root_index_unlocked( forest_domain* rec, index_type root ) noexcept
{
forest_domain* rec = find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot );
if ( rec != nullptr && rec->binding_kind == detail::kForestBindingDirectRoot )
rec->root_offset = off;
index_type* root_ptr = forest_domain_root_index_ptr_unlocked( rec );
if ( root_ptr == nullptr )
return false;
*root_ptr = root;
return true;
}

// ─── Symbol domain helpers ────────────────────────────────────────────────────
// ─── Canonical system domain records ─────────────────────────────────────────

static forest_domain* symbol_domain_record_unlocked() noexcept
{
return find_domain_by_name_unlocked( detail::kSystemDomainSymbols );
}

static index_type symbol_domain_root_offset_unlocked() noexcept
{
forest_domain* rec = symbol_domain_record_unlocked();
return ( rec != nullptr ) ? rec->root_offset : static_cast<index_type>( 0 );
}

// ─── Domain registration ─────────────────────────────────────────────────────

static bool register_domain_unlocked( const char* name, std::uint8_t flags, std::uint8_t binding_kind,
Expand Down Expand Up @@ -179,11 +175,11 @@ static pptr<pstringview> intern_symbol_unlocked( const char* s ) noexcept
if ( s == nullptr )
s = "";

using symbol_policy = typename pstringview::forest_domain_policy;
if ( symbol_policy::root_index_ptr() == nullptr )
auto symbol_policy = pstringview::forest_domain_ops();
if ( symbol_policy.root_index_ptr() == nullptr )
return pptr<pstringview>();

pptr<pstringview> found = symbol_policy::find( s );
pptr<pstringview> found = symbol_policy.find( s );
if ( !found.is_null() )
return found;

Expand All @@ -209,7 +205,7 @@ static pptr<pstringview> intern_symbol_unlocked( const char* s ) noexcept
if ( !lock_block_permanent_unlocked( public_raw ) )
return pptr<pstringview>();

symbol_policy::insert( new_node );
symbol_policy.insert( new_node );

return new_node;
}
Expand Down Expand Up @@ -375,7 +371,7 @@ static bool validate_bootstrap_invariants_unlocked() noexcept
if ( free_rec->binding_kind != detail::kForestBindingFreeTree )
return false;
// 5. Symbol dictionary root is non-zero (at least bootstrap symbols exist)
if ( symbol_domain_root_offset_unlocked() == 0 )
if ( pstringview::forest_domain_ops().root_index() == 0 )
return false;
// 6. Registry domain root matches header root_offset
const forest_domain* reg_rec = find_domain_by_name_unlocked( detail::kSystemDomainRegistry );
Expand Down Expand Up @@ -404,7 +400,8 @@ static bool validate_or_bootstrap_forest_registry_unlocked() noexcept
detail::kForestBindingFreeTree, 0 ) )
return false;
if ( !register_domain_unlocked( detail::kSystemDomainSymbols, detail::kForestDomainFlagSystem,
detail::kForestBindingDirectRoot, symbol_domain_root_offset_unlocked() ) )
detail::kForestBindingDirectRoot,
pstringview::forest_domain_ops().root_index() ) )
return false;
if ( !register_domain_unlocked( detail::kSystemDomainRegistry, detail::kForestDomainFlagSystem,
detail::kForestBindingDirectRoot, hdr->root_offset ) )
Expand Down
18 changes: 9 additions & 9 deletions include/pmm/persist_memory_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi<PersistMemoryM
typename thread_policy::unique_lock_type lock( _mutex );
if ( !_initialized )
return;
set_legacy_root_offset_unlocked( p.is_null() ? static_cast<index_type>( 0 ) : p.offset() );
set_forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ),
p.is_null() ? static_cast<index_type>( 0 ) : p.offset() );
}

/**
Expand All @@ -531,7 +532,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi<PersistMemoryM
typename thread_policy::shared_lock_type lock( _mutex );
if ( !_initialized )
return pptr<T>();
index_type legacy_root = get_legacy_root_offset_unlocked();
index_type legacy_root =
forest_domain_root_index_unlocked( find_domain_by_name_unlocked( detail::kServiceNameLegacyRoot ) );
if ( legacy_root == static_cast<index_type>( 0 ) )
return pptr<T>();
return pptr<T>( legacy_root );
Expand Down Expand Up @@ -587,7 +589,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi<PersistMemoryM
if ( !_initialized )
return 0;
const forest_domain* rec = find_domain_by_name_unlocked( name );
return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) );
return forest_domain_root_index_unlocked( rec );
}

static index_type get_domain_root_offset( index_type binding_id ) noexcept
Expand All @@ -596,7 +598,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi<PersistMemoryM
if ( !_initialized )
return 0;
const forest_domain* rec = find_domain_by_binding_unlocked( binding_id );
return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) );
return forest_domain_root_index_unlocked( rec );
}

static index_type get_domain_root_offset( pptr<pstringview> symbol ) noexcept
Expand All @@ -605,7 +607,7 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi<PersistMemoryM
if ( !_initialized )
return 0;
const forest_domain* rec = find_domain_by_symbol_unlocked( symbol );
return domain_root_offset_unlocked( rec, get_header_c( _backend.base_ptr() ) );
return forest_domain_root_index_unlocked( rec );
}

template <typename T> static pptr<T> get_domain_root( const char* name ) noexcept
Expand All @@ -632,10 +634,8 @@ class PersistMemoryManager : public detail::PersistMemoryTypedApi<PersistMemoryM
if ( !_initialized )
return false;
forest_domain* rec = find_domain_by_name_unlocked( name );
if ( rec == nullptr || rec->binding_kind != detail::kForestBindingDirectRoot )
return false;
rec->root_offset = root.is_null() ? static_cast<index_type>( 0 ) : root.offset();
return true;
return set_forest_domain_root_index_unlocked( rec,
root.is_null() ? static_cast<index_type>( 0 ) : root.offset() );
}

// ─── Методы доступа к полям AVL-узла блока ─────────────
Expand Down
Loading
Loading