use std::any::Any;
use std::cell::Ref;
use std::cell::RefCell;
use std::cell::RefMut;
use std::collections::HashMap;
use std::sync::LazyLock;
pub struct NonSendArena;
impl NonSendArena {
thread_local! {
static ARENA: LazyLock<NonSendArenaMap> = LazyLock::new(|| NonSendArenaMap::new());
}
pub fn with<F, R>(
&'static self,
func: impl FnOnce(&LazyLock<NonSendArenaMap>) -> R,
) -> R {
Self::ARENA.with(func)
}
pub fn insert<T: 'static>(object: T) -> NonSendRcArenaHandle<T> {
Self::ARENA.with(|arena| arena.insert(object))
}
pub fn len() -> usize { Self::ARENA.with(|arena| arena.len()) }
pub fn clear() { Self::ARENA.with(|arena| arena.clear()) }
pub fn is_empty() -> bool { Self::ARENA.with(|arena| arena.is_empty()) }
}
struct NonSendArenaEntry {
object: Box<dyn Any>,
ref_count: usize,
}
pub struct NonSendArenaMap {
objects: RefCell<HashMap<usize, NonSendArenaEntry>>,
next_id: RefCell<usize>,
}
impl NonSendArenaMap {
fn new() -> Self {
Self {
objects: RefCell::new(HashMap::new()),
next_id: RefCell::new(0),
}
}
pub fn insert<T: 'static>(&self, object: T) -> NonSendRcArenaHandle<T> {
let mut objects = self.objects.borrow_mut();
let mut next_id = self.next_id.borrow_mut();
let id = *next_id;
*next_id += 1;
let entry = NonSendArenaEntry {
object: Box::new(object),
ref_count: 1,
};
objects.insert(id, entry);
NonSendRcArenaHandle {
id,
arena: self as *const NonSendArenaMap,
_phantom: std::marker::PhantomData,
}
}
fn inc_ref(&self, id: usize) -> bool {
let mut objects = self.objects.borrow_mut();
if let Some(entry) = objects.get_mut(&id) {
entry.ref_count += 1;
true
} else {
false
}
}
fn dec_ref(&self, id: usize) {
let mut objects = self.objects.borrow_mut();
if let Some(entry) = objects.get_mut(&id) {
entry.ref_count -= 1;
if entry.ref_count == 0 {
objects.remove(&id);
}
}
}
pub fn get<H: NonSendHandle>(
&self,
handle: &H,
) -> Option<Ref<'_, H::ObjectType>> {
let objects = self.objects.borrow();
if objects.contains_key(&handle.id()) {
Ref::filter_map(objects, |map| {
map.get(&handle.id())
.and_then(|entry| entry.object.downcast_ref())
})
.ok()
} else {
None
}
}
pub fn get_mut<H: NonSendHandle>(
&self,
handle: &H,
) -> Option<RefMut<'_, H::ObjectType>> {
let objects = self.objects.borrow_mut();
RefMut::filter_map(objects, |map| {
map.get_mut(&handle.id())
.and_then(|entry| entry.object.downcast_mut::<H::ObjectType>())
})
.ok()
}
pub fn remove<H: NonSendHandle>(
&self,
handle: &H,
) -> Option<H::ObjectType> {
let mut objects = self.objects.borrow_mut();
objects
.remove(&handle.id())
.and_then(|entry| entry.object.downcast().ok())
.map(|boxed| *boxed)
}
pub fn len(&self) -> usize { self.objects.borrow().len() }
pub fn is_empty(&self) -> bool { self.objects.borrow().is_empty() }
pub fn clear(&self) { self.objects.borrow_mut().clear(); }
pub fn ref_count(&self, id: usize) -> Option<usize> {
self.objects.borrow().get(&id).map(|entry| entry.ref_count)
}
}
const PANIC_MSG: &str = r#"
Object does not exist in the NonSendArena.
It may have been manually removed by another handle, or created in a different thread.
"#;
pub trait NonSendHandle: Sized {
type ObjectType: 'static;
fn id(&self) -> usize;
fn get_arena(&self) -> &NonSendArenaMap;
fn get(&self) -> Ref<'_, Self::ObjectType> {
self.get_arena().get(self).expect(PANIC_MSG)
}
fn get_mut(&self) -> RefMut<'_, Self::ObjectType> {
self.get_arena().get_mut(self).expect(PANIC_MSG)
}
fn remove(self) -> Self::ObjectType {
self.get_arena().remove(&self).expect(PANIC_MSG)
}
fn ref_count(&self) -> usize {
self.get_arena().ref_count(self.id()).expect(PANIC_MSG)
}
}
impl<T: 'static> NonSendHandle for NonSendRcArenaHandle<T> {
type ObjectType = T;
fn id(&self) -> usize { self.id }
fn get_arena(&self) -> &NonSendArenaMap { unsafe { &*self.arena } }
}
impl<T: 'static> NonSendHandle for NonSendArenaHandle<T> {
type ObjectType = T;
fn id(&self) -> usize { self.id }
fn get_arena(&self) -> &NonSendArenaMap { unsafe { &*self.arena } }
}
pub struct NonSendRcArenaHandle<T: 'static> {
id: usize,
arena: *const NonSendArenaMap,
_phantom: std::marker::PhantomData<T>,
}
impl<T> NonSendRcArenaHandle<T> {
pub fn forget(self) -> NonSendArenaHandle<T> {
let id = self.id;
let handle = NonSendArenaHandle {
id,
arena: self.arena,
_phantom: std::marker::PhantomData,
};
std::mem::forget(self);
handle
}
}
impl<T: 'static> Clone for NonSendRcArenaHandle<T> {
fn clone(&self) -> Self {
let arena = self.get_arena();
if arena.inc_ref(self.id) {
NonSendRcArenaHandle {
id: self.id,
arena: self.arena,
_phantom: std::marker::PhantomData,
}
} else {
panic!("Attempted to clone invalid handle");
}
}
}
impl<T: 'static> Drop for NonSendRcArenaHandle<T> {
fn drop(&mut self) {
let arena = self.get_arena();
arena.dec_ref(self.id);
}
}
#[derive(Clone, Copy)]
pub struct NonSendArenaHandle<T> {
id: usize,
arena: *const NonSendArenaMap,
_phantom: std::marker::PhantomData<T>,
}
#[cfg(test)]
mod tests {
use std::rc::Rc;
use super::*;
use crate::prelude::*;
#[derive(Debug)]
struct NonSendCounter {
value: Rc<RefCell<i32>>,
name: String,
}
impl NonSendCounter {
fn new(name: String, initial_value: i32) -> Self {
Self {
value: Rc::new(RefCell::new(initial_value)),
name,
}
}
fn increment(&self) { *self.value.borrow_mut() += 1; }
fn get_value(&self) -> i32 { *self.value.borrow() }
fn get_name(&self) -> &str { &self.name }
}
impl Drop for NonSendCounter {
fn drop(&mut self) {
}
}
#[test]
fn handle_is_send() {
}
#[test]
fn test_automatic_cleanup() {
NonSendArena::clear();
{
let handle1 = NonSendArena::insert(NonSendCounter::new(
"test".to_string(),
42,
));
NonSendArena::len().xpect_eq(1);
handle1.ref_count().xpect_eq(1);
let handle2 = handle1.clone();
handle1.ref_count().xpect_eq(2);
drop(handle1);
handle2.ref_count().xpect_eq(1);
NonSendArena::len().xpect_eq(1);
{
let counter = handle2.get();
counter.get_value().xpect_eq(42);
}
}
NonSendArena::len().xpect_eq(0);
}
#[test]
fn test_multiple_objects_cleanup() {
NonSendArena::clear();
let handle1 =
NonSendArena::insert(NonSendCounter::new("first".to_string(), 1));
let handle2 =
NonSendArena::insert(NonSendCounter::new("second".to_string(), 2));
let handle3 = NonSendArena::insert("string".to_string());
NonSendArena::len().xpect_eq(3);
let handle1_clone = handle1.clone();
handle1.ref_count().xpect_eq(2);
drop(handle1);
NonSendArena::len().xpect_eq(3);
drop(handle2);
NonSendArena::len().xpect_eq(2);
drop(handle3);
NonSendArena::len().xpect_eq(1);
drop(handle1_clone);
NonSendArena::len().xpect_eq(0);
}
#[test]
fn test_manual_remove_with_clones() {
NonSendArena::clear();
let handle1 =
NonSendArena::insert(NonSendCounter::new("test".to_string(), 42));
let _handle2 = handle1.clone();
NonSendArena::len().xpect_eq(1);
handle1.ref_count().xpect_eq(2);
let removed = handle1.remove();
removed.get_name().xpect_eq("test");
NonSendArena::len().xpect_eq(0);
NonSendArena::len().xpect_eq(0);
}
#[test]
fn test_basic_arena_operations() {
NonSendArena::clear();
let counter_handle =
NonSendArena::insert(NonSendCounter::new("test".to_string(), 42));
let string_handle = NonSendArena::insert("Hello, World!".to_string());
let number_handle = NonSendArena::insert(123i32);
NonSendArena::len().xpect_eq(3);
{
let counter = counter_handle.get();
counter.get_value().xpect_eq(42);
counter.get_name().xpect_eq("test");
}
{
let string = string_handle.get();
string.as_str().xpect_eq("Hello, World!");
}
{
let number = number_handle.get();
(*number).xpect_eq(123);
}
{
let counter = counter_handle.get();
counter.increment();
counter.get_value().xpect_eq(43);
}
drop(counter_handle);
drop(string_handle);
drop(number_handle);
NonSendArena::len().xpect_eq(0);
}
#[test]
fn test_forget_functionality() {
NonSendArena::clear();
let forgotten_handle;
{
let handle = NonSendArena::insert(NonSendCounter::new(
"forgotten".to_string(),
100,
));
NonSendArena::len().xpect_eq(1);
handle.ref_count().xpect_eq(1);
forgotten_handle = handle.forget();
NonSendArena::len().xpect_eq(1);
forgotten_handle.ref_count().xpect_eq(1);
}
NonSendArena::len().xpect_eq(1);
forgotten_handle.ref_count().xpect_eq(1);
{
let counter = forgotten_handle.get();
counter.get_value().xpect_eq(100);
counter.get_name().xpect_eq("forgotten");
}
drop(forgotten_handle);
NonSendArena::len().xpect_eq(1);
NonSendArena::clear();
NonSendArena::len().xpect_eq(0);
}
#[test]
#[should_panic]
fn test_panic_on_invalid_handle_access() {
NonSendArena::clear();
let handle1 =
NonSendArena::insert(NonSendCounter::new("test".to_string(), 42));
let handle2 = handle1.clone();
let _removed = handle1.remove();
NonSendArena::len().xpect_eq(0);
let _counter = handle2.get();
}
#[test]
#[should_panic]
fn test_panic_on_invalid_handle_get_mut() {
NonSendArena::clear();
let handle1 =
NonSendArena::insert(NonSendCounter::new("test".to_string(), 42));
let handle2 = handle1.clone();
let _removed = handle1.remove();
NonSendArena::len().xpect_eq(0);
let _counter = handle2.get_mut();
}
#[test]
#[should_panic]
fn test_panic_on_invalid_handle_remove() {
NonSendArena::clear();
let handle1 =
NonSendArena::insert(NonSendCounter::new("test".to_string(), 42));
let handle2 = handle1.clone();
let _removed = handle1.remove();
NonSendArena::len().xpect_eq(0);
let _removed2 = handle2.remove();
}
}