use core::cell::UnsafeCell;
use core::marker::PhantomPinned;
use core::pin::Pin;
use core::sync::atomic::{AtomicUsize, Ordering};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[repr(align(2))]
#[derive(Clone, Copy)]
struct OwnerIDTarget {
_data: u16,
}
const MAGIC_OWNER_ID_TARGET: OwnerIDTarget = OwnerIDTarget { _data: 0xCE11 };
#[cold]
#[inline(never)]
fn bad_owner_panic() -> ! {
panic!("QCell accessed with incorrect owner");
}
macro_rules! owner_check {
($owner:expr $(, $qcell:expr)+) => {
$(
if $qcell.owner.0 != $owner.id().0 {
bad_owner_panic();
}
)+
}
}
#[cold]
#[inline(never)]
fn not_distinct_panic() -> ! {
panic!("Illegal to borrow same QCell twice with rw2() or rw3()");
}
macro_rules! distinct_check {
($qc1:expr, $qc2:expr) => {{
let qc1 = $qc1 as *const _ as *const () as usize;
let qc2 = $qc2 as *const _ as *const () as usize;
if qc1 == qc2 {
not_distinct_panic();
}
}};
($qc1:expr, $qc2:expr, $qc3:expr) => {{
let qc1 = $qc1 as *const _ as *const () as usize;
let qc2 = $qc2 as *const _ as *const () as usize;
let qc3 = $qc3 as *const _ as *const () as usize;
if qc1 == qc2 || qc2 == qc3 || qc3 == qc1 {
not_distinct_panic();
}
}};
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct QCellOwnerID(usize);
impl QCellOwnerID {
pub fn cell<T>(self, value: T) -> QCell<T> {
QCell {
value: UnsafeCell::new(value),
owner: self,
}
}
}
#[cfg(feature = "alloc")]
impl From<&QCellOwner> for QCellOwnerID {
fn from(owner: &QCellOwner) -> Self {
owner.id()
}
}
impl From<&QCellOwnerSeq> for QCellOwnerID {
fn from(owner: &QCellOwnerSeq) -> Self {
owner.id()
}
}
impl From<Pin<&QCellOwnerPinned>> for QCellOwnerID {
fn from(owner: Pin<&QCellOwnerPinned>) -> Self {
owner.id()
}
}
pub struct QCell<T: ?Sized> {
owner: QCellOwnerID,
value: UnsafeCell<T>,
}
unsafe impl<T: Send + Sync + ?Sized> Sync for QCell<T> {}
impl<T> QCell<T> {
#[inline]
pub fn new(id: impl Into<QCellOwnerID>, value: T) -> QCell<T> {
QCell {
value: UnsafeCell::new(value),
owner: id.into(),
}
}
#[inline]
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
#[cfg(feature = "alloc")]
impl<T: ?Sized> QCell<T> {
#[inline]
pub fn ro<'a>(&'a self, owner: &'a QCellOwner) -> &'a T {
owner.ro(self)
}
#[inline]
pub fn rw<'a>(&'a self, owner: &'a mut QCellOwner) -> &'a mut T {
owner.rw(self)
}
#[inline]
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub struct QCellOwner {
handle: Pin<Box<OwnerIDTarget>>,
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl Default for QCellOwner {
fn default() -> Self {
QCellOwner::new()
}
}
#[cfg(feature = "alloc")]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
impl QCellOwner {
#[inline]
pub fn new() -> Self {
let handle = Box::pin(MAGIC_OWNER_ID_TARGET);
Self { handle }
}
#[inline]
pub fn id(&self) -> QCellOwnerID {
let raw_ptr: *const OwnerIDTarget = &*self.handle;
QCellOwnerID(raw_ptr as usize)
}
#[inline]
pub fn cell<T>(&self, value: T) -> QCell<T> {
let id: QCellOwnerID = self.into();
id.cell(value)
}
#[inline]
pub fn ro<'a, T: ?Sized>(&'a self, qc: &'a QCell<T>) -> &'a T {
owner_check!(self, qc);
unsafe { &*qc.value.get() }
}
#[inline]
pub fn rw<'a, T: ?Sized>(&'a mut self, qc: &'a QCell<T>) -> &'a mut T {
owner_check!(self, qc);
unsafe { &mut *qc.value.get() }
}
#[inline]
pub fn rw2<'a, T: ?Sized, U: ?Sized>(
&'a mut self,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
) -> (&'a mut T, &'a mut U) {
owner_check!(self, qc1, qc2);
distinct_check!(qc1, qc2);
unsafe { (&mut *qc1.value.get(), &mut *qc2.value.get()) }
}
#[inline]
pub fn rw3<'a, T: ?Sized, U: ?Sized, V: ?Sized>(
&'a mut self,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
qc3: &'a QCell<V>,
) -> (&'a mut T, &'a mut U, &'a mut V) {
owner_check!(self, qc1, qc2, qc3);
distinct_check!(qc1, qc2, qc3);
unsafe {
(
&mut *qc1.value.get(),
&mut *qc2.value.get(),
&mut *qc3.value.get(),
)
}
}
}
static FAST_QCELLOWNER_ID: AtomicUsize = AtomicUsize::new(1);
pub struct QCellOwnerSeq {
id: QCellOwnerID,
}
impl QCellOwnerSeq {
#[inline]
pub unsafe fn new() -> Self {
Self {
id: QCellOwnerID(FAST_QCELLOWNER_ID.fetch_add(2, Ordering::Relaxed)),
}
}
#[inline]
pub fn id(&self) -> QCellOwnerID {
self.id
}
#[inline]
pub fn cell<T>(&self, value: T) -> QCell<T> {
self.id.cell(value)
}
#[inline]
pub fn ro<'a, T: ?Sized>(&'a self, qc: &'a QCell<T>) -> &'a T {
owner_check!(self, qc);
unsafe { &*qc.value.get() }
}
#[inline]
pub fn rw<'a, T: ?Sized>(&'a mut self, qc: &'a QCell<T>) -> &'a mut T {
owner_check!(self, qc);
unsafe { &mut *qc.value.get() }
}
#[inline]
pub fn rw2<'a, T: ?Sized, U: ?Sized>(
&'a mut self,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
) -> (&'a mut T, &'a mut U) {
owner_check!(self, qc1, qc2);
distinct_check!(qc1, qc2);
unsafe { (&mut *qc1.value.get(), &mut *qc2.value.get()) }
}
#[inline]
pub fn rw3<'a, T: ?Sized, U: ?Sized, V: ?Sized>(
&'a mut self,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
qc3: &'a QCell<V>,
) -> (&'a mut T, &'a mut U, &'a mut V) {
owner_check!(self, qc1, qc2, qc3);
distinct_check!(qc1, qc2, qc3);
unsafe {
(
&mut *qc1.value.get(),
&mut *qc2.value.get(),
&mut *qc3.value.get(),
)
}
}
}
pub struct QCellOwnerPinned {
target: OwnerIDTarget,
_marker: PhantomPinned,
}
impl Default for QCellOwnerPinned {
fn default() -> Self {
QCellOwnerPinned::new()
}
}
impl QCellOwnerPinned {
#[inline]
pub fn new() -> Self {
Self {
target: MAGIC_OWNER_ID_TARGET,
_marker: PhantomPinned,
}
}
pub fn id(self: Pin<&Self>) -> QCellOwnerID {
let raw_ptr: *const OwnerIDTarget = &self.target;
QCellOwnerID(raw_ptr as usize)
}
#[inline]
pub fn cell<T>(self: Pin<&Self>, value: T) -> QCell<T> {
let id: QCellOwnerID = self.into();
id.cell(value)
}
#[inline]
pub fn ro<'a, T: ?Sized>(self: Pin<&'a Self>, qc: &'a QCell<T>) -> &'a T {
owner_check!(self, qc);
unsafe { &*qc.value.get() }
}
#[inline]
#[allow(clippy::mut_from_ref)]
pub fn rw<'a, T: ?Sized>(self: Pin<&'a mut Self>, qc: &'a QCell<T>) -> &'a mut T {
owner_check!(self.as_ref(), qc);
unsafe { &mut *qc.value.get() }
}
#[inline]
pub fn rw2<'a, T: ?Sized, U: ?Sized>(
self: Pin<&'a mut Self>,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
) -> (&'a mut T, &'a mut U) {
owner_check!(self.as_ref(), qc1, qc2);
distinct_check!(qc1, qc2);
unsafe { (&mut *qc1.value.get(), &mut *qc2.value.get()) }
}
#[inline]
pub fn rw3<'a, T: ?Sized, U: ?Sized, V: ?Sized>(
self: Pin<&'a mut Self>,
qc1: &'a QCell<T>,
qc2: &'a QCell<U>,
qc3: &'a QCell<V>,
) -> (&'a mut T, &'a mut U, &'a mut V) {
owner_check!(self.as_ref(), qc1, qc2, qc3);
distinct_check!(qc1, qc2, qc3);
unsafe {
(
&mut *qc1.value.get(),
&mut *qc2.value.get(),
&mut *qc3.value.get(),
)
}
}
}
#[cfg(test)]
mod tests {
use core::pin::Pin;
use pin_utils::pin_mut;
use super::{QCell, QCellOwnerPinned, QCellOwnerSeq};
#[test]
fn qcell_pinned() {
let owner = QCellOwnerPinned::new();
pin_mut!(owner);
let c1 = owner.as_ref().cell(100u32);
let c2 = owner.as_ref().cell(200u32);
(*owner.as_mut().rw(&c1)) += 1;
(*owner.as_mut().rw(&c2)) += 2;
let c1ref = owner.as_ref().ro(&c1);
let c2ref = owner.as_ref().ro(&c2);
let total = *c1ref + *c2ref;
assert_eq!(total, 303);
}
#[test]
fn qcell_fast_ids_pinned() {
let owner1 = QCellOwnerPinned::new();
pin_mut!(owner1);
let id1 = owner1.as_ref().id();
let owner2 = unsafe { QCellOwnerSeq::new() };
let id2 = owner2.id;
assert_ne!(id1.0, id2.0, "Expected ID 1/2 to be different");
let owner3 = unsafe { QCellOwnerSeq::new() };
let id3 = owner3.id;
assert_ne!(id2.0, id3.0, "Expected ID 2/3 to be different");
drop(owner2);
drop(owner3);
let owner4 = QCellOwnerPinned::new();
pin_mut!(owner4);
let id4 = owner4.as_ref().id();
assert_ne!(id1.0, id4.0, "Expected ID 1/4 to be different");
assert_ne!(id2.0, id4.0, "Expected ID 2/4 to be different");
assert_ne!(id3.0, id4.0, "Expected ID 3/4 to be different");
}
#[test]
fn qcell_sep_ids_pinned() {
let owner1 = QCellOwnerPinned::new();
let owner2 = QCellOwnerPinned::new();
pin_mut!(owner1);
pin_mut!(owner2);
let id1 = owner1.as_ref().id();
let id2 = owner2.as_ref().id();
let c11 = id1.cell(1u32);
let c12 = id2.cell(2u32);
let c21 = owner1.as_ref().cell(4u32);
let c22 = owner2.as_ref().cell(8u32);
assert_eq!(
15,
owner1.as_ref().ro(&c11)
+ owner2.as_ref().ro(&c12)
+ owner1.as_ref().ro(&c21)
+ owner2.as_ref().ro(&c22)
);
}
#[test]
fn qcell_unsized_pinned() {
let owner = QCellOwnerPinned::new();
struct Squares(u32);
struct Integers(u64);
trait Series {
fn step(&mut self);
fn value(&self) -> u64;
}
impl Series for Squares {
fn step(&mut self) {
self.0 += 1;
}
fn value(&self) -> u64 {
(self.0 as u64) * (self.0 as u64)
}
}
impl Series for Integers {
fn step(&mut self) {
self.0 += 1;
}
fn value(&self) -> u64 {
self.0
}
}
fn series(
init: u32,
is_squares: bool,
owner: Pin<&QCellOwnerPinned>,
) -> Box<QCell<dyn Series>> {
if is_squares {
Box::new(owner.cell(Squares(init)))
} else {
Box::new(owner.cell(Integers(init as u64)))
}
}
pin_mut!(owner);
let cell1 = series(4, false, owner.as_ref());
let cell2 = series(7, true, owner.as_ref());
let cell3 = series(3, true, owner.as_ref());
assert_eq!(owner.as_ref().ro(&cell1).value(), 4);
owner.as_mut().rw(&cell1).step();
assert_eq!(owner.as_ref().ro(&cell1).value(), 5);
assert_eq!(owner.as_ref().ro(&cell2).value(), 49);
owner.as_mut().rw(&cell2).step();
assert_eq!(owner.as_ref().ro(&cell2).value(), 64);
let (r1, r2, r3) = owner.as_mut().rw3(&cell1, &cell2, &cell3);
r1.step();
r2.step();
r3.step();
assert_eq!(owner.as_ref().ro(&cell1).value(), 6);
assert_eq!(owner.as_ref().ro(&cell2).value(), 81);
assert_eq!(owner.as_ref().ro(&cell3).value(), 16);
let (r1, r2) = owner.as_mut().rw2(&cell1, &cell2);
r1.step();
r2.step();
assert_eq!(owner.as_ref().ro(&cell1).value(), 7);
assert_eq!(owner.as_ref().ro(&cell2).value(), 100);
}
}
#[cfg(all(test, feature = "alloc"))]
mod tests_with_alloc {
use super::{QCell, QCellOwner, QCellOwnerSeq};
#[test]
fn qcell() {
let mut owner = QCellOwner::new();
let c1 = QCell::new(&owner, 100u32);
let c2 = QCell::new(&owner, 200u32);
(*owner.rw(&c1)) += 1;
(*owner.rw(&c2)) += 2;
let c1ref = owner.ro(&c1);
let c2ref = owner.ro(&c2);
let total = *c1ref + *c2ref;
assert_eq!(total, 303);
}
#[test]
fn qcell_ids() {
let owner1 = QCellOwner::new();
let id1 = owner1.id();
let owner2 = QCellOwner::new();
let id2 = owner2.id();
assert_ne!(id1.0, id2.0, "Expected ID 1/2 to be different");
drop(owner2);
let owner3 = QCellOwner::new();
let id3 = owner3.id();
assert_ne!(id1.0, id3.0, "Expected ID 1/3 to be different");
drop(owner3);
drop(owner1);
let owner4 = QCellOwner::new();
let id4 = owner4.id();
let owner5 = QCellOwner::new();
let id5 = owner5.id();
assert_ne!(id4.0, id5.0, "Expected ID 4/5 to be different");
}
#[test]
fn qcell_fast_ids() {
let owner1 = QCellOwner::new();
let id1 = owner1.id();
let owner2 = unsafe { QCellOwnerSeq::new() };
let id2 = owner2.id();
assert_ne!(id1.0, id2.0, "Expected ID 1/2 to be different");
let owner3 = unsafe { QCellOwnerSeq::new() };
let id3 = owner3.id();
assert_ne!(id2.0, id3.0, "Expected ID 2/3 to be different");
drop(owner2);
drop(owner3);
let owner4 = QCellOwner::new();
let id4 = owner4.id();
assert_ne!(id1.0, id4.0, "Expected ID 1/4 to be different");
assert_ne!(id2.0, id4.0, "Expected ID 2/4 to be different");
assert_ne!(id3.0, id4.0, "Expected ID 3/4 to be different");
}
#[test]
fn qcell_sep_ids() {
let owner1 = QCellOwner::new();
let owner2 = QCellOwner::new();
let id1 = owner1.id();
let id2 = owner2.id();
let c11 = id1.cell(1u32);
let c12 = id2.cell(2u32);
let c21 = owner1.cell(4u32);
let c22 = owner2.cell(8u32);
assert_eq!(
15,
owner1.ro(&c11) + owner2.ro(&c12) + owner1.ro(&c21) + owner2.ro(&c22)
);
}
#[test]
fn qcell_get_mut() {
let owner = QCellOwner::new();
let mut cell = QCell::new(&owner, 100u32);
let mut_ref = cell.get_mut();
*mut_ref = 50;
let cell_ref = owner.ro(&cell);
assert_eq!(*cell_ref, 50);
}
#[test]
fn qcell_into_inner() {
let owner = QCellOwner::new();
let cell = QCell::new(&owner, 100u32);
assert_eq!(cell.into_inner(), 100);
}
#[test]
fn qcell_unsized() {
let mut owner = QCellOwner::new();
struct Squares(u32);
struct Integers(u64);
trait Series {
fn step(&mut self);
fn value(&self) -> u64;
}
impl Series for Squares {
fn step(&mut self) {
self.0 += 1;
}
fn value(&self) -> u64 {
(self.0 as u64) * (self.0 as u64)
}
}
impl Series for Integers {
fn step(&mut self) {
self.0 += 1;
}
fn value(&self) -> u64 {
self.0
}
}
fn series(init: u32, is_squares: bool, owner: &QCellOwner) -> Box<QCell<dyn Series>> {
if is_squares {
Box::new(QCell::new(owner, Squares(init)))
} else {
Box::new(QCell::new(owner, Integers(init as u64)))
}
}
let own = &mut owner;
let cell1 = series(4, false, own);
let cell2 = series(7, true, own);
let cell3 = series(3, true, own);
assert_eq!(cell1.ro(own).value(), 4);
cell1.rw(own).step();
assert_eq!(cell1.ro(own).value(), 5);
assert_eq!(own.ro(&cell2).value(), 49);
own.rw(&cell2).step();
assert_eq!(own.ro(&cell2).value(), 64);
let (r1, r2, r3) = own.rw3(&cell1, &cell2, &cell3);
r1.step();
r2.step();
r3.step();
assert_eq!(cell1.ro(own).value(), 6);
assert_eq!(cell2.ro(own).value(), 81);
assert_eq!(cell3.ro(own).value(), 16);
let (r1, r2) = own.rw2(&cell1, &cell2);
r1.step();
r2.step();
assert_eq!(cell1.ro(own).value(), 7);
assert_eq!(cell2.ro(own).value(), 100);
}
}