Skip to content

Instantly share code, notes, and snippets.

@zicklag
Created October 20, 2023 16:18
Show Gist options
  • Save zicklag/ac91f5a9af3832f10baa3613756a7092 to your computer and use it in GitHub Desktop.
Save zicklag/ac91f5a9af3832f10baa3613756a7092 to your computer and use it in GitHub Desktop.
Arc<AtomicCell<T>> Static Lock
use std::ptr::NonNull;
use bones_framework::prelude::borrow::{AtomicBorrow, AtomicBorrowMut};
use crate::prelude::*;
/// Extension trait over [`Arc<AtomicCell>`] to allow obtaining a `'static` borrow guard.
pub trait ArcAtomicCellExt<T> {
fn lock_static(self) -> ArcRef<T>;
fn lock_static_mut(self) -> ArcRefMut<T>;
}
impl<T> ArcAtomicCellExt<T> for Arc<AtomicCell<T>> {
fn lock_static(self) -> ArcRef<T> {
let reflock = self.borrow();
let (ptr, borrow) = Ref::into_split(reflock);
// SOUND: This transmutes the lifetime of the atomic borrow to 'static, so that we can
// store it without the lifetime. This is sound because it's reference refers to the arc
// that we are storing along with it. As long as we drop the borrow before we drop the
// arc, we should be fine.
// TODO: get some review to make sure that's correct.
let borrow =
unsafe { std::mem::transmute::<AtomicBorrow<'_>, AtomicBorrow<'static>>(borrow) };
ArcRef {
arc: self,
ptr,
borrow,
}
}
fn lock_static_mut(self) -> ArcRefMut<T> {
let reflock = self.borrow_mut();
let (ptr, borrow) = RefMut::into_split(reflock);
// SOUND: This transmutes the lifetime of the atomic borrow to 'static, so that we can
// store it without the lifetime. This is sound because it's reference refers to the arc
// that we are storing along with it. As long as we drop the borrow before we drop the
// arc, we should be fine.
// TODO: get some review to make sure that's correct.
let borrow =
unsafe { std::mem::transmute::<AtomicBorrowMut<'_>, AtomicBorrowMut<'static>>(borrow) };
ArcRefMut {
arc: self,
ptr,
borrow,
}
}
}
/// A borrow lock on an [`Arc<AtomicCell>`].
pub struct ArcRef<T: ?Sized> {
arc: Arc<AtomicCell<T>>,
ptr: NonNull<T>,
borrow: AtomicBorrow<'static>,
}
unsafe impl<T: Sync + Send + ?Sized> Sync for ArcRef<T> {}
unsafe impl<T: Send + Send + ?Sized> Send for ArcRef<T> {}
impl<T: ?Sized> std::ops::Deref for ArcRef<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// SOUND: We hold an atomic borrow
unsafe { self.ptr.as_ref() }
}
}
/// A mutable borrow lock on an [`Arc<AtomicCell>`].
pub struct ArcRefMut<T: ?Sized> {
arc: Arc<AtomicCell<T>>,
ptr: NonNull<T>,
borrow: AtomicBorrowMut<'static>,
}
unsafe impl<T: Sync + Send + ?Sized> Sync for ArcRefMut<T> {}
unsafe impl<T: Send + Send + ?Sized> Send for ArcRefMut<T> {}
impl<T: ?Sized> std::ops::Deref for ArcRefMut<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
// SOUND: We hold an atomic borrow
unsafe { self.ptr.as_ref() }
}
}
impl<T: ?Sized> std::ops::DerefMut for ArcRefMut<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
// SOUND: we hold an atomic borrow
unsafe { self.ptr.as_mut() }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment