use crate::utils::gc::{GcPtr, GcObject, GenerationId, ObjectId, gc_alloc, gc_add_root, gc_remove_root};
use crate::eval::value::{Value, ThreadSafeEnvironment, Generation};
use crate::ast::Expr;
use crate::diagnostics::Span;
use std::collections::HashMap;
use std::sync::{Arc, RwLock, atomic::{AtomicBool, AtomicU32, Ordering}};
use std::hash::{Hash, Hasher};
#[derive(Debug, Clone)]
pub struct GcValue {
inner: GcPtr,
}
#[derive(Debug)]
struct ValueGcWrapper {
value: Value,
generation: AtomicU32,
marked: AtomicBool,
}
#[derive(Debug, Clone)]
pub struct GcEnvironment {
inner: Arc<ThreadSafeEnvironment>,
gc_metadata: Option<GcPtr>,
}
#[derive(Debug)]
pub struct GcIntegration {
root_environments: RwLock<Vec<Arc<ThreadSafeEnvironment>>>,
continuation_roots: RwLock<Vec<ObjectId>>,
macro_roots: RwLock<Vec<ObjectId>>,
config: GcIntegrationConfig,
}
#[derive(Debug, Clone)]
pub struct GcIntegrationConfig {
pub auto_register_environments: bool,
pub preserve_stack_traces: bool,
pub gc_aware_macros: bool,
pub gc_threshold_size: usize,
}
impl ValueGcWrapper {
pub fn new(value: Value) -> Self {
Self {
value,
generation: AtomicU32::new(0),
marked: AtomicBool::new(false),
}
}
pub fn value(&self) -> &Value {
&self.value
}
fn estimate_size(&self) -> usize {
match &self.value {
Value::Literal(_) => 32, Value::Symbol(_) => 16, Value::Keyword(s) => 16 + s.len(),
Value::Nil | Value::Unspecified => 8,
Value::Pair(_, _) => 64, Value::MutablePair(_, _) => 96, Value::Vector(vec) => {
if let Ok(guard) = vec.read() {
48 + guard.len() * 8 } else {
48
}
}
Value::Hashtable(map) => {
if let Ok(guard) = map.read() {
64 + guard.len() * 16 } else {
64
}
}
Value::MutableString(s) => {
if let Ok(guard) = s.read() {
32 + guard.len() * 4 } else {
32
}
}
Value::Procedure(_) => 128, Value::CaseLambda(_) => 256, Value::Primitive(_) => 64, Value::Continuation(_) => 512, Value::Syntax(_) => 128, Value::Port(_) => 256, Value::Promise(_) => 96, Value::Type(_) => 64, Value::Foreign(_) => 64, Value::ErrorObject(_) => 128, Value::CharSet(_) => 1024, Value::Parameter(_) => 96, Value::Record(_) => 128, Value::AdvancedHashTable(_) => 1024,
Value::Ideque(_) => 512,
Value::PriorityQueue(_) => 512,
Value::OrderedSet(_) => 1024,
Value::ListQueue(_) => 256,
Value::RandomAccessList(_) => 512,
Value::Set(_) => 512,
Value::Bag(_) => 512,
Value::Generator(_) => 256,
#[cfg(feature = "async-runtime")]
Value::Future(_) => 128,
#[cfg(feature = "async-runtime")]
Value::Channel(_) => 256,
#[cfg(feature = "async-runtime")]
Value::Mutex(_) => 64,
#[cfg(feature = "async-runtime")]
Value::Semaphore(_) => 64,
#[cfg(feature = "async-runtime")]
Value::AtomicCounter(_) => 32,
#[cfg(feature = "async-runtime")]
Value::DistributedNode(_) => 1024,
Value::Opaque(_) => 64,
}
}
fn collect_references(&self) -> Vec<GcPtr> {
Vec::new()
}
}
impl GcObject for ValueGcWrapper {
fn generation(&self) -> GenerationId {
self.generation.load(Ordering::Relaxed)
}
fn set_generation(&mut self, generation: GenerationId) {
self.generation.store(generation, Ordering::Relaxed);
}
fn references(&self) -> Vec<GcPtr> {
self.collect_references()
}
fn mark(&self) {
self.marked.store(true, Ordering::Relaxed);
}
fn is_marked(&self) -> bool {
self.marked.load(Ordering::Relaxed)
}
fn clear_mark(&self) {
self.marked.store(false, Ordering::Relaxed);
}
fn size_hint(&self) -> usize {
self.estimate_size()
}
}
impl GcValue {
pub fn new(value: Value) -> Self {
let wrapper = ValueGcWrapper::new(value);
let inner = gc_alloc(wrapper);
Self { inner }
}
pub fn value(&self) -> &Value {
let any_ref = &*self.inner as &dyn std::any::Any;
if let Some(wrapper) = any_ref.downcast_ref::<ValueGcWrapper>() {
wrapper.value()
} else {
panic!("GcValue contains invalid wrapper type")
}
}
pub fn into_value(self) -> Value {
self.value().clone()
}
pub fn from_value_conditional(value: Value, config: &GcIntegrationConfig) -> Result<Self, Value> {
let wrapper = ValueGcWrapper::new(value);
if wrapper.estimate_size() >= config.gc_threshold_size {
Ok(Self {
inner: gc_alloc(wrapper),
})
} else {
Err(wrapper.value.clone())
}
}
pub fn add_as_root(&self) {
gc_add_root(&self.inner);
}
pub fn remove_from_roots(&self) {
gc_remove_root(&self.inner);
}
pub fn gc_id(&self) -> ObjectId {
self.inner.id()
}
}
impl GcEnvironment {
pub fn new(env: Arc<ThreadSafeEnvironment>) -> Self {
Self {
inner: env,
gc_metadata: None,
}
}
pub fn inner(&self) -> &Arc<ThreadSafeEnvironment> {
&self.inner
}
pub fn register_as_root(&mut self, integration: &GcIntegration) {
if let Ok(mut roots) = integration.root_environments.write() {
roots.push(self.inner.clone());
}
}
pub fn unregister_from_roots(&self, integration: &GcIntegration) {
if let Ok(mut roots) = integration.root_environments.write() {
roots.retain(|env| !Arc::ptr_eq(env, &self.inner));
}
}
pub fn scan_for_gc_roots(&self) -> Vec<Value> {
let mut roots = Vec::new();
let var_names = self.inner.all_variable_names();
for var_name in var_names {
if let Some(value) = self.inner.lookup(&var_name) {
roots.push(value);
}
}
roots
}
}
impl GcIntegration {
pub fn new(config: GcIntegrationConfig) -> Self {
Self {
root_environments: RwLock::new(Vec::new()),
continuation_roots: RwLock::new(Vec::new()),
macro_roots: RwLock::new(Vec::new()),
config,
}
}
pub fn with_default_config() -> Self {
Self::new(GcIntegrationConfig::default())
}
pub fn scan_environment_roots(&self) -> Vec<Value> {
let mut all_roots = Vec::new();
if let Ok(environments) = self.root_environments.read() {
for env in environments.iter() {
let gc_env = GcEnvironment::new(env.clone());
all_roots.extend(gc_env.scan_for_gc_roots());
}
}
all_roots
}
pub fn register_continuation_root(&self, continuation_id: ObjectId) {
if let Ok(mut roots) = self.continuation_roots.write() {
roots.push(continuation_id);
}
}
pub fn unregister_continuation_root(&self, continuation_id: ObjectId) {
if let Ok(mut roots) = self.continuation_roots.write() {
roots.retain(|&id| id != continuation_id);
}
}
pub fn register_macro_root(&self, macro_id: ObjectId) {
if let Ok(mut roots) = self.macro_roots.write() {
roots.push(macro_id);
}
}
pub fn comprehensive_root_scan(&self) -> GcRootScanResult {
let environment_roots = self.scan_environment_roots();
let continuation_count = self.continuation_roots.read()
.map(|roots| roots.len()).unwrap_or(0);
let macro_count = self.macro_roots.read()
.map(|roots| roots.len()).unwrap_or(0);
GcRootScanResult {
environment_roots,
continuation_root_count: continuation_count,
macro_root_count: macro_count,
}
}
pub fn should_use_gc_for_size(&self, estimated_size: usize) -> bool {
estimated_size >= self.config.gc_threshold_size
}
pub fn config(&self) -> &GcIntegrationConfig {
&self.config
}
}
#[derive(Debug)]
pub struct GcRootScanResult {
pub environment_roots: Vec<Value>,
pub continuation_root_count: usize,
pub macro_root_count: usize,
}
impl Default for GcIntegrationConfig {
fn default() -> Self {
Self {
auto_register_environments: true,
preserve_stack_traces: true,
gc_aware_macros: true,
gc_threshold_size: 256, }
}
}
impl PartialEq for GcValue {
fn eq(&self, other: &Self) -> bool {
self.value() == other.value()
}
}
impl Eq for GcValue {}
impl Hash for GcValue {
fn hash<H: Hasher>(&self, state: &mut H) {
self.value().hash(state);
}
}
impl std::fmt::Display for GcValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.value())
}
}
unsafe impl Send for GcValue {}
unsafe impl Sync for GcValue {}
pub fn maybe_gc_alloc(value: Value, integration: &GcIntegration) -> Value {
if integration.should_use_gc_for_size(
ValueGcWrapper::new(value.clone()).estimate_size()
) {
value
} else {
value
}
}
pub fn scan_value_for_gc_integration(value: &Value) -> Vec<Value> {
let mut references = Vec::new();
match value {
Value::Pair(car, cdr) => {
references.push((**car).clone());
references.push((**cdr).clone());
}
Value::MutablePair(car_ref, cdr_ref) => {
if let (Ok(car), Ok(cdr)) = (car_ref.read(), cdr_ref.read()) {
references.push(car.clone());
references.push(cdr.clone());
}
}
Value::Vector(vec_ref) => {
if let Ok(vec) = vec_ref.read() {
references.extend(vec.iter().cloned());
}
}
Value::Hashtable(map_ref) => {
if let Ok(map) = map_ref.read() {
for (key, value) in map.iter() {
references.push(key.clone());
references.push(value.clone());
}
}
}
Value::Procedure(proc) => {
}
_ => {}
}
references
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eval::value::Value;
#[test]
fn test_gc_value_creation() {
let original_value = Value::integer(42);
let gc_value = GcValue::new(original_value.clone());
assert_eq!(gc_value.value(), &original_value);
assert_eq!(gc_value.into_value(), original_value);
}
#[test]
fn test_gc_integration_config() {
let config = GcIntegrationConfig::default();
let integration = GcIntegration::new(config);
assert!(!integration.should_use_gc_for_size(100));
assert!(integration.should_use_gc_for_size(1000));
}
#[test]
fn test_value_size_estimation() {
let wrapper = ValueGcWrapper::new(Value::integer(42));
assert!(wrapper.estimate_size() > 0);
let big_vector = Value::vector(vec![Value::integer(1); 100]);
let big_wrapper = ValueGcWrapper::new(big_vector);
assert!(big_wrapper.estimate_size() > wrapper.estimate_size());
}
#[test]
fn test_gc_environment_root_scanning() {
use crate::eval::value::ThreadSafeEnvironment;
let env = Arc::new(ThreadSafeEnvironment::new(None, 0));
env.define("test-var".to_string(), Value::integer(42));
env.define("test-string".to_string(), Value::string("hello"));
let gc_env = GcEnvironment::new(env);
let roots = gc_env.scan_for_gc_roots();
assert_eq!(roots.len(), 2);
assert!(roots.iter().any(|v| matches!(v, Value::Literal(lit) if lit.to_i64() == Some(42))));
}
}