use parking_lot::Mutex;
use std::sync::{Arc, Weak};
pub(crate) struct Slot<T>(Arc<Mutex<Option<T>>>);
impl<T> Slot<T> {
pub(crate) fn new(value: T) -> Self {
Self(Arc::new(Mutex::new(Some(value))))
}
pub(crate) fn new_leased(value: T) -> (Self, Lease<T>) {
let mut slot = Self::new(value);
let lease = slot.lease().expect("BUG: new slot empty");
(slot, lease)
}
pub(crate) fn lease(&mut self) -> Option<Lease<T>> {
if let Some(value) = self.0.try_lock().and_then(|mut slot| slot.take()) {
Some(Lease::new(value, Arc::downgrade(&self.0)))
} else {
None
}
}
pub(crate) fn into_inner(self) -> Option<T> {
self.0.try_lock().and_then(|mut slot| slot.take())
}
}
#[derive(Debug)]
pub(crate) struct Lease<T>(lease::State<T>);
impl<T> Lease<T> {
fn new(value: T, slot: Weak<Mutex<Option<T>>>) -> Self {
Self(lease::State::new(value, slot))
}
pub(crate) fn steal(mut self) -> T {
self.0.steal()
}
}
impl<T> Drop for Lease<T> {
fn drop(&mut self) {
self.0.drop()
}
}
impl<T> AsRef<T> for Lease<T> {
fn as_ref(&self) -> &T {
self.0.as_ref()
}
}
impl<T> AsMut<T> for Lease<T> {
fn as_mut(&mut self) -> &mut T {
self.0.as_mut()
}
}
impl<T> std::ops::Deref for Lease<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.as_ref()
}
}
impl<T> std::ops::DerefMut for Lease<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.as_mut()
}
}
mod lease {
use std::sync::Weak;
use parking_lot::Mutex;
#[derive(Debug)]
pub(super) struct State<T>(Inner<T>);
#[derive(Debug)]
enum Inner<T> {
Dropped,
Stolen,
Live {
value: T,
slot: Weak<Mutex<Option<T>>>,
},
}
impl<T> State<T> {
pub(super) fn new(value: T, slot: Weak<Mutex<Option<T>>>) -> Self {
Self(Inner::Live { value, slot })
}
pub(super) fn as_ref(&self) -> &T {
match &self.0 {
Inner::Dropped | Inner::Stolen => panic!("BUG: LeaseState used after drop/steal"),
Inner::Live { value, .. } => value,
}
}
pub(super) fn as_mut(&mut self) -> &mut T {
match &mut self.0 {
Inner::Dropped | Inner::Stolen => panic!("BUG: LeaseState used after drop/steal"),
Inner::Live { value, .. } => value,
}
}
pub(super) fn drop(&mut self) {
match std::mem::replace(&mut self.0, Inner::Dropped) {
Inner::Dropped => panic!("BUG: LeaseState::drop called twice"),
Inner::Stolen => {} Inner::Live { value, slot } => {
if let Some(slot) = slot.upgrade() {
if let Some(mut slot) = slot.try_lock() {
assert!(slot.is_none(), "BUG: slot repopulated during lease");
*slot = Some(value);
}
}
}
}
}
pub(super) fn steal(&mut self) -> T {
match std::mem::replace(&mut self.0, Inner::Stolen) {
Inner::Dropped => panic!("BUG: LeaseState::steal called after drop"),
Inner::Stolen => panic!("BUG: LeaseState::steal called twice"),
Inner::Live { value, .. } => value,
}
}
}
}
#[cfg(test)]
mod tests {
use super::Slot;
#[test]
fn lease_and_return() {
let mut slot = Slot::new("Hello".to_string());
let mut lease = slot.lease().unwrap();
std::thread::spawn(move || {
lease.push_str(", world!");
})
.join()
.unwrap();
assert_eq!(
slot.lease().as_deref().map(|s| s.as_str()),
Some("Hello, world!")
);
assert_eq!(slot.into_inner(), Some("Hello, world!".to_string()));
}
#[test]
fn lease_and_steal() {
let mut slot = Slot::new("Hello".to_string());
let lease = slot.lease().unwrap();
std::thread::spawn(move || {
let _: String = lease.steal();
})
.join()
.unwrap();
assert!(slot.lease().is_none());
}
}