use cycle_ptr::prelude::*;
use cycle_ptr::sync::{GcMtMemberPtr, GcMtPtr, GenerationRef, Metadata, Weak};
use std::num::NonZero;
use std::sync::{Condvar, Mutex};
use std::thread;
const GENERATIONS: usize = 37;
const SIZE: usize = 100;
struct Xs {
generations: Vec<GenerationRef>,
vec_of_x: Mutex<(Vec<GcMtPtr<Mutex<X>>>, usize)>,
vec_of_x_cnd: Condvar,
vec_of_weak_x: Mutex<(Vec<Weak<Mutex<X>>>, usize)>,
vec_of_weak_x_cnd: Condvar,
}
impl Xs {
fn append(&self, x: GcMtPtr<Mutex<X>>) {
self.vec_of_weak_x
.lock()
.unwrap()
.0
.push(GcMtPtr::downgrade(&x));
self.vec_of_weak_x_cnd.notify_one();
self.vec_of_x.lock().unwrap().0.push(x);
self.vec_of_x_cnd.notify_one();
}
}
impl Default for Xs {
fn default() -> Self {
let generations = (0..GENERATIONS).map(|_| GenerationRef::default()).collect();
Self {
generations,
vec_of_x: Mutex::default(),
vec_of_x_cnd: Condvar::default(),
vec_of_weak_x: Mutex::default(),
vec_of_weak_x_cnd: Condvar::default(),
}
}
}
struct X {
gc_metadata: Metadata,
vec_of_y: Vec<YPtr>,
}
impl X {
fn new(generation: &GenerationRef) -> GcMtPtr<Mutex<Self>> {
generation.make(|gc_metadata| {
Mutex::new(X {
gc_metadata,
vec_of_y: Vec::new(),
})
})
}
fn append(&mut self, y: GcMtPtr<Mutex<Y>>) {
self.vec_of_y.push(self.gc_metadata.new_pointer(y));
}
}
struct Y {
gc_metadata: Metadata,
vec_of_z: Vec<ZPtr>,
}
impl Y {
fn new(generation: &GenerationRef) -> GcMtPtr<Mutex<Self>> {
generation.make(|gc_metadata| {
Mutex::new(Y {
gc_metadata,
vec_of_z: Vec::new(),
})
})
}
fn append(&mut self, z: GcMtPtr<Mutex<Z>>) {
self.vec_of_z.push(self.gc_metadata.new_pointer(z));
}
}
struct Z {
#[expect(dead_code, reason = "we don't use it, but we just like the symmetry")]
gc_metadata: Metadata,
}
impl Z {
fn new(generation: &GenerationRef) -> GcMtPtr<Mutex<Self>> {
generation.make(|gc_metadata| Mutex::new(Z { gc_metadata }))
}
}
type YPtr = GcMtMemberPtr<Mutex<Y>>;
type ZPtr = GcMtMemberPtr<Mutex<Z>>;
fn produce_thread(xs: &Xs) {
for i in 0..SIZE {
let generation = &xs.generations[i % xs.generations.len()];
let x = X::new(generation);
for _ in 0..SIZE {
let y = Y::new(generation);
x.lock().unwrap().append(y.clone());
for _ in 0..SIZE {
let z = Z::new(generation);
y.lock().unwrap().append(z);
}
}
xs.append(x);
}
{
let mut vec = xs.vec_of_x.lock().unwrap();
vec.1 = vec.1.strict_sub(1);
xs.vec_of_x_cnd.notify_all();
}
{
let mut vec = xs.vec_of_weak_x.lock().unwrap();
vec.1 = vec.1.strict_sub(1);
xs.vec_of_weak_x_cnd.notify_all();
}
eprintln!("produce {:?} done", thread::current().id());
}
fn consume_thread(xs: &Xs) {
loop {
let mut vec_of_x = xs
.vec_of_x_cnd
.wait_while(xs.vec_of_x.lock().unwrap(), |vec| {
vec.0.is_empty() && vec.1 != 0
})
.unwrap();
if let Some(x) = vec_of_x.0.pop() {
drop(vec_of_x); drop(x);
} else if vec_of_x.1 == 0 {
break;
}
}
eprintln!("consume {:?} done", thread::current().id());
}
fn weak_promote_thread(xs: &Xs) {
loop {
let mut vec_of_weak_x = xs
.vec_of_weak_x_cnd
.wait_while(xs.vec_of_weak_x.lock().unwrap(), |vec| {
vec.0.is_empty() && vec.1 != 0
})
.unwrap();
if let Some(weak_x) = vec_of_weak_x.0.pop() {
drop(vec_of_weak_x); if let Ok(x) = Weak::upgrade(&weak_x) {
xs.vec_of_x.lock().unwrap().0.push(x);
xs.vec_of_x_cnd.notify_one();
}
} else if vec_of_weak_x.1 == 0 {
break;
}
}
{
let mut vec = xs.vec_of_x.lock().unwrap();
vec.1 = vec.1.strict_sub(1);
xs.vec_of_x_cnd.notify_all();
}
eprintln!("weak promote {:?} done", thread::current().id());
}
fn main() {
let num_threads: usize = NonZero::new(
thread::available_parallelism()
.map(|x| x.into())
.unwrap_or(4_usize)
/ 3,
)
.map(|x| x.into())
.unwrap_or(1);
let xs = Xs::default();
xs.vec_of_x.lock().unwrap().1 = 2 * num_threads;
xs.vec_of_weak_x.lock().unwrap().1 = num_threads;
thread::scope(|scope| {
for _ in 0..num_threads {
scope.spawn(|| consume_thread(&xs));
scope.spawn(|| weak_promote_thread(&xs));
scope.spawn(|| produce_thread(&xs));
}
});
}