Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change the trait bound Arc<K>: Borrow<Q> to K: Borrow<Q> (sync and future caches) #167

Merged
merged 7 commits into from
Jul 16, 2022
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
1 change: 1 addition & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"deqs",
"Deque",
"Deques",
"docsrs",
"Einziger",
"else's",
"Eytan",
Expand Down
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
# Moka Cache &mdash; Change Log

## Version 0.9.1

### Fixed (Changed)

- Relax the too restrictive requirement `Arc<K>: Borrow<Q>` to `K: Borrow<Q>` for the
`contains_key`, `get` and `invalidate` methods of the following caches, so that
they will accept `&[u8]` as the key when `K` is `Vec<u8>`: ([#167][gh-pull-0167])
- `sync::Cache`
- `sync::SegmentedCache`
- `future::Cache`


## Version 0.9.0

### Added

- Add support for eviction listener to the following caches ([#145][gh-pull-0145]).
Eviction listener is a callback function that will be called when an entry is
removed from the cache.
removed from the cache:
- `sync::Cache`
- `sync::SegmentedCache`
- `future::Cache`
Expand Down Expand Up @@ -412,6 +424,7 @@ The minimum supported Rust version (MSRV) is now 1.51.0 (2021-03-25).
[gh-issue-0034]: https://github.com/moka-rs/moka/issues/34/
[gh-issue-0031]: https://github.com/moka-rs/moka/issues/31/

[gh-pull-0167]: https://github.com/moka-rs/moka/pull/167/
[gh-pull-0159]: https://github.com/moka-rs/moka/pull/159/
[gh-pull-0145]: https://github.com/moka-rs/moka/pull/145/
[gh-pull-0143]: https://github.com/moka-rs/moka/pull/143/
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "moka"
version = "0.9.0"
version = "0.9.1"
edition = "2018"
rust-version = "1.51"

Expand Down
88 changes: 54 additions & 34 deletions src/cht/map/bucket.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::{
borrow::Borrow,
hash::{BuildHasher, Hash, Hasher},
mem::{self, MaybeUninit},
ptr,
Expand Down Expand Up @@ -72,16 +71,12 @@ impl<K, V> Drop for BucketArray<K, V> {
}

impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
pub(crate) fn get<Q>(
pub(crate) fn get(
&self,
guard: &'g Guard,
hash: u64,
key: &Q,
) -> Result<Shared<'g, Bucket<K, V>>, RelocatedError>
where
Q: Eq + ?Sized,
K: Borrow<Q>,
{
mut eq: impl FnMut(&K) -> bool,
) -> Result<Shared<'g, Bucket<K, V>>, RelocatedError> {
let loop_result = self.probe_loop(guard, hash, |_, _, this_bucket_ptr| {
let this_bucket_ref = if let Some(r) = unsafe { this_bucket_ptr.as_ref() } {
r
Expand All @@ -90,7 +85,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
return ProbeLoopAction::Return(Shared::null());
};

if this_bucket_ref.key.borrow() != key {
if !eq(&this_bucket_ref.key) {
// Different key. Try next bucket
return ProbeLoopAction::Continue;
}
Expand All @@ -111,16 +106,14 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
}
}

pub(crate) fn remove_if<Q, F>(
pub(crate) fn remove_if<F>(
&self,
guard: &'g Guard,
hash: u64,
key: &Q,
mut eq: impl FnMut(&K) -> bool,
mut condition: F,
) -> Result<Shared<'g, Bucket<K, V>>, F>
where
Q: Eq + ?Sized,
K: Borrow<Q>,
F: FnMut(&K, &V) -> bool,
{
let loop_result = self.probe_loop(guard, hash, |_, this_bucket, this_bucket_ptr| {
Expand All @@ -133,7 +126,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {

let this_key = &this_bucket_ref.key;

if this_key.borrow() != key {
if !eq(this_key) {
// Different key. Try next bucket.
return ProbeLoopAction::Continue;
}
Expand Down Expand Up @@ -189,7 +182,7 @@ impl<'g, K: 'g + Eq, V: 'g> BucketArray<K, V> {
let state = maybe_state.take().unwrap();

if let Some(this_bucket_ref) = unsafe { this_bucket_ptr.as_ref() } {
if this_bucket_ref.key.borrow() != state.key() {
if &this_bucket_ref.key != state.key() {
// Different key. Try next bucket.
maybe_state = Some(state);
return ProbeLoopAction::Continue;
Expand Down Expand Up @@ -784,52 +777,79 @@ mod tests {
let h3 = hash(&build_hasher, k3);
let v3 = 15;

assert_eq!(buckets.get(guard, h1, k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h1, |&k| k == k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

assert!(matches!(
insert(&buckets, guard, k1, h1, || v1),
Ok(InsertionResult::Inserted)
));

assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1)));
assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(
into_value(buckets.get(guard, h1, |&k| k == k1)),
Ok(Some(v1))
);
assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

assert!(matches!(
insert(&buckets, guard, k2, h2, || v2),
Ok(InsertionResult::Inserted)
));

assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1)));
assert_eq!(into_value(buckets.get(guard, h2, k2)), Ok(Some(v2)));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(
into_value(buckets.get(guard, h1, |&k| k == k1)),
Ok(Some(v1))
);
assert_eq!(
into_value(buckets.get(guard, h2, |&k| k == k2)),
Ok(Some(v2))
);
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

assert!(matches!(
insert(&buckets, guard, k3, h3, || v3),
Ok(InsertionResult::Inserted)
));

assert_eq!(into_value(buckets.get(guard, h1, k1)), Ok(Some(v1)));
assert_eq!(into_value(buckets.get(guard, h2, k2)), Ok(Some(v2)));
assert_eq!(into_value(buckets.get(guard, h3, k3)), Ok(Some(v3)));

let b1 = buckets.remove_if(guard, h1, k1, |_, _| true).ok().unwrap();
assert_eq!(
into_value(buckets.get(guard, h1, |&k| k == k1)),
Ok(Some(v1))
);
assert_eq!(
into_value(buckets.get(guard, h2, |&k| k == k2)),
Ok(Some(v2))
);
assert_eq!(
into_value(buckets.get(guard, h3, |&k| k == k3)),
Ok(Some(v3))
);

let b1 = buckets
.remove_if(guard, h1, |&k| k == k1, |_, _| true)
.ok()
.unwrap();
assert!(is_tombstone(b1));
unsafe { defer_destroy_tombstone(guard, b1) };

let b2 = buckets.remove_if(guard, h2, k2, |_, _| true).ok().unwrap();
let b2 = buckets
.remove_if(guard, h2, |&k| k == k2, |_, _| true)
.ok()
.unwrap();
assert!(is_tombstone(b2));
unsafe { defer_destroy_tombstone(guard, b2) };

let b3 = buckets.remove_if(guard, h3, k3, |_, _| true).ok().unwrap();
let b3 = buckets
.remove_if(guard, h3, |&k| k == k3, |_, _| true)
.ok()
.unwrap();
assert!(is_tombstone(b3));
unsafe { defer_destroy_tombstone(guard, b3) };

assert_eq!(buckets.get(guard, h1, k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, k3), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h1, |&k| k == k1), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h2, |&k| k == k2), Ok(Shared::null()));
assert_eq!(buckets.get(guard, h3, |&k| k == k3), Ok(Shared::null()));

for this_bucket in buckets.buckets.iter() {
let this_bucket_ptr = this_bucket.swap(Shared::null(), Ordering::Relaxed, guard);
Expand Down
66 changes: 21 additions & 45 deletions src/cht/map/bucket_array_ref.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use super::bucket::{self, Bucket, BucketArray, InsertOrModifyState, RehashOp};

use std::{
borrow::Borrow,
hash::{BuildHasher, Hash},
sync::atomic::{AtomicUsize, Ordering},
};
Expand All @@ -19,17 +18,12 @@ where
K: Hash + Eq,
S: BuildHasher,
{
pub(crate) fn get_key_value_and_then<Q, F, T>(
pub(crate) fn get_key_value_and_then<T>(
&self,
key: &Q,
hash: u64,
with_entry: F,
) -> Option<T>
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
F: FnOnce(&K, &V) -> Option<T>,
{
mut eq: impl FnMut(&K) -> bool,
with_entry: impl FnOnce(&K, &V) -> Option<T>,
) -> Option<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand All @@ -38,7 +32,7 @@ where

loop {
match bucket_array_ref
.get(guard, hash, key)
.get(guard, hash, &mut eq)
.map(|p| unsafe { p.as_ref() })
{
Ok(Some(Bucket {
Expand All @@ -64,19 +58,13 @@ where
result
}

pub(crate) fn remove_entry_if_and<Q, F, G, T>(
pub(crate) fn remove_entry_if_and<T>(
&self,
key: &Q,
hash: u64,
mut condition: F,
with_previous_entry: G,
) -> Option<T>
where
Q: Hash + Eq + ?Sized,
K: Borrow<Q>,
F: FnMut(&K, &V) -> bool,
G: FnOnce(&K, &V) -> T,
{
mut eq: impl FnMut(&K) -> bool,
mut condition: impl FnMut(&K, &V) -> bool,
with_previous_entry: impl FnOnce(&K, &V) -> T,
) -> Option<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand All @@ -96,7 +84,7 @@ where
bucket_array_ref = bucket_array_ref.rehash(guard, self.build_hasher, rehash_op);
}

match bucket_array_ref.remove_if(guard, hash, key, condition) {
match bucket_array_ref.remove_if(guard, hash, &mut eq, condition) {
Ok(previous_bucket_ptr) => {
if let Some(previous_bucket_ref) = unsafe { previous_bucket_ptr.as_ref() } {
let Bucket {
Expand Down Expand Up @@ -129,17 +117,13 @@ where
result
}

pub(crate) fn insert_if_not_present_and<F, G, T>(
pub(crate) fn insert_if_not_present_and<T>(
&self,
key: K,
hash: u64,
on_insert: F,
with_existing_entry: G,
) -> Option<T>
where
F: FnOnce() -> V,
G: FnOnce(&K, &V) -> T,
{
on_insert: impl FnOnce() -> V,
with_existing_entry: impl FnOnce(&K, &V) -> T,
) -> Option<T> {
use bucket::InsertionResult;

let guard = &crossbeam_epoch::pin();
Expand Down Expand Up @@ -198,19 +182,14 @@ where
result
}

pub(crate) fn insert_with_or_modify_entry_and<T, F, G, H>(
pub(crate) fn insert_with_or_modify_entry_and<T>(
&self,
key: K,
hash: u64,
on_insert: F,
mut on_modify: G,
with_old_entry: H,
) -> Option<T>
where
F: FnOnce() -> V,
G: FnMut(&K, &V) -> V,
H: FnOnce(&K, &V) -> T,
{
on_insert: impl FnOnce() -> V,
mut on_modify: impl FnMut(&K, &V) -> V,
with_old_entry: impl FnOnce(&K, &V) -> T,
) -> Option<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand Down Expand Up @@ -267,10 +246,7 @@ where
result
}

pub(crate) fn keys<F, T>(&self, mut with_key: F) -> Vec<T>
where
F: FnMut(&K) -> T,
{
pub(crate) fn keys<T>(&self, mut with_key: impl FnMut(&K) -> T) -> Vec<T> {
let guard = &crossbeam_epoch::pin();
let current_ref = self.get(guard);
let mut bucket_array_ref = current_ref;
Expand Down
Loading