use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
use std::ops::{Deref, DerefMut};
use std::result;
use std::sync::atomic::{AtomicUsize, Ordering};
use crate::err::{PyErr, PyResult};
use crate::objects::{exc, PyObject};
use crate::python::{PyClone, Python};
#[derive(Debug)]
pub struct PySharedRefCell<T: ?Sized> {
state: PySharedState,
data: RefCell<T>,
}
impl<T> PySharedRefCell<T> {
pub fn new(value: T) -> PySharedRefCell<T> {
Self {
state: PySharedState::new(),
data: RefCell::new(value),
}
}
}
pub struct PySharedRef<'a, T: 'a + ?Sized> {
py: Python<'a>,
owner: &'a PyObject,
state: &'a PySharedState,
data: &'a RefCell<T>,
}
impl<'a, T: ?Sized> PySharedRef<'a, T> {
#[doc(hidden)]
pub unsafe fn new(py: Python<'a>, owner: &'a PyObject, data: &'a PySharedRefCell<T>) -> Self {
Self {
py,
owner,
state: &data.state,
data: &data.data,
}
}
pub fn borrow(&self) -> Ref<'a, T> {
self.try_borrow().expect("already mutably borrowed")
}
pub fn try_borrow(&self) -> result::Result<Ref<'a, T>, BorrowError> {
self.data.try_borrow()
}
pub fn borrow_mut(&self) -> RefMut<'a, T> {
self.try_borrow_mut().expect("already borrowed")
}
pub fn try_borrow_mut(&self) -> result::Result<RefMut<'a, T>, BorrowMutError> {
if self.state.current_borrow_count(self.py) > 0 {
let _dummy = self.data.borrow();
self.data.try_borrow_mut()?;
unreachable!("BorrowMutError must be returned");
}
let data_ref = self.data.try_borrow_mut()?;
self.state.increment_generation(self.py);
Ok(data_ref)
}
pub fn leak_immutable(&self) -> UnsafePyLeaked<&'static T> {
self.try_leak_immutable().expect("already mutably borrowed")
}
pub fn try_leak_immutable(&self) -> result::Result<UnsafePyLeaked<&'static T>, BorrowError> {
let data_ref = self.try_borrow()?;
let state_ptr: *const PySharedState = self.state;
let data_ptr: *const T = &*data_ref;
Ok(UnsafePyLeaked::<&'static T> {
owner: self.owner.clone_ref(self.py),
state: unsafe { &*state_ptr },
generation: self.state.current_generation(self.py),
data: unsafe { &*data_ptr },
})
}
}
#[derive(Debug)]
struct PySharedState {
borrow_count: AtomicUsize,
generation: AtomicUsize,
}
impl PySharedState {
fn new() -> PySharedState {
PySharedState {
borrow_count: AtomicUsize::new(0),
generation: AtomicUsize::new(0),
}
}
fn current_borrow_count(&self, _py: Python) -> usize {
self.borrow_count.load(Ordering::Relaxed)
}
fn increase_borrow_count(&self, _py: Python) {
self.borrow_count.fetch_add(1, Ordering::Relaxed);
}
fn decrease_borrow_count(&self, _py: Python) {
let prev_count = self.borrow_count.fetch_sub(1, Ordering::Relaxed);
assert!(prev_count > 0);
}
fn current_generation(&self, _py: Python) -> usize {
self.generation.load(Ordering::Relaxed)
}
fn increment_generation(&self, py: Python) {
assert_eq!(self.current_borrow_count(py), 0);
self.generation.fetch_add(1, Ordering::Relaxed);
}
}
struct BorrowPyShared<'a> {
py: Python<'a>,
state: &'a PySharedState,
}
impl<'a> BorrowPyShared<'a> {
fn new(py: Python<'a>, state: &'a PySharedState) -> BorrowPyShared<'a> {
state.increase_borrow_count(py);
BorrowPyShared { py, state }
}
}
impl<'a> Drop for BorrowPyShared<'a> {
fn drop(&mut self) {
self.state.decrease_borrow_count(self.py);
}
}
pub struct UnsafePyLeaked<T: ?Sized> {
owner: PyObject,
state: &'static PySharedState,
generation: usize,
data: T,
}
impl<T: ?Sized> UnsafePyLeaked<T> {
pub unsafe fn try_borrow<'a>(&'a self, py: Python<'a>) -> PyResult<PyLeakedRef<'a, T>> {
self.validate_generation(py)?;
Ok(PyLeakedRef {
_borrow: BorrowPyShared::new(py, self.state),
data: &self.data,
})
}
pub unsafe fn try_borrow_mut<'a>(
&'a mut self,
py: Python<'a>,
) -> PyResult<PyLeakedRefMut<'a, T>> {
self.validate_generation(py)?;
Ok(PyLeakedRefMut {
_borrow: BorrowPyShared::new(py, self.state),
data: &mut self.data,
})
}
fn validate_generation(&self, py: Python) -> PyResult<()> {
if self.state.current_generation(py) == self.generation {
Ok(())
} else {
Err(PyErr::new::<exc::RuntimeError, _>(
py,
"Cannot access to leaked reference after mutation",
))
}
}
}
impl<T> UnsafePyLeaked<T> {
pub unsafe fn map<U>(self, py: Python, f: impl FnOnce(T) -> U) -> UnsafePyLeaked<U> {
self.validate_generation(py)
.expect("map() over invalidated leaked reference");
let new_data = f(self.data);
UnsafePyLeaked {
owner: self.owner,
state: self.state,
generation: self.generation,
data: new_data,
}
}
}
pub struct PyLeakedRef<'a, T: 'a + ?Sized> {
_borrow: BorrowPyShared<'a>,
data: &'a T,
}
impl<'a, T: ?Sized> Deref for PyLeakedRef<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.data
}
}
pub struct PyLeakedRefMut<'a, T: 'a + ?Sized> {
_borrow: BorrowPyShared<'a>,
data: &'a mut T,
}
impl<'a, T: ?Sized> Deref for PyLeakedRefMut<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.data
}
}
impl<'a, T: ?Sized> DerefMut for PyLeakedRefMut<'a, T> {
fn deref_mut(&mut self) -> &mut T {
self.data
}
}