// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use std::any::type_name; use std::any::Any; use std::borrow::Borrow; use std::cell::Cell; use std::cell::UnsafeCell; use std::collections::VecDeque; use std::fmt; use std::fmt::Debug; use std::fmt::Formatter; use std::ops::Deref; use std::rc::Rc; use self::internal as i; pub type AsyncRef = i::AsyncBorrowImpl; pub type AsyncMut = i::AsyncBorrowImpl; pub type AsyncRefFuture = i::AsyncBorrowFutureImpl; pub type AsyncMutFuture = i::AsyncBorrowFutureImpl; pub struct AsyncRefCell { value: UnsafeCell, borrow_count: Cell, waiters: Cell>>, turn: Cell, } impl AsyncRefCell { /// Create a new `AsyncRefCell` that encapsulates the specified value. /// Note that in order to borrow the inner value, the `AsyncRefCell` /// needs to be wrapped in an `Rc` or an `RcRef`. These can be created /// either manually, or by using the convenience method /// `AsyncRefCell::new_rc()`. pub fn new(value: T) -> Self { Self { value: UnsafeCell::new(value), borrow_count: Default::default(), waiters: Default::default(), turn: Default::default(), } } pub fn new_rc(value: T) -> Rc { Rc::new(Self::new(value)) } pub fn as_ptr(&self) -> *mut T { self.value.get() } pub fn into_inner(self) -> T { assert!(self.borrow_count.get().is_empty()); self.value.into_inner() } } impl Debug for AsyncRefCell { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "AsyncRefCell<{}>", type_name::()) } } impl Default for AsyncRefCell { fn default() -> Self { Self::new(Default::default()) } } impl AsyncRefCell { pub fn default_rc() -> Rc { Rc::new(Default::default()) } } impl From for AsyncRefCell { fn from(value: T) -> Self { Self::new(value) } } impl AsyncRefCell { pub fn borrow(self: &Rc) -> AsyncRefFuture { AsyncRefFuture::new(self) } pub fn borrow_mut(self: &Rc) -> AsyncMutFuture { AsyncMutFuture::new(self) } pub fn try_borrow(self: &Rc) -> Option> { Self::borrow_sync(self) } pub fn try_borrow_mut(self: &Rc) -> Option> { Self::borrow_sync(self) } } impl RcRef> { pub fn borrow(&self) -> AsyncRefFuture { AsyncRefFuture::new(self) } pub fn borrow_mut(&self) -> AsyncMutFuture { AsyncMutFuture::new(self) } pub fn try_borrow(&self) -> Option> { AsyncRefCell::::borrow_sync(self) } pub fn try_borrow_mut(&self) -> Option> { AsyncRefCell::::borrow_sync(self) } } /// An `RcRef` encapsulates a reference counted pointer, just like a regular /// `std::rc::Rc`. However, unlike a regular `Rc`, it can be remapped so that /// it dereferences to any value that's reachable through the reference-counted /// pointer. This is achieved through the associated method, `RcRef::map()`, /// similar to how `std::cell::Ref::map()` works. Example: /// /// ```rust /// # use std::rc::Rc; /// # use deno_core::RcRef; /// /// struct Stuff { /// foo: u32, /// bar: String, /// } /// /// let stuff_rc = Rc::new(Stuff { /// foo: 42, /// bar: "hello".to_owned(), /// }); /// /// // `foo_rc` and `bar_rc` dereference to different types, however /// // they share a reference count. /// let foo_rc: RcRef = RcRef::map(stuff_rc.clone(), |v| &v.foo); /// let bar_rc: RcRef = RcRef::map(stuff_rc, |v| &v.bar); /// ``` #[derive(Debug)] pub struct RcRef { rc: Rc, value: *const T, } impl RcRef { pub fn new(value: T) -> Self { Self::from(Rc::new(value)) } pub fn map, F: FnOnce(&S) -> &T>( source: R, map_fn: F, ) -> RcRef { let RcRef:: { rc, value } = source.into(); // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let value = map_fn(unsafe { &*value }); RcRef { rc, value } } pub(crate) fn split(rc_ref: &Self) -> (&T, &Rc) { let &Self { ref rc, value } = rc_ref; // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] (unsafe { &*value }, rc) } } impl Default for RcRef { fn default() -> Self { Self::new(Default::default()) } } impl Clone for RcRef { fn clone(&self) -> Self { Self { rc: self.rc.clone(), value: self.value, } } } impl From<&RcRef> for RcRef { fn from(rc_ref: &RcRef) -> Self { rc_ref.clone() } } impl From> for RcRef { fn from(rc: Rc) -> Self { Self { value: &*rc, rc: rc as Rc<_>, } } } impl From<&Rc> for RcRef { fn from(rc: &Rc) -> Self { rc.clone().into() } } impl Deref for RcRef { type Target = T; fn deref(&self) -> &Self::Target { // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] unsafe { &*self.value } } } impl Borrow for RcRef { fn borrow(&self) -> &T { self } } impl AsRef for RcRef { fn as_ref(&self) -> &T { self } } /// The `RcLike` trait provides an abstraction over `std::rc::Rc` and `RcRef`, /// so that applicable methods can operate on either type. pub trait RcLike: AsRef + Into> {} impl RcLike for Rc {} impl RcLike for RcRef {} impl RcLike for &Rc {} impl RcLike for &RcRef {} mod internal { use super::AsyncRefCell; use super::RcLike; use super::RcRef; use futures::future::Future; use futures::ready; use futures::task::Context; use futures::task::Poll; use futures::task::Waker; use std::borrow::Borrow; use std::borrow::BorrowMut; use std::fmt::Debug; use std::marker::PhantomData; use std::ops::Deref; use std::ops::DerefMut; use std::pin::Pin; impl AsyncRefCell { /// Borrow the cell's contents synchronously without creating an /// intermediate future. If the cell has already been borrowed and either /// the existing or the requested borrow is exclusive, this function returns /// `None`. pub fn borrow_sync>>( cell: R, ) -> Option> { let cell_ref = cell.as_ref(); // Don't allow synchronous borrows to cut in line; if there are any // enqueued waiters, return `None`, even if the current borrow is a shared // one and the requested borrow is too. // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let waiters = unsafe { &mut *cell_ref.waiters.as_ptr() }; if waiters.is_empty() { // There are no enqueued waiters, but it is still possible that the cell // is currently borrowed. If there are no current borrows, or both the // existing and requested ones are shared, `try_add()` returns the // adjusted borrow count. let new_borrow_count = cell_ref.borrow_count.get().try_add(M::borrow_mode())?; cell_ref.borrow_count.set(new_borrow_count); Some(AsyncBorrowImpl::::new(cell.into())) } else { None } } fn drop_borrow(&self) { let new_borrow_count = self.borrow_count.get().remove(M::borrow_mode()); self.borrow_count.set(new_borrow_count); if new_borrow_count.is_empty() { self.wake_waiters() } } fn create_waiter(&self) -> usize { let waiter = Waiter::new(M::borrow_mode()); let turn = self.turn.get(); let index = { // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let waiters = unsafe { &mut *self.waiters.as_ptr() }; waiters.push_back(Some(waiter)); waiters.len() - 1 }; if index == 0 { // SAFETY: the `waiters` reference used above *must* be dropped here. self.wake_waiters() } // Return the new waiter's id. turn + index } fn poll_waiter( &self, id: usize, cx: &mut Context, ) -> Poll<()> { let borrow_count = self.borrow_count.get(); let turn = self.turn.get(); if id < turn { // This waiter made it to the front of the line; we reserved a borrow // for it, woke its Waker, and removed the waiter from the queue. // Assertion: BorrowCount::remove() will panic if `mode` is incorrect. let _ = borrow_count.remove(M::borrow_mode()); Poll::Ready(()) } else { // This waiter is still in line and has not yet been woken. // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let waiters = unsafe { &mut *self.waiters.as_ptr() }; // Sanity check: id cannot be higher than the last queue element. assert!(id < turn + waiters.len()); // Sanity check: since we always call wake_waiters() when the queue head // is updated, it should be impossible to add it to the current borrow. assert!(id > turn || borrow_count.try_add(M::borrow_mode()).is_none()); // Save or update the waiter's Waker. let waiter_mut = waiters[id - turn].as_mut().unwrap(); waiter_mut.set_waker(cx.waker()); Poll::Pending } } fn wake_waiters(&self) { let mut borrow_count = self.borrow_count.get(); // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let waiters = unsafe { &mut *self.waiters.as_ptr() }; let mut turn = self.turn.get(); loop { let waiter_entry = match waiters.front().map(Option::as_ref) { None => break, // Queue empty. Some(w) => w, }; let borrow_mode = match waiter_entry { None => { // Queue contains a hole. This happens when a Waiter is dropped // before it makes it to the front of the queue. waiters.pop_front(); turn += 1; continue; } Some(waiter) => waiter.borrow_mode(), }; // See if the waiter at the front of the queue can borrow the cell's // value now. If it does, `try_add()` returns the new borrow count, // effectively "reserving" the borrow until the associated // AsyncBorrowFutureImpl future gets polled and produces the actual // borrow. borrow_count = match borrow_count.try_add(borrow_mode) { None => break, // Can't borrow yet. Some(b) => b, }; // Drop from queue. let mut waiter = waiters.pop_front().unwrap().unwrap(); turn += 1; // Wake this waiter, so the AsyncBorrowFutureImpl future gets polled. if let Some(waker) = waiter.take_waker() { waker.wake() } } // Save updated counters. self.borrow_count.set(borrow_count); self.turn.set(turn); } fn drop_waiter(&self, id: usize) { let turn = self.turn.get(); if id < turn { // We already made a borrow count reservation for this waiter but the // borrow will never be picked up and removesequently, never dropped. // Therefore, call the borrow drop handler here. self.drop_borrow::(); } else { // This waiter is still in the queue, take it out and leave a "hole". // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let waiters = unsafe { &mut *self.waiters.as_ptr() }; waiters[id - turn].take().unwrap(); } if id == turn { // Since the first entry in the waiter queue was touched we have to // reprocess the waiter queue. self.wake_waiters() } } } pub struct AsyncBorrowFutureImpl { cell: Option>>, id: usize, _phantom: PhantomData, } impl AsyncBorrowFutureImpl { pub fn new>>(cell: R) -> Self { Self { id: cell.as_ref().create_waiter::(), cell: Some(cell.into()), _phantom: PhantomData, } } } impl Future for AsyncBorrowFutureImpl { type Output = AsyncBorrowImpl; fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { ready!(self.cell.as_ref().unwrap().poll_waiter::(self.id, cx)); // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] let self_mut = unsafe { Pin::get_unchecked_mut(self) }; let cell = self_mut.cell.take().unwrap(); Poll::Ready(AsyncBorrowImpl::::new(cell)) } } impl Drop for AsyncBorrowFutureImpl { fn drop(&mut self) { // The expected mode of operation is that this future gets polled until it // is ready and yields a value of type `AsyncBorrowImpl`, which has a drop // handler that adjusts the `AsyncRefCell` borrow counter. However if the // `cell` field still holds a value at this point, it means that the // future was never polled to completion and no `AsyncBorrowImpl` was ever // created, so we have to adjust the borrow count here. if let Some(cell) = self.cell.take() { cell.drop_waiter::(self.id) } } } pub struct AsyncBorrowImpl { cell: RcRef>, _phantom: PhantomData, } impl AsyncBorrowImpl { fn new(cell: RcRef>) -> Self { Self { cell, _phantom: PhantomData, } } } impl Deref for AsyncBorrowImpl { type Target = T; fn deref(&self) -> &Self::Target { // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] unsafe { &*self.cell.as_ptr() } } } impl Borrow for AsyncBorrowImpl { fn borrow(&self) -> &T { self } } impl AsRef for AsyncBorrowImpl { fn as_ref(&self) -> &T { self } } impl DerefMut for AsyncBorrowImpl { fn deref_mut(&mut self) -> &mut Self::Target { // TODO(piscisaureus): safety comment #[allow(clippy::undocumented_unsafe_blocks)] unsafe { &mut *self.cell.as_ptr() } } } impl BorrowMut for AsyncBorrowImpl { fn borrow_mut(&mut self) -> &mut T { self } } impl AsMut for AsyncBorrowImpl { fn as_mut(&mut self) -> &mut T { self } } impl Drop for AsyncBorrowImpl { fn drop(&mut self) { self.cell.drop_borrow::() } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum BorrowMode { Shared, Exclusive, } pub trait BorrowModeTrait: Copy { fn borrow_mode() -> BorrowMode; } #[derive(Copy, Clone, Debug)] pub struct Shared; impl BorrowModeTrait for Shared { fn borrow_mode() -> BorrowMode { BorrowMode::Shared } } #[derive(Copy, Clone, Debug)] pub struct Exclusive; impl BorrowModeTrait for Exclusive { fn borrow_mode() -> BorrowMode { BorrowMode::Exclusive } } #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum BorrowCount { Shared(usize), Exclusive, } impl Default for BorrowCount { fn default() -> Self { Self::Shared(0) } } impl BorrowCount { pub fn is_empty(self) -> bool { matches!(self, BorrowCount::Shared(0)) } pub fn try_add(self, mode: BorrowMode) -> Option { match (self, mode) { (BorrowCount::Shared(refs), BorrowMode::Shared) => { Some(BorrowCount::Shared(refs + 1)) } (BorrowCount::Shared(0), BorrowMode::Exclusive) => { Some(BorrowCount::Exclusive) } _ => None, } } #[allow(dead_code)] pub fn add(self, mode: BorrowMode) -> BorrowCount { match self.try_add(mode) { Some(value) => value, None => panic!("Can't add {mode:?} to {self:?}"), } } pub fn try_remove(self, mode: BorrowMode) -> Option { match (self, mode) { (BorrowCount::Shared(refs), BorrowMode::Shared) if refs > 0 => { Some(BorrowCount::Shared(refs - 1)) } (BorrowCount::Exclusive, BorrowMode::Exclusive) => { Some(BorrowCount::Shared(0)) } _ => None, } } pub fn remove(self, mode: BorrowMode) -> BorrowCount { match self.try_remove(mode) { Some(value) => value, None => panic!("Can't remove {mode:?} from {self:?}"), } } } /// The `waiters` queue that is associated with an individual `AsyncRefCell` /// contains elements of the `Waiter` type. pub struct Waiter { borrow_mode: BorrowMode, waker: Option, } impl Waiter { pub fn new(borrow_mode: BorrowMode) -> Self { Self { borrow_mode, waker: None, } } pub fn borrow_mode(&self) -> BorrowMode { self.borrow_mode } pub fn set_waker(&mut self, new_waker: &Waker) { if self .waker .as_ref() .filter(|waker| waker.will_wake(new_waker)) .is_none() { self.waker.replace(new_waker.clone()); } } pub fn take_waker(&mut self) -> Option { self.waker.take() } } } #[cfg(test)] mod tests { use super::*; #[derive(Default)] struct Thing { touch_count: usize, _private: (), } impl Thing { pub fn look(&self) -> usize { self.touch_count } pub fn touch(&mut self) -> usize { self.touch_count += 1; self.touch_count } } #[tokio::test] async fn async_ref_cell_borrow() { let cell = AsyncRefCell::::default_rc(); let fut1 = cell.borrow(); let fut2 = cell.borrow_mut(); let fut3 = cell.borrow(); let fut4 = cell.borrow(); let fut5 = cell.borrow(); let fut6 = cell.borrow(); let fut7 = cell.borrow_mut(); let fut8 = cell.borrow(); // The `try_borrow` and `try_borrow_mut` methods should always return `None` // if there's a queue of async borrowers. assert!(cell.try_borrow().is_none()); assert!(cell.try_borrow_mut().is_none()); assert_eq!(fut1.await.look(), 0); assert_eq!(fut2.await.touch(), 1); { let ref5 = fut5.await; let ref4 = fut4.await; let ref3 = fut3.await; let ref6 = fut6.await; assert_eq!(ref3.look(), 1); assert_eq!(ref4.look(), 1); assert_eq!(ref5.look(), 1); assert_eq!(ref6.look(), 1); } { let mut ref7 = fut7.await; assert_eq!(ref7.look(), 1); assert_eq!(ref7.touch(), 2); } { let ref8 = fut8.await; assert_eq!(ref8.look(), 2); } } #[test] fn async_ref_cell_try_borrow() { let cell = AsyncRefCell::::default_rc(); { let ref1 = cell.try_borrow().unwrap(); assert_eq!(ref1.look(), 0); assert!(cell.try_borrow_mut().is_none()); } { let mut ref2 = cell.try_borrow_mut().unwrap(); assert_eq!(ref2.touch(), 1); assert!(cell.try_borrow().is_none()); assert!(cell.try_borrow_mut().is_none()); } { let ref3 = cell.try_borrow().unwrap(); let ref4 = cell.try_borrow().unwrap(); let ref5 = cell.try_borrow().unwrap(); let ref6 = cell.try_borrow().unwrap(); assert_eq!(ref3.look(), 1); assert_eq!(ref4.look(), 1); assert_eq!(ref5.look(), 1); assert_eq!(ref6.look(), 1); assert!(cell.try_borrow_mut().is_none()); } { let mut ref7 = cell.try_borrow_mut().unwrap(); assert_eq!(ref7.look(), 1); assert_eq!(ref7.touch(), 2); assert!(cell.try_borrow().is_none()); assert!(cell.try_borrow_mut().is_none()); } { let ref8 = cell.try_borrow().unwrap(); assert_eq!(ref8.look(), 2); assert!(cell.try_borrow_mut().is_none()); assert!(cell.try_borrow().is_some()); } } #[derive(Default)] struct ThreeThings { pub thing1: AsyncRefCell, pub thing2: AsyncRefCell, pub thing3: AsyncRefCell, } #[tokio::test] async fn rc_ref_map() { let three_cells = Rc::new(ThreeThings::default()); let rc1 = RcRef::map(three_cells.clone(), |things| &things.thing1); let rc2 = RcRef::map(three_cells.clone(), |things| &things.thing2); let rc3 = RcRef::map(three_cells, |things| &things.thing3); let mut ref1 = rc1.borrow_mut().await; let ref2 = rc2.borrow().await; let mut ref3 = rc3.borrow_mut().await; assert_eq!(ref1.look(), 0); assert_eq!(ref3.touch(), 1); assert_eq!(ref1.touch(), 1); assert_eq!(ref2.look(), 0); assert_eq!(ref3.touch(), 2); assert_eq!(ref1.look(), 1); assert_eq!(ref1.touch(), 2); assert_eq!(ref3.touch(), 3); assert_eq!(ref1.touch(), 3); } }