pub mod agent;
pub mod cleanup;
pub mod git;
pub mod git_operations;
pub mod pool;
pub mod worktree;
#[cfg(test)]
mod tests;
pub use agent::{AgentContext, AgentResourceManager};
pub use cleanup::{CleanupPriority, CleanupRegistry, CleanupTask};
pub use git::GitOperations;
pub use pool::{PoolMetrics, ResourcePool};
pub use worktree::WorktreeResourceManager;
use crate::cook::execution::errors::MapReduceResult;
use crate::cook::orchestrator::ExecutionEnvironment;
use crate::worktree::{WorktreeManager, WorktreePool, WorktreeSession};
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct ResourceManager {
pub worktree_pool: Option<Arc<WorktreePool>>,
pub active_sessions: Arc<RwLock<HashMap<String, WorktreeSession>>>,
pub cleanup_registry: Arc<CleanupRegistry>,
pub git_ops: Arc<GitOperations>,
pub worktree_manager: Arc<WorktreeResourceManager>,
pub agent_manager: Arc<AgentResourceManager>,
metrics: Arc<ResourceMetricsTracker>,
}
impl ResourceManager {
pub fn new(worktree_pool: Option<Arc<WorktreePool>>) -> Self {
let cleanup_registry = Arc::new(CleanupRegistry::new());
let git_ops = Arc::new(GitOperations::new());
let worktree_manager = Arc::new(WorktreeResourceManager::new(worktree_pool.clone()));
let agent_manager = Arc::new(AgentResourceManager::new());
let metrics = Arc::new(ResourceMetricsTracker::new());
Self {
worktree_pool,
active_sessions: Arc::new(RwLock::new(HashMap::new())),
cleanup_registry,
git_ops,
worktree_manager,
agent_manager,
metrics,
}
}
pub fn with_worktree_manager(
worktree_pool: Option<Arc<WorktreePool>>,
worktree_manager: Arc<WorktreeManager>,
) -> Self {
let cleanup_registry = Arc::new(CleanupRegistry::new());
let git_ops = Arc::new(GitOperations::new());
let worktree_resource_manager = Arc::new(WorktreeResourceManager::with_manager(
worktree_pool.clone(),
worktree_manager,
));
let agent_manager = Arc::new(AgentResourceManager::new());
let metrics = Arc::new(ResourceMetricsTracker::new());
Self {
worktree_pool,
active_sessions: Arc::new(RwLock::new(HashMap::new())),
cleanup_registry,
git_ops,
worktree_manager: worktree_resource_manager,
agent_manager,
metrics,
}
}
pub async fn acquire_worktree_session(
&self,
agent_id: &str,
env: &ExecutionEnvironment,
) -> MapReduceResult<WorktreeSession> {
let session = self.worktree_manager.acquire_session(agent_id, env).await?;
self.register_session(agent_id.to_string(), session.clone())
.await;
self.metrics.increment_created();
Ok(session)
}
pub async fn register_session(&self, agent_id: String, session: WorktreeSession) {
let mut sessions = self.active_sessions.write().await;
sessions.insert(agent_id, session);
}
pub async fn unregister_session(&self, agent_id: &str) -> Option<WorktreeSession> {
let mut sessions = self.active_sessions.write().await;
sessions.remove(agent_id)
}
pub async fn get_active_sessions(&self) -> Vec<(String, WorktreeSession)> {
let sessions = self.active_sessions.read().await;
sessions
.iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect()
}
pub async fn cleanup_orphaned_resources(&self, worktree_names: &[String]) {
if !worktree_names.is_empty() {
self.worktree_manager
.cleanup_orphaned_worktrees(worktree_names)
.await;
for name in worktree_names {
let cleanup_task = Box::new(cleanup::WorktreeCleanupTask::new(name.clone(), None));
self.cleanup_registry.register(cleanup_task).await;
log::info!("Registered cleanup task for orphaned worktree: {}", name);
}
}
}
pub async fn cleanup_all(&self) -> MapReduceResult<()> {
let sessions = {
let mut sessions = self.active_sessions.write().await;
let all_sessions: HashMap<String, WorktreeSession> = sessions.drain().collect();
all_sessions
};
for (_agent_id, _session) in sessions {
}
self.cleanup_registry.execute_all().await?;
Ok(())
}
pub async fn get_metrics(&self) -> ResourceMetrics {
let active_sessions = self.active_sessions.read().await.len();
ResourceMetrics {
active_sessions,
total_created: self.metrics.total_created(),
total_reused: self.metrics.total_reused(),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct ResourceMetrics {
pub active_sessions: usize,
pub total_created: usize,
pub total_reused: usize,
}
struct ResourceMetricsTracker {
created: AtomicUsize,
reused: AtomicUsize,
}
impl ResourceMetricsTracker {
fn new() -> Self {
Self {
created: AtomicUsize::new(0),
reused: AtomicUsize::new(0),
}
}
fn increment_created(&self) {
self.created.fetch_add(1, Ordering::Relaxed);
}
#[allow(dead_code)]
fn increment_reused(&self) {
self.reused.fetch_add(1, Ordering::Relaxed);
}
fn total_created(&self) -> usize {
self.created.load(Ordering::Relaxed)
}
fn total_reused(&self) -> usize {
self.reused.load(Ordering::Relaxed)
}
}
pub struct ResourceGuard<T> {
resource: Option<T>,
cleanup: Option<Box<dyn FnOnce(T) + Send>>,
}
impl<T> ResourceGuard<T> {
pub fn new<F>(resource: T, cleanup: F) -> Self
where
F: FnOnce(T) + Send + 'static,
{
Self {
resource: Some(resource),
cleanup: Some(Box::new(cleanup)),
}
}
pub fn get(&self) -> Option<&T> {
self.resource.as_ref()
}
pub fn get_mut(&mut self) -> Option<&mut T> {
self.resource.as_mut()
}
pub fn take(mut self) -> Option<T> {
self.cleanup = None;
self.resource.take()
}
}
impl<T> Drop for ResourceGuard<T> {
fn drop(&mut self) {
if let (Some(resource), Some(cleanup)) = (self.resource.take(), self.cleanup.take()) {
cleanup(resource);
}
}
}
impl<T> std::ops::Deref for ResourceGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.resource
.as_ref()
.expect("Resource guard already consumed")
}
}
impl<T> std::ops::DerefMut for ResourceGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.resource
.as_mut()
.expect("Resource guard already consumed")
}
}