aboutsummaryrefslogblamecommitdiffstats
path: root/src/LifecycleManager.cpp
blob: 78e112daff89ae5a8e2f38f24a398382da2c647e (plain) (tree)
1
2
3
4
5
6
7
8
9

                             




                   

            




                                                                 

                                                                   

                                         

     


























                                                                                 




                                                                            
                                                                      
     















































































                                                                                       

     
#include "LifecycleManager.h"

#include "Merkle.h"
#include "Types.h"

#include <vector>

namespace nt
{
    LifecycleManager::LifecycleManager(ObjectManager& objects,
                                        IStorageBackend& storage)
        : objects_(objects), storage_(storage)
    {}

    void LifecycleManager::Monitor(ObjectManager::registry* object)
    {
        if (object != nullptr)
            ++object->head->handle_count;
    }

    void LifecycleManager::Unmonitor(ObjectManager::registry* object)
    {
        if (object == nullptr || object->head->handle_count == 0) return;
        --object->head->handle_count;
        TryCollect(object);
    }

    void LifecycleManager::Pin(ObjectManager::registry* object)
    {
        if (object != nullptr)
            ++object->head->reference_count;
    }

    void LifecycleManager::Unpin(ObjectManager::registry* object)
    {
        if (object == nullptr || object->head->reference_count == 0) return;
        --object->head->reference_count;
        TryCollect(object);
    }

    bool LifecycleManager::IsEligibleForGC(ObjectManager::registry* object) const
    {
        if (object == nullptr || object->head == nullptr) return false;
        return object->head->handle_count == 0
            && object->head->reference_count == 0;
    }

    const bool LifecycleManager::Contention(ObjectManager::registry* object)
    {
        return object != nullptr;
    }

    void LifecycleManager::TryCollect(ObjectManager::registry* object)
    {
        if (!IsEligibleForGC(object)) return;
        if (object->head == nullptr || object->head->type == nullptr) return;

        // Named-lifetime types are managed explicitly and must not auto-collect:
        //   BRANCH  — named branches persist until a future explicit-delete API
        //             tears them down. Their counters going to zero just means
        //             nobody currently holds them open.
        //   SESSION — rnt_session_close is the sole removal path.
        // Everything else (MULTIGROUP, BRANCH_TREE, RELATION,
        // EPHEMERAL_RELATION, TRANSACTION) is purely structurally referenced
        // and is GC-eligible by counters alone.
        const auto label = object->head->type->label;
        if (label == BRANCH || label == SESSION) return;

        if (label == MULTIGROUP)   CascadeMultigroup(object);
        if (label == BRANCH_TREE)  CascadeBranchTree(object);

        objects_.Unregister(object->head->path);
    }

    void LifecycleManager::CascadeMultigroup(ObjectManager::registry* multigroup)
    {
        if (multigroup == nullptr || multigroup->head == nullptr) return;
        const auto& mg_path = multigroup->head->path;
        if (mg_path.size() < 3) return;

        // /system/snapshots/<hash>/relations/<n> entries are the relation
        // children pinned by this multigroup. Collect first, mutate after —
        // Unpin can Unregister the entry we're standing on if its counters
        // were already zero, and iteration must outlive that mutation.
        std::vector<ObjectManager::registry*> children;
        for (auto* cur = objects_.entries.get(); cur != nullptr; cur = cur->next.get())
        {
            const auto& p = cur->head->path;
            if (p.size() != mg_path.size() + 2) continue;

            bool prefix_match = true;
            for (size_t i = 0; i < mg_path.size(); ++i)
                if (p[i] != mg_path[i]) { prefix_match = false; break; }
            if (!prefix_match) continue;
            if (p[mg_path.size()] != "relations") continue;

            children.push_back(cur);
        }

        for (auto* child : children) Unpin(child);
    }

    void LifecycleManager::CascadeBranchTree(ObjectManager::registry* branch_tree)
    {
        if (branch_tree == nullptr || branch_tree->head == nullptr) return;

        auto* bt = dynamic_cast<ObjectManager::BranchTree*>(branch_tree->object.get());
        if (bt == nullptr || bt->merkle_root.empty()) return;

        // Page through the (mg_name, mg_hash) tree and collect each referenced
        // multigroup before unpinning. Decoupling iteration from mutation
        // keeps us safe against Unpin cascading further GC under our feet.
        std::vector<ObjectManager::registry*> mgs;
        constexpr size_t kPageSize = 1024;
        size_t offset = 0;
        while (true)
        {
            auto page = Merkle<std::string>::Page(storage_, bt->merkle_root,
                                                   offset, kPageSize);
            if (page.empty()) break;
            for (const auto& entry : page)
            {
                static const Hash32 zero{};
                if (entry.payload == zero) continue;
                const std::string mg_hash = bin_to_hex(entry.payload);
                auto* mg_entry = objects_.Find(
                    {"system", "snapshots", mg_hash});
                if (mg_entry) mgs.push_back(mg_entry);
            }
            if (page.size() < kPageSize) break;
            offset += page.size();
        }

        for (auto* mg : mgs) Unpin(mg);
    }
}