#![allow(dead_code)]
use core::hash::Hasher;
use fuchsia_zircon as zx;
use std::{
hash::Hash,
ops::Deref,
sync::{
atomic::{fence, AtomicUsize, Ordering},
Arc, Weak,
},
};
#[macro_export]
macro_rules! make_ownership_types {
($($suffix:ident)?, $self:ty) => { paste::paste! {
pub trait [< Releasable $($suffix)? >] {
type Context<'a>;
fn release<'a>(self: $self, c: Self::Context<'a>);
}
impl<T: [< Releasable $($suffix)? >]> [< Releasable $($suffix)? >] for Option<T> {
type Context<'a> = T::Context<'a>;
fn release<'a>(self: $self, c: Self::Context<'a>) {
if let Some(v) = self {
v.release(c);
}
}
}
impl<T: [< Releasable $($suffix)? >], E> [< Releasable $($suffix)? >] for Result<T, E> {
type Context<'a> = T::Context<'a>;
fn release<'a>(self: $self, c: Self::Context<'a>) {
if let Ok(v) = self {
v.release(c);
}
}
}
impl<T: [< Releasable $($suffix)? >]> [< Releasable $($suffix)? >] for ReleaseGuard<T> {
type Context<'a> = T::Context<'a>;
fn release(self: $self, c: Self::Context<'_>) {
self.drop_guard.disarm();
self.value.release(c);
}
}
}}}
#[must_use = "OwnedRef must be released"]
pub struct OwnedRef<T> {
inner: Option<Arc<RefInner<T>>>,
drop_guard: DropGuard,
}
impl<T> OwnedRef<T> {
pub fn new(value: T) -> Self {
Self { inner: Some(Arc::new(RefInner::new(value))), drop_guard: Default::default() }
}
pub fn new_cyclic<F>(data_fn: F) -> Self
where
F: FnOnce(WeakRef<T>) -> T,
{
let inner = Arc::new_cyclic(|weak_inner| {
let weak = WeakRef(weak_inner.clone());
RefInner::new(data_fn(weak))
});
Self { inner: Some(inner), drop_guard: Default::default() }
}
pub fn as_ptr(this: &Self) -> *const T {
&Self::inner(this).value.value as *const T
}
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
Self::as_ptr(this) == Self::as_ptr(other)
}
pub fn downgrade(this: &Self) -> WeakRef<T> {
WeakRef(Arc::downgrade(Self::inner(this)))
}
pub fn temp(this: &Self) -> TempRef<'_, T> {
TempRef::new(Arc::clone(Self::inner(this)))
}
fn inner(this: &Self) -> &Arc<RefInner<T>> {
this.inner.as_ref().expect("OwnedRef has been released.")
}
}
impl<T: Releasable> OwnedRef<T> {
pub fn take(this: &mut Self) -> Option<ReleaseGuard<T>> {
this.drop_guard.disarm();
let inner = this.inner.take().expect("OwnedRef has been released.");
let previous_count = inner.owned_refs_count.fetch_sub(1, Ordering::Release);
if previous_count == 1 {
fence(Ordering::Acquire);
Some(Self::wait_and_take_value(inner))
} else {
None
}
}
fn wait_and_take_value(mut inner: Arc<RefInner<T>>) -> ReleaseGuard<T> {
loop {
debug_assert_eq!(inner.owned_refs_count.load(Ordering::Acquire), 0);
match Arc::try_unwrap(inner) {
Ok(value) => return value.value,
Err(value) => inner = value,
}
inner.wait_for_no_ref_once();
}
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for OwnedRef<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("OwnedRef").field(&Self::inner(self).value).finish()
}
}
impl<T> Clone for OwnedRef<T> {
fn clone(&self) -> Self {
let inner = Self::inner(self);
let previous_count = inner.owned_refs_count.fetch_add(1, Ordering::Relaxed);
debug_assert!(previous_count > 0, "OwnedRef should not be used after being released.");
Self { inner: Some(Arc::clone(inner)), drop_guard: Default::default() }
}
}
impl<T: Releasable> Releasable for OwnedRef<T> {
type Context<'a> = T::Context<'a>;
#[allow(unused_mut)]
fn release(mut self, c: Self::Context<'_>) {
OwnedRef::take(&mut self).release(c);
}
}
impl<T: Default> Default for OwnedRef<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T> std::ops::Deref for OwnedRef<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&Self::inner(self).deref().value
}
}
impl<T> std::borrow::Borrow<T> for OwnedRef<T> {
fn borrow(&self) -> &T {
self.deref()
}
}
impl<T> std::convert::AsRef<T> for OwnedRef<T> {
fn as_ref(&self) -> &T {
self.deref()
}
}
impl<T: PartialEq> PartialEq for OwnedRef<T> {
fn eq(&self, other: &OwnedRef<T>) -> bool {
Arc::ptr_eq(Self::inner(self), Self::inner(other)) || **self == **other
}
}
impl<T: Eq> Eq for OwnedRef<T> {}
impl<T: PartialOrd> PartialOrd for OwnedRef<T> {
fn partial_cmp(&self, other: &OwnedRef<T>) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: Ord> Ord for OwnedRef<T> {
fn cmp(&self, other: &OwnedRef<T>) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<T: Hash> Hash for OwnedRef<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
impl<T> From<&OwnedRef<T>> for WeakRef<T> {
fn from(owner: &OwnedRef<T>) -> Self {
OwnedRef::downgrade(owner)
}
}
impl<'a, T> From<&'a OwnedRef<T>> for TempRef<'a, T> {
fn from(owner: &'a OwnedRef<T>) -> Self {
OwnedRef::temp(owner)
}
}
impl<'a, T> From<&'a mut OwnedRef<T>> for TempRef<'a, T> {
fn from(owner: &'a mut OwnedRef<T>) -> Self {
OwnedRef::temp(owner)
}
}
#[derive(Debug)]
pub struct WeakRef<T>(Weak<RefInner<T>>);
impl<T> WeakRef<T> {
pub fn new() -> Self {
Self(Weak::new())
}
pub fn upgrade(&self) -> Option<TempRef<'_, T>> {
if let Some(value) = self.0.upgrade() {
let temp_ref = TempRef::new(value);
if temp_ref.0.owned_refs_count.load(Ordering::Acquire) > 0 {
return Some(temp_ref);
}
}
None
}
pub fn as_ptr(&self) -> *const T {
let base = self.0.as_ptr();
let value = memoffset::raw_field!(base, RefInner<T>, value);
memoffset::raw_field!(value, ReleaseGuard<T>, value)
}
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
Self::as_ptr(this) == Self::as_ptr(other)
}
}
impl<T> Default for WeakRef<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> Clone for WeakRef<T> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
pub struct TempRef<'a, T>(Arc<RefInner<T>>, std::marker::PhantomData<(&'a T, *mut u8)>);
impl<'a, T> Drop for TempRef<'a, T> {
fn drop(&mut self) {
self.0.dec_temp_ref();
}
}
impl<'a, T> TempRef<'a, T> {
fn new(inner: Arc<RefInner<T>>) -> Self {
inner.inc_temp_ref();
Self(inner, Default::default())
}
pub fn as_ptr(this: &Self) -> *const T {
&this.0.value.value as *const T
}
pub fn ptr_eq(this: &Self, other: &Self) -> bool {
Self::as_ptr(this) == Self::as_ptr(other)
}
pub fn into_static(this: Self) -> TempRef<'static, T> {
TempRef::new(this.0.clone())
}
}
impl<'a, T> From<&TempRef<'a, T>> for WeakRef<T> {
fn from(temp_ref: &TempRef<'a, T>) -> Self {
Self(Arc::downgrade(&temp_ref.0))
}
}
impl<'a, T> std::ops::Deref for TempRef<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0.deref().value
}
}
impl<'a, T> std::borrow::Borrow<T> for TempRef<'a, T> {
fn borrow(&self) -> &T {
&self.0.deref().value
}
}
impl<'a, T> std::convert::AsRef<T> for TempRef<'a, T> {
fn as_ref(&self) -> &T {
&self.0.deref().value
}
}
impl<'a, T: PartialEq> PartialEq for TempRef<'a, T> {
fn eq(&self, other: &TempRef<'_, T>) -> bool {
Arc::ptr_eq(&self.0, &other.0) || **self == **other
}
}
impl<'a, T: Eq> Eq for TempRef<'a, T> {}
impl<'a, T: PartialOrd> PartialOrd for TempRef<'a, T> {
fn partial_cmp(&self, other: &TempRef<'_, T>) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<'a, T: Ord> Ord for TempRef<'a, T> {
fn cmp(&self, other: &TempRef<'_, T>) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<'a, T: Hash> Hash for TempRef<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
pub struct TempRefKey<'a, T>(pub TempRef<'a, T>);
impl<'a, T> PartialEq for TempRefKey<'a, T> {
fn eq(&self, other: &Self) -> bool {
TempRef::ptr_eq(&self.0, &other.0)
}
}
impl<'a, T> Eq for TempRefKey<'a, T> {}
impl<'a, T> Hash for TempRefKey<'a, T> {
fn hash<H: Hasher>(&self, state: &mut H) {
TempRef::as_ptr(&self.0).hash(state);
}
}
impl<'a, T> std::ops::Deref for TempRefKey<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
#[must_use = "ReleaseGuard must be released"]
pub struct ReleaseGuard<T> {
value: T,
drop_guard: DropGuard,
}
#[cfg(test)]
impl<T> ReleaseGuard<T> {
pub fn new_released(value: T) -> Self {
let result: Self = value.into();
result.drop_guard.disarm();
result
}
}
impl<T> ReleaseGuard<T> {
pub fn take(this: ReleaseGuard<T>) -> T {
this.drop_guard.disarm();
this.value
}
}
#[cfg(test)]
impl<T: Default> ReleaseGuard<T> {
pub fn default_released() -> Self {
Self::new_released(T::default())
}
}
impl<T: std::fmt::Debug> std::fmt::Debug for ReleaseGuard<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.value.fmt(f)
}
}
impl<T: Default> Default for ReleaseGuard<T> {
fn default() -> Self {
T::default().into()
}
}
impl<T: Clone> Clone for ReleaseGuard<T> {
fn clone(&self) -> Self {
self.value.clone().into()
}
}
impl<T> From<T> for ReleaseGuard<T> {
fn from(value: T) -> Self {
Self { value, drop_guard: Default::default() }
}
}
impl<T> std::ops::Deref for ReleaseGuard<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.value
}
}
impl<T> std::ops::DerefMut for ReleaseGuard<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.value
}
}
impl<T> std::borrow::Borrow<T> for ReleaseGuard<T> {
fn borrow(&self) -> &T {
self.deref()
}
}
impl<T> std::convert::AsRef<T> for ReleaseGuard<T> {
fn as_ref(&self) -> &T {
self.deref()
}
}
impl<T: PartialEq> PartialEq for ReleaseGuard<T> {
fn eq(&self, other: &ReleaseGuard<T>) -> bool {
**self == **other
}
}
impl<T: Eq> Eq for ReleaseGuard<T> {}
impl<T: PartialOrd> PartialOrd for ReleaseGuard<T> {
fn partial_cmp(&self, other: &ReleaseGuard<T>) -> Option<std::cmp::Ordering> {
(**self).partial_cmp(&**other)
}
}
impl<T: Ord> Ord for ReleaseGuard<T> {
fn cmp(&self, other: &ReleaseGuard<T>) -> std::cmp::Ordering {
(**self).cmp(&**other)
}
}
impl<T: Hash> Hash for ReleaseGuard<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
(**self).hash(state)
}
}
#[derive(Default, Debug)]
pub struct DropGuard {
#[cfg(any(test, debug_assertions))]
released: std::sync::atomic::AtomicBool,
}
impl DropGuard {
#[inline(always)]
pub fn disarm(&self) {
#[cfg(any(test, debug_assertions))]
{
if self
.released
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
.is_err()
{
panic!("Guard was disarmed twice");
}
}
}
}
#[cfg(any(test, debug_assertions))]
impl Drop for DropGuard {
fn drop(&mut self) {
assert!(*self.released.get_mut());
}
}
#[cfg(any(test, debug_assertions))]
thread_local! {
static TEMP_REF_LOCAL_COUNT: std::cell::RefCell<usize> = std::cell::RefCell::new(0);
}
pub fn debug_assert_no_local_temp_ref() {
#[cfg(any(test, debug_assertions))]
{
TEMP_REF_LOCAL_COUNT.with(|count| {
assert_eq!(*count.borrow(), 0, "Current threads owns {} TempRef", *count.borrow());
});
}
}
struct RefInner<T> {
value: ReleaseGuard<T>,
owned_refs_count: AtomicUsize,
temp_refs_count: zx::Futex,
}
impl<T> RefInner<T> {
fn new(value: T) -> Self {
Self {
value: value.into(),
owned_refs_count: AtomicUsize::new(1),
temp_refs_count: zx::Futex::new(0),
}
}
fn inc_temp_ref(&self) {
self.temp_refs_count.fetch_add(1, Ordering::Relaxed);
#[cfg(any(test, debug_assertions))]
{
TEMP_REF_LOCAL_COUNT.with(|count| {
*count.borrow_mut() += 1;
});
}
}
fn dec_temp_ref(&self) {
let previous_count = self.temp_refs_count.fetch_sub(1, Ordering::Release);
if previous_count == 1 {
fence(Ordering::Acquire);
self.temp_refs_count.wake_single_owner();
}
#[cfg(any(test, debug_assertions))]
{
TEMP_REF_LOCAL_COUNT.with(|count| {
*count.borrow_mut() -= 1;
});
}
}
fn wait_for_no_ref_once(self: &Arc<Self>) {
let current_value = self.temp_refs_count.load(Ordering::Acquire);
if current_value == 0 {
return;
}
let result = self.temp_refs_count.wait(current_value, None, zx::Time::INFINITE);
debug_assert!(
result == Ok(()) || result == Err(zx::Status::BAD_STATE),
"Unexpected result: {result:?}"
);
}
}
make_ownership_types!(ByRef, &Self);
make_ownership_types!(ByMut, &mut Self);
make_ownership_types!(, Self);
#[macro_export]
macro_rules! release_on_error {
($releasable_name:ident, $context:expr, $body:block ) => {{
#[allow(clippy::redundant_closure_call)]
let result = { (|| $body)() };
match result {
Err(e) => {
$releasable_name.release($context);
return Err(e);
}
Ok(x) => x,
}
}};
}
#[macro_export]
macro_rules! release_after {
($releasable_name:ident, $context:expr, $(|| -> $output_type:ty)? $body:block ) => {{
#[allow(clippy::redundant_closure_call)]
let result = { (|| $(-> $output_type)? { $body })() };
$releasable_name.release($context);
result
}};
}
pub mod internal {
pub async fn async_try<E>(block: impl std::future::Future<Output = E>) -> E {
block.await
}
}
#[macro_export]
macro_rules! async_release_after {
($releasable_name:ident, $context:expr, $(|| -> $output_type:ty)? $body:block ) => {{
let result =
crate::ownership::internal::async_try$(::<$output_type>)?(async { $body }).await;
$releasable_name.release($context);
result
}};
}
pub use release_after;
pub use release_on_error;
#[cfg(test)]
mod test {
use super::*;
#[derive(Default)]
struct Data;
impl ReleasableByRef for Data {
type Context<'a> = ();
fn release(&self, _: ()) {}
}
impl ReleasableByMut for Data {
type Context<'a> = ();
fn release(&mut self, _: ()) {}
}
impl Releasable for Data {
type Context<'a> = ();
fn release(self, _: ()) {}
}
#[derive(Default)]
struct DataWithMutableReleaseContext;
impl Releasable for DataWithMutableReleaseContext {
type Context<'a> = &'a mut ();
fn release(self, _: &mut ()) {}
}
#[::fuchsia::test]
#[should_panic]
fn drop_without_release() {
let _ = OwnedRef::new(Data {});
}
#[::fuchsia::test]
fn test_creation_and_reference() {
let value = OwnedRef::new(Data {});
let reference = WeakRef::from(&value);
reference.upgrade().expect("upgrade");
value.release(());
assert!(reference.upgrade().is_none());
}
#[::fuchsia::test]
fn test_clone() {
let value = OwnedRef::new(Data {});
{
let value2 = OwnedRef::clone(&value);
value2.release(());
}
#[allow(clippy::redundant_clone)]
{
let reference = WeakRef::from(&value);
let _reference2 = reference.clone();
}
value.release(());
}
#[::fuchsia::test]
fn test_default() {
let reference = WeakRef::<Data>::default();
assert!(reference.upgrade().is_none());
}
#[::fuchsia::test]
fn test_release_on_error() {
fn release_on_error() -> Result<(), ()> {
let value = OwnedRef::new(Data {});
release_on_error!(value, (), {
if true {
return Err(());
}
Ok(())
});
Ok(())
}
assert_eq!(release_on_error(), Err(()));
}
#[::fuchsia::test]
fn test_into_static() {
let value = OwnedRef::new(Data {});
let weak = WeakRef::from(&value);
let static_ref = TempRef::into_static(weak.upgrade().unwrap());
std::mem::drop(weak);
std::mem::drop(static_ref);
value.release(());
}
#[::fuchsia::test]
fn test_debug_assert_no_local_temp_ref() {
debug_assert_no_local_temp_ref();
let value = OwnedRef::new(Data {});
debug_assert_no_local_temp_ref();
let _temp_ref = OwnedRef::temp(&value);
std::thread::spawn(|| {
debug_assert_no_local_temp_ref();
})
.join()
.expect("join");
std::mem::drop(_temp_ref);
debug_assert_no_local_temp_ref();
value.release(());
debug_assert_no_local_temp_ref();
}
#[::fuchsia::test]
#[should_panic]
fn test_debug_assert_no_local_temp_ref_aborts() {
let value = OwnedRef::new(Data {});
{
let _temp_ref = OwnedRef::temp(&value);
debug_assert_no_local_temp_ref();
}
value.release(());
}
#[::fuchsia::test]
#[should_panic]
fn test_unrelease_release_guard() {
let _value = ReleaseGuard::<Data>::default();
}
#[::fuchsia::test]
#[should_panic]
fn test_double_release_release_guard() {
let value = ReleaseGuard::<Data>::default();
ReleasableByRef::release(&value, ());
ReleasableByRef::release(&value, ());
}
#[::fuchsia::test]
fn test_released_release_guard() {
let _value = ReleaseGuard::<Data>::default_released();
}
#[::fuchsia::test]
fn release_with_mutable_context() {
let value = OwnedRef::new(DataWithMutableReleaseContext {});
let mut context = ();
value.release(&mut context);
}
#[::fuchsia::test]
fn upgrade_while_release() {
let value = OwnedRef::new(Data {});
for _ in 0..10 {
std::thread::spawn({
let weak = OwnedRef::downgrade(&value);
move || loop {
if weak.upgrade().is_none() {
return;
}
}
});
}
std::thread::sleep(std::time::Duration::from_millis(10));
value.release(());
}
#[::fuchsia::test]
fn new_cyclic() {
let mut weak_value = None;
let value = OwnedRef::new_cyclic(|weak| {
weak_value = Some(weak);
Data {}
});
let weak_value = weak_value.expect("weak_value");
assert!(weak_value.upgrade().is_some());
value.release(());
assert!(weak_value.upgrade().is_none());
}
#[::fuchsia::test]
fn as_ptr() {
let value = OwnedRef::new(Data {});
let weak = OwnedRef::downgrade(&value);
let temp = weak.upgrade().expect("upgrade");
assert_eq!(OwnedRef::as_ptr(&value), weak.as_ptr());
assert_eq!(OwnedRef::as_ptr(&value), TempRef::as_ptr(&temp));
std::mem::drop(temp);
value.release(());
}
}