use std::marker::PhantomData;
use std::ptr::NonNull;
const HANDLE_BLOCK_CAPACITY: usize = 1_024;
struct HandleBlock {
slots: Vec<*mut u8>,
}
unsafe impl Send for HandleBlock {}
impl HandleBlock {
fn new() -> Self {
Self {
slots: Vec::with_capacity(HANDLE_BLOCK_CAPACITY),
}
}
fn is_full(&self) -> bool {
self.slots.len() >= HANDLE_BLOCK_CAPACITY
}
fn push(&mut self, ptr: *mut u8) {
self.slots.push(ptr);
}
}
pub struct HandleScope<'isolate> {
blocks: Vec<HandleBlock>,
_isolate: PhantomData<&'isolate mut ()>,
}
unsafe impl<'isolate> Send for HandleScope<'isolate> {}
impl<'isolate> HandleScope<'isolate> {
pub fn new(_isolate: &'isolate mut ()) -> Self {
Self {
blocks: Vec::new(),
_isolate: PhantomData,
}
}
fn push_handle(&mut self, ptr: *mut u8) {
if self.blocks.last().is_none_or(|b| b.is_full()) {
self.blocks.push(HandleBlock::new());
}
self.blocks
.last_mut()
.expect("block stack is non-empty; a block was just pushed if needed")
.push(ptr);
}
pub unsafe fn create_local<T>(&mut self, ptr: NonNull<T>) -> Local<'isolate, T> {
self.push_handle(ptr.as_ptr() as *mut u8);
Local {
ptr,
_scope: PhantomData,
}
}
pub fn open_child_scope<'parent>(&'parent mut self) -> HandleScope<'parent>
where
'isolate: 'parent,
{
HandleScope {
blocks: Vec::new(),
_isolate: PhantomData,
}
}
pub fn raw_handles(&self) -> impl Iterator<Item = *mut u8> + '_ {
self.blocks.iter().flat_map(|b| b.slots.iter().copied())
}
}
#[derive(Copy, Clone)]
pub struct Local<'scope, T> {
ptr: NonNull<T>,
_scope: PhantomData<&'scope T>,
}
impl<'scope, T> Local<'scope, T> {
pub unsafe fn as_ref(&self) -> &'scope T {
unsafe { self.ptr.as_ref() }
}
pub fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
}
pub struct PersistentRoots {
roots: Vec<Option<*mut u8>>,
}
unsafe impl Send for PersistentRoots {}
impl PersistentRoots {
pub fn new() -> Self {
Self { roots: Vec::new() }
}
fn register(&mut self, ptr: *mut u8) -> usize {
if let Some(idx) = self.roots.iter().position(|s| s.is_none()) {
self.roots[idx] = Some(ptr);
return idx;
}
let idx = self.roots.len();
self.roots.push(Some(ptr));
idx
}
fn unregister(&mut self, index: usize) {
if let Some(slot) = self.roots.get_mut(index) {
*slot = None;
}
}
pub fn iter_roots(&self) -> impl Iterator<Item = *mut u8> + '_ {
self.roots.iter().filter_map(|s| *s)
}
}
impl Default for PersistentRoots {
fn default() -> Self {
Self::new()
}
}
pub struct Persistent<T> {
ptr: NonNull<T>,
roots: *mut PersistentRoots,
index: Option<usize>,
_marker: PhantomData<T>,
}
unsafe impl<T: Send> Send for Persistent<T> {}
impl<T> Persistent<T> {
pub unsafe fn from_local<'scope>(local: Local<'scope, T>, roots: &mut PersistentRoots) -> Self {
let ptr = local.ptr;
let index = roots.register(ptr.as_ptr() as *mut u8);
Self {
ptr,
roots,
index: Some(index),
_marker: PhantomData,
}
}
pub fn reset(&mut self) {
if let Some(idx) = self.index.take() {
unsafe { (*self.roots).unregister(idx) };
}
}
pub fn is_empty(&self) -> bool {
self.index.is_none()
}
pub unsafe fn as_ref(&self) -> &T {
unsafe { self.ptr.as_ref() }
}
pub fn as_ptr(&self) -> *mut T {
self.ptr.as_ptr()
}
}
impl<T> Drop for Persistent<T> {
fn drop(&mut self) {
self.reset();
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_isolate() {}
#[test]
fn local_lifetime_tied_to_scope() {
make_isolate();
let mut unit = ();
let mut scope = HandleScope::new(&mut unit);
let value: u32 = 99;
let mut boxed = Box::new(value);
let ptr = NonNull::new(boxed.as_mut() as *mut u32).unwrap();
let local = unsafe { scope.create_local(ptr) };
assert_eq!(unsafe { *local.as_ref() }, 99);
}
#[test]
fn handle_scope_uses_blocks_for_storage() {
let mut unit = ();
let mut scope = HandleScope::new(&mut unit);
let mut values: Vec<u32> = (0..10).collect();
for v in &mut values {
let ptr = NonNull::new(v as *mut u32).unwrap();
unsafe { scope.create_local(ptr) };
}
assert_eq!(scope.raw_handles().count(), 10);
}
#[test]
fn nested_scopes_inner_handles_tracked_separately() {
let mut unit = ();
let mut outer_scope = HandleScope::new(&mut unit);
let mut v_outer: u32 = 42;
let ptr_outer = NonNull::new(&mut v_outer as *mut u32).unwrap();
let local_outer = unsafe { outer_scope.create_local(ptr_outer) };
assert_eq!(outer_scope.raw_handles().count(), 1);
{
let mut inner_scope = outer_scope.open_child_scope();
let mut v_inner: u32 = 7;
let ptr_inner = NonNull::new(&mut v_inner as *mut u32).unwrap();
let local_inner = unsafe { inner_scope.create_local(ptr_inner) };
assert_eq!(unsafe { *local_inner.as_ref() }, 7);
assert_eq!(inner_scope.raw_handles().count(), 1);
}
assert_eq!(outer_scope.raw_handles().count(), 1);
assert_eq!(unsafe { *local_outer.as_ref() }, 42);
}
#[test]
fn persistent_root_is_tracked() {
make_isolate();
let mut roots = PersistentRoots::new();
let value: u32 = 42;
let mut boxed = Box::new(value);
let ptr = NonNull::new(boxed.as_mut() as *mut u32).unwrap();
let persistent = {
let mut unit = ();
let mut scope = HandleScope::new(&mut unit);
let local = unsafe { scope.create_local(ptr) };
unsafe { Persistent::from_local(local, &mut roots) }
};
let live_roots: Vec<_> = roots.iter_roots().collect();
assert_eq!(live_roots.len(), 1);
assert_eq!(live_roots[0], boxed.as_mut() as *mut u32 as *mut u8);
drop(persistent);
assert_eq!(roots.iter_roots().count(), 0);
}
#[test]
fn persistent_explicit_reset_clears_root() {
let mut roots = PersistentRoots::new();
let mut value: u32 = 55;
let ptr = NonNull::new(&mut value as *mut u32).unwrap();
let mut unit = ();
let mut scope = HandleScope::new(&mut unit);
let local = unsafe { scope.create_local(ptr) };
let mut persistent = unsafe { Persistent::from_local(local, &mut roots) };
assert!(!persistent.is_empty());
assert_eq!(roots.iter_roots().count(), 1);
persistent.reset();
assert!(persistent.is_empty());
assert_eq!(roots.iter_roots().count(), 0);
persistent.reset();
drop(persistent);
assert_eq!(roots.iter_roots().count(), 0);
}
#[test]
fn persistent_roots_reuses_freed_slots() {
let mut roots = PersistentRoots::new();
let mut v1: u8 = 1;
let mut v2: u8 = 2;
let p1 = NonNull::new(&mut v1 as *mut u8).unwrap();
let p2 = NonNull::new(&mut v2 as *mut u8).unwrap();
let idx1 = roots.register(p1.as_ptr());
roots.unregister(idx1);
let idx2 = roots.register(p2.as_ptr());
assert_eq!(idx1, idx2);
}
}