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}