Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 8 additions & 12 deletions src/policies/wrapped.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,16 @@ use crate::policies::traits::VacantExt;
///
/// The shared (lock-free) fields of the policy are accessible directly via
/// [`Wrapped::shared`], while mutable state is accessed through the inner
/// [`std::sync::Mutex`].
/// [`parking_lot::Mutex`].
pub struct Wrapped<P: PolicyExt> {
/// Read-only fields after initialization — no lock required.
/// Accessible directly without acquiring the mutex.
shared: P::Shared,
/// Mutable policy state — protected by a [`std::sync::Mutex`].
/// Mutable policy state — protected by a [`parking_lot::Mutex`].
///
/// Wait for it with [`Self::policy_py_attached`] (or [`Self::try_policy`]), never
/// with a plain `lock()`: a thread blocked while attached deadlocks with a lock
/// holder that needs the interpreter (#95).
inner: parking_lot::Mutex<P>,
}

Expand All @@ -46,14 +50,6 @@ impl<P: PolicyExt> Wrapped<P> {
&self.shared
}

/// Acquires the mutex and returns a guard over the mutable policy state.
///
/// Prefer [`Self::policy_py_attached`] when GIL is attached.
#[inline(always)]
pub fn policy(&self) -> parking_lot::MutexGuard<'_, P> {
self.inner.lock()
}

