#![cfg(feature = "lock_api")]
use alloc::boxed::Box;
use core::ptr::NonNull;
use lock_api::{Mutex, MutexGuard, RawMutex, RawRwLock, RwLock, RwLockReadGuard, RwLockWriteGuard};
use crate::{
DeclId, Def, Facet, KnownPointer, LockGuardVTable, OxPtrMut, PointerDef, PointerFlags,
PointerVTable, PtrConst, PtrMut, PtrUninit, ReadLockResult, Shape, ShapeBuilder, Type,
TypeNameOpts, TypeOpsIndirect, TypeParam, UserType, VTableIndirect, Variance, VarianceDep,
VarianceDesc, WriteLockResult, decl_id_hash,
};
fn type_name_mutex<'a, T: Facet<'a>>(
_shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "Mutex")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
T::SHAPE.write_type_name(f, opts)?;
write!(f, ">")?;
} else {
write!(f, "<\u{2026}>")?;
}
Ok(())
}
unsafe extern "C" fn mutex_new_into<R: RawMutex, T>(this: PtrUninit, value: PtrMut) -> PtrMut {
unsafe {
let t = value.read::<T>();
this.put(Mutex::<R, T>::new(t))
}
}
unsafe fn mutex_drop<R: RawMutex, T>(ox: OxPtrMut) {
unsafe { core::ptr::drop_in_place(ox.ptr().as_ptr::<Mutex<R, T>>() as *mut Mutex<R, T>) }
}
unsafe fn mutex_lock<'a, R: RawMutex, T: Facet<'a>>(
opaque: PtrConst,
) -> Result<WriteLockResult, ()> {
unsafe {
let mutex = &*opaque.as_ptr::<Mutex<R, T>>();
let mut guard = mutex.lock();
let data_ptr = &mut *guard as *mut T;
let guard_box = Box::new(guard);
let guard_ptr = Box::into_raw(guard_box) as *const u8;
Ok(WriteLockResult::new(
PtrMut::new(data_ptr as *mut u8),
PtrConst::new(guard_ptr),
&const { mutex_guard_vtable::<R, T>() },
))
}
}
const fn mutex_guard_vtable<R: RawMutex, T>() -> LockGuardVTable {
unsafe fn drop_guard<R: RawMutex, T>(guard: PtrConst) {
unsafe {
drop(Box::from_raw(
guard.as_ptr::<MutexGuard<'_, R, T>>() as *mut MutexGuard<'_, R, T>
));
}
}
LockGuardVTable {
drop_in_place: drop_guard::<R, T>,
}
}
unsafe impl<'a, R: RawMutex + 'a, T: Facet<'a>> Facet<'a> for Mutex<R, T> {
const SHAPE: &'static Shape = &const {
ShapeBuilder::for_sized::<Self>("Mutex")
.decl_id(DeclId::new(decl_id_hash("#lock_api#Mutex")))
.type_name(type_name_mutex::<T>)
.vtable_indirect(&VTableIndirect::EMPTY)
.type_ops_indirect(
&const {
TypeOpsIndirect {
drop_in_place: mutex_drop::<R, T>,
default_in_place: None,
clone_into: None,
is_truthy: None,
}
},
)
.ty(Type::User(UserType::Opaque))
.def(Def::Pointer(PointerDef {
vtable: &const {
PointerVTable {
lock_fn: Some(mutex_lock::<R, T>),
new_into_fn: Some(mutex_new_into::<R, T>),
..PointerVTable::new()
}
},
pointee: Some(T::SHAPE),
weak: None,
strong: None,
flags: PointerFlags::LOCK,
known: Some(KnownPointer::Mutex),
}))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::invariant(T::SHAPE)] },
})
.build()
};
}
fn type_name_rwlock<'a, T: Facet<'a>>(
_shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "RwLock")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
T::SHAPE.write_type_name(f, opts)?;
write!(f, ">")?;
} else {
write!(f, "<\u{2026}>")?;
}
Ok(())
}
unsafe extern "C" fn rwlock_new_into<R: RawRwLock, T>(this: PtrUninit, value: PtrMut) -> PtrMut {
unsafe {
let t = value.read::<T>();
this.put(RwLock::<R, T>::new(t))
}
}
unsafe fn rwlock_drop<R: RawRwLock, T>(ox: OxPtrMut) {
unsafe { core::ptr::drop_in_place(ox.ptr().as_ptr::<RwLock<R, T>>() as *mut RwLock<R, T>) }
}
const fn rwlock_read_guard_vtable<R: RawRwLock, T>() -> LockGuardVTable {
unsafe fn drop_guard<R: RawRwLock, T>(guard: PtrConst) {
unsafe {
drop(Box::from_raw(
guard.as_ptr::<RwLockReadGuard<'_, R, T>>() as *mut RwLockReadGuard<'_, R, T>
));
}
}
LockGuardVTable {
drop_in_place: drop_guard::<R, T>,
}
}
const fn rwlock_write_guard_vtable<R: RawRwLock, T>() -> LockGuardVTable {
unsafe fn drop_guard<R: RawRwLock, T>(guard: PtrConst) {
unsafe {
drop(Box::from_raw(
guard.as_ptr::<RwLockWriteGuard<'_, R, T>>() as *mut RwLockWriteGuard<'_, R, T>
));
}
}
LockGuardVTable {
drop_in_place: drop_guard::<R, T>,
}
}
unsafe fn rwlock_read<'a, R: RawRwLock, T: Facet<'a>>(
opaque: PtrConst,
) -> Result<ReadLockResult, ()> {
unsafe {
let rwlock = &*opaque.as_ptr::<RwLock<R, T>>();
let guard = rwlock.read();
let data_ptr = &*guard as *const T;
let guard_box = Box::new(guard);
let guard_ptr = Box::into_raw(guard_box) as *const u8;
Ok(ReadLockResult::new(
PtrConst::new(data_ptr as *const u8),
PtrConst::new(guard_ptr),
&const { rwlock_read_guard_vtable::<R, T>() },
))
}
}
unsafe fn rwlock_write<'a, R: RawRwLock, T: Facet<'a>>(
opaque: PtrConst,
) -> Result<WriteLockResult, ()> {
unsafe {
let rwlock = &*opaque.as_ptr::<RwLock<R, T>>();
let mut guard = rwlock.write();
let data_ptr = &mut *guard as *mut T;
let guard_box = Box::new(guard);
let guard_ptr = Box::into_raw(guard_box) as *const u8;
Ok(WriteLockResult::new(
PtrMut::new(data_ptr as *mut u8),
PtrConst::new(guard_ptr),
&const { rwlock_write_guard_vtable::<R, T>() },
))
}
}
unsafe impl<'a, R: RawRwLock + 'a, T: Facet<'a>> Facet<'a> for RwLock<R, T> {
const SHAPE: &'static Shape = &const {
ShapeBuilder::for_sized::<Self>("RwLock")
.decl_id(DeclId::new(decl_id_hash("#lock_api#RwLock")))
.type_name(type_name_rwlock::<T>)
.vtable_indirect(&VTableIndirect::EMPTY)
.type_ops_indirect(
&const {
TypeOpsIndirect {
drop_in_place: rwlock_drop::<R, T>,
default_in_place: None,
clone_into: None,
is_truthy: None,
}
},
)
.ty(Type::User(UserType::Opaque))
.def(Def::Pointer(PointerDef {
vtable: &const {
PointerVTable {
read_fn: Some(rwlock_read::<R, T>),
write_fn: Some(rwlock_write::<R, T>),
new_into_fn: Some(rwlock_new_into::<R, T>),
..PointerVTable::new()
}
},
pointee: Some(T::SHAPE),
weak: None,
strong: None,
flags: PointerFlags::LOCK,
known: Some(KnownPointer::RwLock),
}))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::invariant(T::SHAPE)] },
})
.build()
};
}
fn type_name_mutex_guard<'a, T: Facet<'a>>(
_shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "MutexGuard")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
T::SHAPE.write_type_name(f, opts)?;
write!(f, ">")?;
} else {
write!(f, "<\u{2026}>")?;
}
Ok(())
}
unsafe extern "C" fn mutex_guard_borrow<'a, R: RawMutex, T: Facet<'a>>(this: PtrConst) -> PtrConst {
unsafe {
let guard = this.get::<MutexGuard<'_, R, T>>();
let data: &T = guard;
PtrConst::new(NonNull::from(data).as_ptr())
}
}
unsafe fn mutex_guard_drop_impl<R: RawMutex, T>(ox: OxPtrMut) {
unsafe {
core::ptr::drop_in_place(
ox.ptr().as_ptr::<MutexGuard<'_, R, T>>() as *mut MutexGuard<'_, R, T>
)
}
}
unsafe impl<'a, R: RawMutex + 'a, T: Facet<'a>> Facet<'a> for MutexGuard<'a, R, T> {
const SHAPE: &'static Shape = &const {
ShapeBuilder::for_sized::<Self>("MutexGuard")
.decl_id(DeclId::new(decl_id_hash("#lock_api#MutexGuard")))
.type_name(type_name_mutex_guard::<T>)
.vtable_indirect(&VTableIndirect::EMPTY)
.type_ops_indirect(
&const {
TypeOpsIndirect {
drop_in_place: mutex_guard_drop_impl::<R, T>,
default_in_place: None,
clone_into: None,
is_truthy: None,
}
},
)
.ty(Type::User(UserType::Opaque))
.def(Def::Pointer(PointerDef {
vtable: &const {
PointerVTable {
borrow_fn: Some(mutex_guard_borrow::<R, T>),
..PointerVTable::new()
}
},
pointee: Some(T::SHAPE),
weak: None,
strong: None,
flags: PointerFlags::EMPTY,
known: None,
}))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::invariant(T::SHAPE)] },
})
.build()
};
}
fn type_name_rwlock_read_guard<'a, T: Facet<'a>>(
_shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "RwLockReadGuard")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
T::SHAPE.write_type_name(f, opts)?;
write!(f, ">")?;
} else {
write!(f, "<\u{2026}>")?;
}
Ok(())
}
unsafe extern "C" fn rwlock_read_guard_borrow<'a, R: RawRwLock, T: Facet<'a>>(
this: PtrConst,
) -> PtrConst {
unsafe {
let guard = this.get::<RwLockReadGuard<'_, R, T>>();
let data: &T = guard;
PtrConst::new(NonNull::from(data).as_ptr())
}
}
unsafe fn rwlock_read_guard_drop_impl<R: RawRwLock, T>(ox: OxPtrMut) {
unsafe {
core::ptr::drop_in_place(
ox.ptr().as_ptr::<RwLockReadGuard<'_, R, T>>() as *mut RwLockReadGuard<'_, R, T>
)
}
}
unsafe impl<'a, R: RawRwLock + 'a, T: Facet<'a>> Facet<'a> for RwLockReadGuard<'a, R, T> {
const SHAPE: &'static Shape = &const {
ShapeBuilder::for_sized::<Self>("RwLockReadGuard")
.decl_id(DeclId::new(decl_id_hash("#lock_api#RwLockReadGuard")))
.type_name(type_name_rwlock_read_guard::<T>)
.vtable_indirect(&VTableIndirect::EMPTY)
.type_ops_indirect(
&const {
TypeOpsIndirect {
drop_in_place: rwlock_read_guard_drop_impl::<R, T>,
default_in_place: None,
clone_into: None,
is_truthy: None,
}
},
)
.ty(Type::User(UserType::Opaque))
.def(Def::Pointer(PointerDef {
vtable: &const {
PointerVTable {
borrow_fn: Some(rwlock_read_guard_borrow::<R, T>),
..PointerVTable::new()
}
},
pointee: Some(T::SHAPE),
weak: None,
strong: None,
flags: PointerFlags::EMPTY,
known: None,
}))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::covariant(T::SHAPE)] },
})
.build()
};
}
fn type_name_rwlock_write_guard<'a, T: Facet<'a>>(
_shape: &'static Shape,
f: &mut core::fmt::Formatter<'_>,
opts: TypeNameOpts,
) -> core::fmt::Result {
write!(f, "RwLockWriteGuard")?;
if let Some(opts) = opts.for_children() {
write!(f, "<")?;
T::SHAPE.write_type_name(f, opts)?;
write!(f, ">")?;
} else {
write!(f, "<\u{2026}>")?;
}
Ok(())
}
unsafe extern "C" fn rwlock_write_guard_borrow<'a, R: RawRwLock, T: Facet<'a>>(
this: PtrConst,
) -> PtrConst {
unsafe {
let guard = this.get::<RwLockWriteGuard<'_, R, T>>();
let data: &T = guard;
PtrConst::new(NonNull::from(data).as_ptr())
}
}
unsafe fn rwlock_write_guard_drop_impl<R: RawRwLock, T>(ox: OxPtrMut) {
unsafe {
core::ptr::drop_in_place(
ox.ptr().as_ptr::<RwLockWriteGuard<'_, R, T>>() as *mut RwLockWriteGuard<'_, R, T>
)
}
}
unsafe impl<'a, R: RawRwLock + 'a, T: Facet<'a>> Facet<'a> for RwLockWriteGuard<'a, R, T> {
const SHAPE: &'static Shape = &const {
ShapeBuilder::for_sized::<Self>("RwLockWriteGuard")
.decl_id(DeclId::new(decl_id_hash("#lock_api#RwLockWriteGuard")))
.type_name(type_name_rwlock_write_guard::<T>)
.vtable_indirect(&VTableIndirect::EMPTY)
.type_ops_indirect(
&const {
TypeOpsIndirect {
drop_in_place: rwlock_write_guard_drop_impl::<R, T>,
default_in_place: None,
clone_into: None,
is_truthy: None,
}
},
)
.ty(Type::User(UserType::Opaque))
.def(Def::Pointer(PointerDef {
vtable: &const {
PointerVTable {
borrow_fn: Some(rwlock_write_guard_borrow::<R, T>),
..PointerVTable::new()
}
},
pointee: Some(T::SHAPE),
weak: None,
strong: None,
flags: PointerFlags::EMPTY,
known: None,
}))
.type_params(&[TypeParam {
name: "T",
shape: T::SHAPE,
}])
.inner(T::SHAPE)
.variance(VarianceDesc {
base: Variance::Bivariant,
deps: &const { [VarianceDep::invariant(T::SHAPE)] },
})
.build()
};
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::string::String;
use parking_lot::{RawMutex as ParkingLotRawMutex, RawRwLock as ParkingLotRawRwLock};
type TestMutex<T> = Mutex<ParkingLotRawMutex, T>;
type TestRwLock<T> = RwLock<ParkingLotRawRwLock, T>;
#[test]
fn test_mutex_shape() {
facet_testhelpers::setup();
let shape = <TestMutex<i32>>::SHAPE;
assert_eq!(shape.type_identifier, "Mutex");
let [type_param] = shape.type_params else {
panic!("Mutex should have 1 type param");
};
assert_eq!(type_param.name, "T");
assert_eq!(type_param.shape, i32::SHAPE);
}
#[test]
fn test_rwlock_shape() {
facet_testhelpers::setup();
let shape = <TestRwLock<String>>::SHAPE;
assert_eq!(shape.type_identifier, "RwLock");
let [type_param] = shape.type_params else {
panic!("RwLock should have 1 type param");
};
assert_eq!(type_param.name, "T");
assert_eq!(type_param.shape, String::SHAPE);
}
#[test]
fn test_mutex_guard_shape() {
facet_testhelpers::setup();
let shape = <MutexGuard<'_, ParkingLotRawMutex, i32>>::SHAPE;
assert_eq!(shape.type_identifier, "MutexGuard");
let [type_param] = shape.type_params else {
panic!("MutexGuard should have 1 type param");
};
assert_eq!(type_param.name, "T");
}
#[test]
fn test_rwlock_read_guard_shape() {
facet_testhelpers::setup();
let shape = <RwLockReadGuard<'_, ParkingLotRawRwLock, i32>>::SHAPE;
assert_eq!(shape.type_identifier, "RwLockReadGuard");
let [type_param] = shape.type_params else {
panic!("RwLockReadGuard should have 1 type param");
};
assert_eq!(type_param.name, "T");
}
#[test]
fn test_rwlock_write_guard_shape() {
facet_testhelpers::setup();
let shape = <RwLockWriteGuard<'_, ParkingLotRawRwLock, i32>>::SHAPE;
assert_eq!(shape.type_identifier, "RwLockWriteGuard");
let [type_param] = shape.type_params else {
panic!("RwLockWriteGuard should have 1 type param");
};
assert_eq!(type_param.name, "T");
}
#[test]
fn test_mutex_vtable() {
facet_testhelpers::setup();
let shape = <TestMutex<i32>>::SHAPE;
let pointer_def = shape
.def
.into_pointer()
.expect("Mutex should be a pointer type");
assert!(
pointer_def.vtable.lock_fn.is_some(),
"Mutex should have lock_fn"
);
assert!(
pointer_def.vtable.new_into_fn.is_some(),
"Mutex should have new_into_fn"
);
assert!(
pointer_def.vtable.read_fn.is_none(),
"Mutex should not have read_fn"
);
assert!(
pointer_def.vtable.write_fn.is_none(),
"Mutex should not have write_fn"
);
assert!(
pointer_def.flags.contains(PointerFlags::LOCK),
"Mutex should have LOCK flag"
);
assert_eq!(pointer_def.known, Some(KnownPointer::Mutex));
}
#[test]
fn test_rwlock_vtable() {
facet_testhelpers::setup();
let shape = <TestRwLock<i32>>::SHAPE;
let pointer_def = shape
.def
.into_pointer()
.expect("RwLock should be a pointer type");
assert!(
pointer_def.vtable.read_fn.is_some(),
"RwLock should have read_fn"
);
assert!(
pointer_def.vtable.write_fn.is_some(),
"RwLock should have write_fn"
);
assert!(
pointer_def.vtable.new_into_fn.is_some(),
"RwLock should have new_into_fn"
);
assert!(
pointer_def.vtable.lock_fn.is_none(),
"RwLock should not have lock_fn"
);
assert!(
pointer_def.flags.contains(PointerFlags::LOCK),
"RwLock should have LOCK flag"
);
assert_eq!(pointer_def.known, Some(KnownPointer::RwLock));
}
#[test]
fn test_guard_vtables_have_borrow_fn() {
facet_testhelpers::setup();
let mutex_guard_shape = <MutexGuard<'_, ParkingLotRawMutex, i32>>::SHAPE;
let mutex_guard_def = mutex_guard_shape
.def
.into_pointer()
.expect("MutexGuard should be a pointer type");
assert!(
mutex_guard_def.vtable.borrow_fn.is_some(),
"MutexGuard should have borrow_fn"
);
let read_guard_shape = <RwLockReadGuard<'_, ParkingLotRawRwLock, i32>>::SHAPE;
let read_guard_def = read_guard_shape
.def
.into_pointer()
.expect("RwLockReadGuard should be a pointer type");
assert!(
read_guard_def.vtable.borrow_fn.is_some(),
"RwLockReadGuard should have borrow_fn"
);
let write_guard_shape = <RwLockWriteGuard<'_, ParkingLotRawRwLock, i32>>::SHAPE;
let write_guard_def = write_guard_shape
.def
.into_pointer()
.expect("RwLockWriteGuard should be a pointer type");
assert!(
write_guard_def.vtable.borrow_fn.is_some(),
"RwLockWriteGuard should have borrow_fn"
);
}
#[test]
fn test_mutex_lock_and_access() {
facet_testhelpers::setup();
let mutex = TestMutex::new(42i32);
let shape = <TestMutex<i32>>::SHAPE;
let pointer_def = shape.def.into_pointer().unwrap();
let lock_fn = pointer_def.vtable.lock_fn.unwrap();
let mutex_ptr = PtrConst::new(&mutex as *const _ as *const u8);
let lock_result = unsafe { lock_fn(mutex_ptr) }.expect("Lock should succeed");
let data_ptr = lock_result.data();
let value = unsafe { data_ptr.as_const().get::<i32>() };
assert_eq!(*value, 42);
drop(lock_result);
let lock_result2 = unsafe { lock_fn(mutex_ptr) }.expect("Second lock should succeed");
drop(lock_result2);
}
#[test]
fn test_rwlock_read_access() {
facet_testhelpers::setup();
let rwlock = TestRwLock::new(String::from("hello"));
let shape = <TestRwLock<String>>::SHAPE;
let pointer_def = shape.def.into_pointer().unwrap();
let read_fn = pointer_def.vtable.read_fn.unwrap();
let rwlock_ptr = PtrConst::new(&rwlock as *const _ as *const u8);
let read_result = unsafe { read_fn(rwlock_ptr) }.expect("Read lock should succeed");
let data_ptr = read_result.data();
let value = unsafe { data_ptr.get::<String>() };
assert_eq!(value.as_str(), "hello");
drop(read_result);
}
#[test]
fn test_rwlock_write_access() {
facet_testhelpers::setup();
let rwlock = TestRwLock::new(100i32);
let shape = <TestRwLock<i32>>::SHAPE;
let pointer_def = shape.def.into_pointer().unwrap();
let write_fn = pointer_def.vtable.write_fn.unwrap();
let rwlock_ptr = PtrConst::new(&rwlock as *const _ as *const u8);
let write_result = unsafe { write_fn(rwlock_ptr) }.expect("Write lock should succeed");
let data_ptr = write_result.data();
unsafe {
*data_ptr.as_mut_ptr::<i32>() = 200;
}
drop(write_result);
let read_fn = pointer_def.vtable.read_fn.unwrap();
let read_result = unsafe { read_fn(rwlock_ptr) }.expect("Read lock should succeed");
let value = unsafe { read_result.data().get::<i32>() };
assert_eq!(*value, 200);
}
#[test]
fn test_mutex_multiple_read_locks_blocked() {
facet_testhelpers::setup();
let mutex = TestMutex::new(42i32);
let shape = <TestMutex<i32>>::SHAPE;
let pointer_def = shape.def.into_pointer().unwrap();
let lock_fn = pointer_def.vtable.lock_fn.unwrap();
let mutex_ptr = PtrConst::new(&mutex as *const _ as *const u8);
let lock1 = unsafe { lock_fn(mutex_ptr) }.expect("First lock should succeed");
let value = unsafe { lock1.data().as_const().get::<i32>() };
assert_eq!(*value, 42);
drop(lock1);
}
}