aboutsummaryrefslogtreecommitdiffstats
path: root/src/LifecycleManager.cpp
diff options
context:
space:
mode:
authorMarcos Magueta <maguetamarcos@gmail.com>2026-05-18 12:50:25 -0300
committerGitHub <noreply@github.com>2026-05-18 12:50:25 -0300
commitaf8759a9c1d965a3220024d1857aea219d62d898 (patch)
tree9cf14af855cac3674f508ec001bd04e262a524ae /src/LifecycleManager.cpp
parent0fc76b99abbff1d3cef8e2f9fcd56f3ae4f23a7a (diff)
downloadRNT-af8759a9c1d965a3220024d1857aea219d62d898.tar.gz
RNT-af8759a9c1d965a3220024d1857aea219d62d898.zip
Branching and object registry rework (#11)
Diffstat (limited to 'src/LifecycleManager.cpp')
-rw-r--r--src/LifecycleManager.cpp121
1 files changed, 118 insertions, 3 deletions
diff --git a/src/LifecycleManager.cpp b/src/LifecycleManager.cpp
index a83c9e1..78e112d 100644
--- a/src/LifecycleManager.cpp
+++ b/src/LifecycleManager.cpp
@@ -1,21 +1,136 @@
#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::Unmonitor(ObjectManager::registry* object)
+ void LifecycleManager::TryCollect(ObjectManager::registry* object)
{
- if (object != nullptr && object->head->handle_count > 0)
- --object->head->handle_count;
+ 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);
}
}