use std::collections::{BTreeMap, HashMap};
use std::sync::{Arc, Mutex};
use rusty_v8 as v8;
use std::time::{Duration, Instant};
use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub struct ContextPoolStats {
pub created_count: u64,
pub reused_count: u64,
pub active_contexts: usize,
pub pool_size: usize,
pub avg_reuse_time_ms: f64,
}
impl Default for ContextPoolStats {
fn default() -> Self {
Self {
created_count: 0,
reused_count: 0,
active_contexts: 0,
pool_size: 0,
avg_reuse_time_ms: 0.0,
}
}
}
#[derive(Debug)]
struct ReusableContext {
isolate: v8::OwnedIsolate,
context: v8::Global<v8::Context>,
created_at: Instant,
reuse_count: u32,
last_used: Instant,
}
impl ReusableContext {
fn new(mut isolate: v8::OwnedIsolate, context: v8::Global<v8::Context>) -> Self {
let now: _ = Instant::now();
Self {
isolate,
context,
created_at: now,
reuse_count: 0,
last_used: now,
}
}
fn reset(&mut self) {
self.last_used = Instant::now();
self.reuse_count += 1;
}
fn is_stale(&self, max_age: Duration) -> bool {
self.created_at.elapsed() > max_age
}
}
pub struct V8ContextPool {
pool: Arc<Mutex<VecDeque<ReusableContext>>>,
hot_pool: Arc<Mutex<VecDeque<ReusableContext>>>,
max_pool_size: usize,
max_hot_pool_size: usize,
max_context_age: Duration,
optimization_level: OptimizationLevel,
stats: Arc<Mutex<ContextPoolStats>>,
init_time_saved: Arc<Mutex<Duration>>,
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub enum OptimizationLevel {
None = 0,
Basic = 1,
Aggressive = 2,
Maximum = 3,
}
impl V8ContextPool {
pub fn new(
max_pool_size: usize,
max_context_age: Duration,
) -> Self {
let hot_size: _ = std::cmp::min(4, max_pool_size); Self {
pool: Arc::new(Mutex::new(VecDeque::new())),
hot_pool: Arc::new(Mutex::new(VecDeque::new())),
max_pool_size,
max_hot_pool_size: hot_size,
max_context_age,
optimization_level: OptimizationLevel::Aggressive,
stats: Arc::new(Mutex::new(ContextPoolStats::default())),
init_time_saved: Arc::new(Mutex::new(Duration::default())),
}
}
pub fn new_with_level(
max_pool_size: usize,
max_context_age: Duration,
level: OptimizationLevel,
) -> Self {
let hot_size: _ = match level {
OptimizationLevel::None => 0,
OptimizationLevel::Basic => 2,
OptimizationLevel::Aggressive => std::cmp::min(4, max_pool_size),
OptimizationLevel::Maximum => std::cmp::min(8, max_pool_size),
};
Self {
pool: Arc::new(Mutex::new(VecDeque::new())),
hot_pool: Arc::new(Mutex::new(VecDeque::new())),
max_pool_size,
max_hot_pool_size: hot_size,
max_context_age,
optimization_level: level,
stats: Arc::new(Mutex::new(ContextPoolStats::default())),
init_time_saved: Arc::new(Mutex::new(Duration::default())),
}
}
pub fn initialize(&self, runtime: &RuntimeLite, initial_size: usize) -> Result<()> {
let init_start: _ = Instant::now();
let warm_size: _ = match self.optimization_level {
OptimizationLevel::None => 0,
OptimizationLevel::Basic => std::cmp::min(2, initial_size),
OptimizationLevel::Aggressive => std::cmp::min(4, initial_size),
OptimizationLevel::Maximum => std::cmp::min(8, initial_size),
};
if warm_size > 0 {
let mut hot_pool = self.hot_pool.lock().unwrap();
for _ in 0..warm_size {
match self.create_context_minimal(runtime) {
Ok((isolate, context)) => {
hot_pool.push_back(ReusableContext::new(isolate, context));
}
Err(e) => {
eprintln!("⚠️ Failed to pre-warm context: {:?}", e);
}
}
}
}
if initial_size > warm_size {
let mut pool = self.pool.lock().unwrap();
for _ in warm_size..initial_size {
match self.create_context_minimal(runtime) {
Ok((isolate, context)) => {
pool.push_back(ReusableContext::new(isolate, context));
}
Err(e) => {
eprintln!("⚠️ Failed to pre-warm context: {:?}", e);
}
}
}
}
let init_time: _ = init_start.elapsed();
let mut stats = self.stats.lock().unwrap();
stats.pool_size = self.hot_pool.lock().unwrap().len() + self.pool.lock().unwrap().len();
stats.created_count = stats.pool_size as u64;
if stats.pool_size > 0 {
eprintln!("🚀 Initialized V8 Context Pool: {} hot + {} regular contexts in {:?}",
self.hot_pool.lock().unwrap().len(),
self.pool.lock().unwrap().len(),
init_time);
}
Ok(())
}
pub fn get_hot_context(&self) -> Option<(v8::OwnedIsolate, v8::Global<v8::Context>)> {
if self.optimization_level == OptimizationLevel::None {
return None;
}
let mut hot_pool = self.hot_pool.lock().unwrap();
if let Some(mut ctx) = hot_pool.pop_front() {
ctx.reset();
{
let mut stats = self.stats.lock().unwrap();
stats.reused_count += 1;
}
Some((ctx.isolate, ctx.context))
} else {
None
}
}
pub fn get_context(&self, runtime: &RuntimeLite) -> Result<(v8::OwnedIsolate, v8::Global<v8::Context>)> {
if let Some((isolate, context)) = self.get_hot_context() {
return Ok((isolate, context));
}
let mut pool = self.pool.lock().unwrap();
if let Some(mut ctx) = pool.pop_front() {
ctx.reset();
let mut stats = self.stats.lock().unwrap();
stats.reused_count += 1;
return Ok((ctx.isolate, ctx.context));
}
let (isolate, context) = if self.optimization_level >= OptimizationLevel::Aggressive {
self.create_context_minimal(runtime)?
} else {
self.create_context(runtime)?
};
let mut stats = self.stats.lock().unwrap();
stats.created_count += 1;
Ok((isolate, context))
}
pub fn return_context(&self, isolate: v8::OwnedIsolate, context: v8::Global<v8::Context>) {
if self.optimization_level == OptimizationLevel::None {
return;
}
let ctx: _ = ReusableContext::new(isolate, context);
let mut hot_pool = self.hot_pool.lock().unwrap();
if hot_pool.len() < self.max_hot_pool_size {
hot_pool.push_back(ctx);
return;
}
let mut pool = self.pool.lock().unwrap();
if pool.len() < self.max_pool_size {
pool.push_back(ctx);
}
}
fn create_context_minimal(&self, _runtime: &RuntimeLite) -> Result<(v8::OwnedIsolate, v8::Global<v8::Context>)> {
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
let context_global: _ = {
let mut scope = v8::HandleScope::new(&mut isolate);
let context: _ = v8::Context::new(&mut scope);
v8::Global::new(&mut scope, context)
};
Ok((isolate, context_global))
}
fn create_context(&self, runtime: &RuntimeLite) -> Result<(v8::OwnedIsolate, v8::Global<v8::Context>)> {
let mut isolate = v8::Isolate::new(v8::CreateParams::default());
let context_global: _ = {
let mut scope = v8::HandleScope::new(&mut isolate);
let context: _ = v8::Context::new(&mut scope);
let mut context_scope = v8::ContextScope::new(&mut scope, context);
if self.optimization_level >= OptimizationLevel::Basic {
if let Err(e) = crate::web_api::init_web_api(&mut context_scope, &context) {
let _: _ = e;
}
}
v8::Global::new(&mut context_scope, context)
};
Ok((isolate, context_global))
}
pub fn get_stats(&self) -> ContextPoolStats {
let stats: _ = self.stats.lock().unwrap();
let init_saved: _ = self.init_time_saved.lock().unwrap();
let hot_pool_size: _ = self.hot_pool.lock().unwrap().len();
let pool_size: _ = self.pool.lock().unwrap().len();
ContextPoolStats {
created_count: stats.created_count,
reused_count: stats.reused_count,
active_contexts: hot_pool_size + pool_size,
pool_size: hot_pool_size + pool_size,
avg_reuse_time_ms: if stats.reused_count > 0 {
init_saved.as_secs_f64() * 1000.0 / stats.reused_count as f64
} else {
0.0
},
}
}
pub fn optimization_level(&self) -> OptimizationLevel {
self.optimization_level
}
pub fn set_optimization_level(&mut self, level: OptimizationLevel) {
self.optimization_level = level;
}
pub fn cleanup(&self) -> usize {
let mut hot_pool = self.hot_pool.lock().unwrap();
let mut pool = self.pool.lock().unwrap();
let hot_before: _ = hot_pool.len();
let before: _ = pool.len();
hot_pool.retain(|ctx| !ctx.is_stale(self.max_context_age));
pool.retain(|ctx| !ctx.is_stale(self.max_context_age));
let hot_removed: _ = hot_before - hot_pool.len();
let removed: _ = before - pool.len();
if removed > 0 || hot_removed > 0 {
eprintln!("🧹 Context pool cleanup: removed {} hot + {} regular stale contexts",
hot_removed, removed);
}
removed + hot_removed
}
pub fn len(&self) -> usize {
self.hot_pool.lock().unwrap().len() + self.pool.lock().unwrap().len()
}
pub fn is_empty(&self) -> bool {
self.hot_pool.lock().unwrap().is_empty() && self.pool.lock().unwrap().is_empty()
}
}
impl Default for V8ContextPool {
fn default() -> Self {
Self::new(8, Duration::from_secs(300))
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_context_pool_creation() {
let pool: _ = V8ContextPool::default();
assert_eq!(pool.len(), 0);
assert!(pool.is_empty());
}
}