/// Acquires the mutex and returns a guard over the mutable policy state.
// https://github.com/awolverp/cachebox/issues/95
#[inline(always)]
Expand Down Expand Up @@ -115,8 +111,8 @@ fn insert_inner<P: PolicyExt>(
impl<P: PolicyExt> Wrapped<P> {
/// Returns the remaining size. Equals to `maxsize - current_size`.
#[inline]
pub fn remaining_size(&self) -> usize {
let policy = self.inner.lock();
pub fn remaining_size(&self, py: pyo3::Python<'_>) -> usize {
let policy = self.policy_py_attached(py);
self.shared.maxsize().saturating_sub(policy.current_size())
}

Expand Down
48 changes: 24 additions & 24 deletions src/pyclasses/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,15 @@ impl PyCache {
}

#[inline]
fn current_size(&self) -> pyo3::PyResult<usize> {
fn current_size(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
Ok(inner.policy().current_size())
Ok(inner.policy_py_attached(py).current_size())
}

#[inline]
fn remaining_size(&self) -> pyo3::PyResult<usize> {
fn remaining_size(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
Ok(inner.remaining_size())
Ok(inner.remaining_size(py))
}

#[getter]
Expand All @@ -137,41 +137,41 @@ impl PyCache {

/// Returns the number of elements the map can hold without reallocating.
#[inline]
fn capacity(&self) -> pyo3::PyResult<usize> {
fn capacity(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.table().capacity())
}

/// Returns the number of entries currently in the cache.
#[inline]
fn __len__(&self) -> pyo3::PyResult<usize> {
fn __len__(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.table().len())
}

#[inline]
fn __sizeof__(&self) -> usize {
fn __sizeof__(&self, py: pyo3::Python) -> usize {
const FIXED_SIZE: usize = size_of::<Wrapped<nopolicy::NoPolicy>>();

let Ok(inner) = self.0.get() else {
return FIXED_SIZE;
};
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

FIXED_SIZE + (policy.table().capacity() * size_of::<nopolicy::Handle>())
}

#[inline]
fn __bool__(&self) -> bool {
fn __bool__(&self, py: pyo3::Python) -> bool {
let Ok(inner) = self.0.get() else {
return false;
};

let policy = inner.policy();
let policy = inner.policy_py_attached(py);

!policy.table().is_empty()
}
Expand All @@ -191,19 +191,19 @@ impl PyCache {

/// Returns `True` if cache is empty.
#[inline]
fn is_empty(&self) -> pyo3::PyResult<bool> {
fn is_empty(&self, py: pyo3::Python) -> pyo3::PyResult<bool> {
let inner = self.0.get()?;
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.table().is_empty())
}

/// Returns `True` when the cumulative size has reached the maxsize limit.
#[inline]
fn is_full(&self) -> pyo3::PyResult<bool> {
fn is_full(&self, py: pyo3::Python) -> pyo3::PyResult<bool> {
let inner = self.0.get()?;
let shared = inner.shared();
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.current_size() >= shared.maxsize())
}
Expand Down Expand Up @@ -455,9 +455,9 @@ impl PyCache {
/// Remove and return a (key, value) pair as a 2-tuple.
///
/// NOTE: `Cache` always raises `NotImplementedError` because has neither policy nor algorithm to evict items.
fn popitem(&self) -> pyo3::PyResult<(alias::PyObject, alias::PyObject)> {
fn popitem(&self, py: pyo3::Python) -> pyo3::PyResult<(alias::PyObject, alias::PyObject)> {
let inner = self.0.get()?;
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);

let handle = policy.evict(inner.shared())?;
drop(policy);
Expand All @@ -479,19 +479,19 @@ impl PyCache {

/// Shrinks the internal allocation as close to the current length as possible.
#[inline]
fn shrink_to_fit(&self) -> pyo3::PyResult<()> {
fn shrink_to_fit(&self, py: pyo3::Python) -> pyo3::PyResult<()> {
let inner = self.0.get()?;
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);
policy.shrink_to_fit(inner.shared());
Ok(())
}

/// Removes all entries from the table and resets the cumulative size to zero.
#[pyo3(signature=(*, reuse=false))]
fn clear(&self, reuse: bool) -> pyo3::PyResult<()> {
fn clear(&self, py: pyo3::Python, reuse: bool) -> pyo3::PyResult<()> {
let inner = self.0.get()?;
let shared = inner.shared();
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);

policy.clear(shared);

Expand Down Expand Up @@ -668,9 +668,9 @@ impl PyCache {
Ok(())
}

fn __clear__(&self) {
fn __clear__(&self, py: pyo3::Python) {
if let Ok(inner) = self.0.get() {
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);
policy.clear(inner.shared());
} else {
// object is not initialized, so there's nothing to clear
Expand Down
48 changes: 24 additions & 24 deletions src/pyclasses/fifocache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,15 +123,15 @@ impl PyFIFOCache {
}

#[inline]
fn current_size(&self) -> pyo3::PyResult<usize> {
fn current_size(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
Ok(inner.policy().current_size())
Ok(inner.policy_py_attached(py).current_size())
}

#[inline]
fn remaining_size(&self) -> pyo3::PyResult<usize> {
fn remaining_size(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
Ok(inner.remaining_size())
Ok(inner.remaining_size(py))
}

#[getter]
Expand All @@ -143,43 +143,43 @@ impl PyFIFOCache {

/// Returns the number of elements the map can hold without reallocating.
#[inline]
fn capacity(&self) -> pyo3::PyResult<usize> {
fn capacity(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.table().capacity().min(policy.entries().capacity()))
}

/// Returns the number of entries currently in the cache.
#[inline]
fn __len__(&self) -> pyo3::PyResult<usize> {
fn __len__(&self, py: pyo3::Python) -> pyo3::PyResult<usize> {
let inner = self.0.get()?;
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

debug_assert!(policy.table().len() == policy.entries().len());
Ok(policy.table().len())
}

#[inline]
fn __sizeof__(&self) -> usize {
fn __sizeof__(&self, py: pyo3::Python) -> usize {
const FIXED_SIZE: usize = size_of::<Wrapped<fifopolicy::FIFOPolicy>>();

let Ok(inner) = self.0.get() else {
return FIXED_SIZE;
};
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

let table_cap = policy.table().capacity() * size_of::<usize>();
let vecdeque_cap = policy.entries().capacity() * size_of::<fifopolicy::Handle>();
FIXED_SIZE + table_cap + vecdeque_cap
}

#[inline]
fn __bool__(&self) -> bool {
fn __bool__(&self, py: pyo3::Python) -> bool {
let Ok(inner) = self.0.get() else {
return false;
};
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

!policy.table().is_empty()
}
Expand All @@ -199,19 +199,19 @@ impl PyFIFOCache {

/// Returns `True` if cache is empty.
#[inline]
fn is_empty(&self) -> pyo3::PyResult<bool> {
fn is_empty(&self, py: pyo3::Python) -> pyo3::PyResult<bool> {
let inner = self.0.get()?;
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.table().is_empty())
}

/// Returns `True` when the cumulative size has reached the maxsize limit.
#[inline]
fn is_full(&self) -> pyo3::PyResult<bool> {
fn is_full(&self, py: pyo3::Python) -> pyo3::PyResult<bool> {
let inner = self.0.get()?;
let shared = inner.shared();
let policy = inner.policy();
let policy = inner.policy_py_attached(py);

Ok(policy.current_size() >= shared.maxsize())
}
Expand Down Expand Up @@ -458,9 +458,9 @@ impl PyFIFOCache {
}

/// Remove and return a (key, value) pair as a 2-tuple.
fn popitem(&self) -> pyo3::PyResult<(alias::PyObject, alias::PyObject)> {
fn popitem(&self, py: pyo3::Python) -> pyo3::PyResult<(alias::PyObject, alias::PyObject)> {
let inner = self.0.get()?;
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);

let handle = policy.evict(inner.shared())?;
drop(policy);
Expand All @@ -482,19 +482,19 @@ impl PyFIFOCache {

/// Shrinks the internal allocation as close to the current length as possible.
#[inline]
fn shrink_to_fit(&self) -> pyo3::PyResult<()> {
fn shrink_to_fit(&self, py: pyo3::Python) -> pyo3::PyResult<()> {
let inner = self.0.get()?;
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);
policy.shrink_to_fit(inner.shared());
Ok(())
}

/// Removes all entries from the table and resets the cumulative size to zero.
#[pyo3(signature=(*, reuse=false))]
fn clear(&self, reuse: bool) -> pyo3::PyResult<()> {
fn clear(&self, py: pyo3::Python, reuse: bool) -> pyo3::PyResult<()> {
let inner = self.0.get()?;
let shared = inner.shared();
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);

policy.clear(shared);

Expand Down Expand Up @@ -692,12 +692,12 @@ impl PyFIFOCache {
Ok(())
}

fn __clear__(&self) {
fn __clear__(&self, py: pyo3::Python) {
let Ok(inner) = self.0.get() else {
// object is not initialized, so there's nothing to clear
return;
};
let mut policy = inner.policy();
let mut policy = inner.policy_py_attached(py);
policy.clear(inner.shared());
}
}
Expand Down
Loading
Loading