khora_agents/ecs_agent/garbage_collector_agent.rs
1// Copyright 2025 eraflo
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! The Intelligent Subsystem Agent for garbage collection.
16
17use std::collections::VecDeque;
18
19use khora_data::ecs::{PageIndex, SemanticDomain, World};
20use khora_lanes::ecs_lane::{CompactionLane, GcWorkPlan};
21
22/// The agent responsible for managing the garbage collection of orphaned component data.
23///
24/// This agent collects orphaned data locations, decides when and how much to clean
25/// based on a strategy, and dispatches the work to a `CompactionLane`.
26pub struct GarbageCollectorAgent {
27 /// A queue of locations pointing to orphaned data that needs cleanup.
28 pending_cleanup: VecDeque<(PageIndex, SemanticDomain)>,
29 /// The worker lane that performs the actual data compaction.
30 compaction_lane: CompactionLane,
31}
32
33impl GarbageCollectorAgent {
34 /// Creates a new `GarbageCollectorAgent`.
35 pub fn new() -> Self {
36 Self {
37 pending_cleanup: VecDeque::new(),
38 compaction_lane: CompactionLane::new(),
39 }
40 }
41
42 /// Adds a new orphaned data location to the cleanup queue.
43 /// This is called by the `World`'s user after `add/remove_component`.
44 pub fn queue_cleanup(&mut self, page_index: PageIndex, domain: SemanticDomain) {
45 self.pending_cleanup.push_back((page_index, domain));
46 }
47
48 /// Runs the agent's decision-making and execution logic for one frame.
49 pub fn run(&mut self, world: &mut World) {
50 if self.pending_cleanup.is_empty() {
51 return;
52 }
53
54 // --- SAA Strategy Logic (currently a simple placeholder) ---
55 // In the future, this budget would be determined by the DCC based on system load.
56 const MAX_CLEANUP_PER_FRAME: usize = 10;
57 let budget = self.pending_cleanup.len().min(MAX_CLEANUP_PER_FRAME);
58
59 // --- Prepare Work Plan ---
60 let items_to_clean: Vec<_> = self.pending_cleanup.drain(..budget).collect();
61 let work_plan = GcWorkPlan {
62 budget,
63 items_to_clean,
64 };
65
66 // --- Dispatch to Lane ---
67 self.compaction_lane.run(world, &work_plan);
68 }
69}
70
71impl Default for GarbageCollectorAgent {
72 fn default() -> Self {
73 Self::new()
74 }
75}