use crate::memory_stats_cache::MemoryStatsCache;
use crate::service::{PlasticityCommand, PlasticityConfig, PlasticityService};
use parking_lot::RwLock;
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, Mutex};
use std::thread;
use tracing::{debug, info, warn};
pub struct PlasticityLifecycleManager {
service: Arc<Mutex<Option<PlasticityService>>>,
thread_handle: Option<thread::JoinHandle<()>>,
running: Arc<AtomicBool>,
config: PlasticityConfig,
memory_stats_cache: MemoryStatsCache,
memory_area_count: Arc<RwLock<usize>>,
}
impl PlasticityLifecycleManager {
pub fn new(config: PlasticityConfig, memory_stats_cache: MemoryStatsCache) -> Self {
Self {
service: Arc::new(Mutex::new(None)),
thread_handle: None,
running: Arc::new(AtomicBool::new(false)),
config,
memory_stats_cache,
memory_area_count: Arc::new(RwLock::new(0)),
}
}
pub fn is_running(&self) -> bool {
self.running.load(Ordering::Relaxed)
}
pub fn get_memory_stats_cache(&self) -> MemoryStatsCache {
self.memory_stats_cache.clone()
}
pub fn start_if_needed(&mut self) -> bool {
if self.is_running() {
return false;
}
info!("Starting plasticity service");
let mut service_lock = self.service.lock().unwrap();
let service = PlasticityService::new(self.config.clone(), self.memory_stats_cache.clone());
let thread_handle = service.start();
self.running.store(true, Ordering::Relaxed);
*service_lock = Some(service);
drop(service_lock);
self.thread_handle = Some(thread_handle);
info!("Plasticity service started");
true
}
pub fn stop_if_needed(&mut self) -> bool {
if !self.is_running() {
return false;
}
info!("Stopping plasticity service");
let mut service_lock = self.service.lock().unwrap();
if let Some(service) = service_lock.take() {
service.stop();
}
drop(service_lock);
self.running.store(false, Ordering::Relaxed);
if let Some(handle) = self.thread_handle.take() {
let _ = handle.join();
}
info!("Plasticity service stopped");
true
}
pub fn register_memory_area(
&mut self,
area_idx: u32,
area_name: String,
temporal_depth: u32,
upstream_areas: Vec<u32>,
lifecycle_config: Option<crate::memory_neuron_array::MemoryNeuronLifecycleConfig>,
mp_learning_enabled: bool,
) {
{
let mut count = self.memory_area_count.write();
*count += 1;
debug!("Memory area registered: {} (total: {})", area_name, *count);
}
if !self.is_running() {
self.start_if_needed();
}
if let Some(service) = self.service.lock().unwrap().as_ref() {
service.register_memory_area(area_idx, area_name, temporal_depth, upstream_areas, lifecycle_config, mp_learning_enabled);
}
}
pub fn unregister_memory_area(&mut self) {
let should_stop = {
let mut count = self.memory_area_count.write();
if *count > 0 {
*count -= 1;
}
debug!("Memory area unregistered (remaining: {})", *count);
*count == 0
};
if should_stop && self.is_running() {
self.stop_if_needed();
}
}
pub fn notify_burst(&self, timestep: u64) {
if let Some(service) = self.service.lock().unwrap().as_ref() {
service.notify_burst(timestep);
}
}
pub fn drain_commands(&self) -> Vec<PlasticityCommand> {
if let Some(service) = self.service.lock().unwrap().as_ref() {
service.drain_commands()
} else {
Vec::new()
}
}
}
impl Drop for PlasticityLifecycleManager {
fn drop(&mut self) {
if self.is_running() {
warn!("PlasticityLifecycleManager dropped while service was running, stopping now");
self.stop_if_needed();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lifecycle_start_stop() {
let cache = crate::memory_stats_cache::create_memory_stats_cache();
let config = PlasticityConfig::default();
let mut manager = PlasticityLifecycleManager::new(config, cache);
assert!(!manager.is_running());
assert!(manager.start_if_needed());
assert!(manager.is_running());
assert!(!manager.start_if_needed());
assert!(manager.stop_if_needed());
assert!(!manager.is_running());
assert!(!manager.stop_if_needed());
}
#[test]
fn test_auto_start_on_register() {
let cache = crate::memory_stats_cache::create_memory_stats_cache();
let config = PlasticityConfig::default();
let mut manager = PlasticityLifecycleManager::new(config, cache);
assert!(!manager.is_running());
manager.register_memory_area(0, "mem_00".to_string(), 3, vec![1, 2], None, false);
assert!(manager.is_running());
manager.stop_if_needed();
}
}