Skip to content

Commit

Permalink
Add the remove method
Browse files Browse the repository at this point in the history
  • Loading branch information
tatsuya6502 committed Apr 23, 2023
1 parent 0f1f6a5 commit 7cf4ada
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 40 deletions.
77 changes: 61 additions & 16 deletions src/future/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1232,6 +1232,9 @@ where

/// Discards any cached value for the key.
///
/// If you need to get the value that has been discarded, use the
/// [`remove`](#method.remove) method instead.
///
/// The key may be any borrowed form of the cache's key type, but `Hash` and `Eq`
/// on the borrowed form _must_ match those for the key type.
pub async fn invalidate<Q>(&self, key: &Q)
Expand All @@ -1240,23 +1243,58 @@ where
Q: Hash + Eq + ?Sized,
{
let hash = self.base.hash(key);
if let Some(kv) = self.base.remove_entry(key, hash) {
if self.base.is_removal_notifier_enabled() {
self.base.notify_invalidate(&kv.key, &kv.entry)
self.invalidate_with_hash(key, hash, false).await;
}

/// Discards any cached value for the key and returns a _clone_ of the value.
///
/// If you do not need to get the value that has been discarded, use the
/// [`invalidate`](#method.invalidate) method instead.
///
/// The key may be any borrowed form of the cache's key type, but `Hash` and `Eq`
/// on the borrowed form _must_ match those for the key type.
pub async fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.base.hash(key);
self.invalidate_with_hash(key, hash, true).await
}

pub async fn invalidate_with_hash<Q>(&self, key: &Q, hash: u64, need_value: bool) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
match self.base.remove_entry(key, hash) {
None => None,
Some(kv) => {
let maybe_v = if need_value {
Some(kv.entry.value.clone())
} else {
None
};

if self.base.is_removal_notifier_enabled() {
self.base.notify_invalidate(&kv.key, &kv.entry)
}

let op = WriteOp::Remove(kv);
let now = self.base.current_time_from_expiration_clock();
let hk = self.base.housekeeper.as_ref();
Self::schedule_write_op(
self.base.inner.as_ref(),
&self.base.write_op_ch,
op,
now,
hk,
)
.await
.expect("Failed to remove");
crossbeam_epoch::pin().flush();
maybe_v
}
let op = WriteOp::Remove(kv);
let now = self.base.current_time_from_expiration_clock();
let hk = self.base.housekeeper.as_ref();
Self::schedule_write_op(
self.base.inner.as_ref(),
&self.base.write_op_ch,
op,
now,
hk,
)
.await
.expect("Failed to remove");
crossbeam_epoch::pin().flush();
}
}

Expand Down Expand Up @@ -1966,6 +2004,13 @@ mod tests {
assert_eq!(cache.get(&"b"), None);
assert!(!cache.contains_key(&"b"));

assert!(cache.remove(&"b").await.is_none());
assert_eq!(cache.remove(&"d").await, Some("dennis"));
expected.push((Arc::new("d"), "dennis", RemovalCause::Explicit));
cache.sync();
assert_eq!(cache.get(&"d"), None);
assert!(!cache.contains_key(&"d"));

verify_notification_vec(&cache, actual, &expected);
}

Expand Down
82 changes: 59 additions & 23 deletions src/sync/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,9 @@ where

/// Discards any cached value for the key.
///
/// If you need to get a the value that has been discarded, use the
/// [`remove`](#method.remove) method instead.
///
/// The key may be any borrowed form of the cache's key type, but `Hash` and `Eq`
/// on the borrowed form _must_ match those for the key type.
pub fn invalidate<Q>(&self, key: &Q)
Expand All @@ -1626,10 +1629,26 @@ where
Q: Hash + Eq + ?Sized,
{
let hash = self.base.hash(key);
self.invalidate_with_hash(key, hash);
self.invalidate_with_hash(key, hash, false);
}

/// Discards any cached value for the key and returns a _clone_ of the value.
///
/// If you do not need to get the value that has been discarded, use the
/// [`invalidate`](#method.invalidate) method instead.
///
/// The key may be any borrowed form of the cache's key type, but `Hash` and `Eq`
/// on the borrowed form _must_ match those for the key type.
pub fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.base.hash(key);
self.invalidate_with_hash(key, hash, true)
}

pub(crate) fn invalidate_with_hash<Q>(&self, key: &Q, hash: u64)
pub(crate) fn invalidate_with_hash<Q>(&self, key: &Q, hash: u64, need_value: bool) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
Expand All @@ -1654,28 +1673,38 @@ where
}
}

if let Some(kv) = self.base.remove_entry(key, hash) {
if self.base.is_removal_notifier_enabled() {
self.base.notify_invalidate(&kv.key, &kv.entry)
match self.base.remove_entry(key, hash) {
None => None,
Some(kv) => {
if self.base.is_removal_notifier_enabled() {
self.base.notify_invalidate(&kv.key, &kv.entry)
}
// Drop the locks before scheduling write op to avoid a potential dead lock.
// (Scheduling write can do spin lock when the queue is full, and queue will
// be drained by the housekeeping thread that can lock the same key)
std::mem::drop(klg);
std::mem::drop(kl);

let maybe_v = if need_value {
Some(kv.entry.value.clone())
} else {
None
};

let op = WriteOp::Remove(kv);
let now = self.base.current_time_from_expiration_clock();
let hk = self.base.housekeeper.as_ref();
Self::schedule_write_op(
self.base.inner.as_ref(),
&self.base.write_op_ch,
op,
now,
hk,
)
.expect("Failed to remove");
crossbeam_epoch::pin().flush();
maybe_v
}
// Drop the locks before scheduling write op to avoid a potential dead lock.
// (Scheduling write can do spin lock when the queue is full, and queue will
// be drained by the housekeeping thread that can lock the same key)
std::mem::drop(klg);
std::mem::drop(kl);

let op = WriteOp::Remove(kv);
let now = self.base.current_time_from_expiration_clock();
let hk = self.base.housekeeper.as_ref();
Self::schedule_write_op(
self.base.inner.as_ref(),
&self.base.write_op_ch,
op,
now,
hk,
)
.expect("Failed to remove");
crossbeam_epoch::pin().flush();
}
}

Expand Down Expand Up @@ -2020,6 +2049,13 @@ mod tests {
assert_eq_with_mode!(cache.get(&"b"), None, delivery_mode);
assert_with_mode!(!cache.contains_key(&"b"), delivery_mode);

assert!(cache.remove(&"b").is_none());
assert_eq!(cache.remove(&"d"), Some("dennis"));
expected.push((Arc::new("d"), "dennis", RemovalCause::Explicit));
cache.sync();
assert_eq!(cache.get(&"d"), None);
assert!(!cache.contains_key(&"d"));

verify_notification_vec(&cache, actual, &expected, delivery_mode);
}
}
Expand Down
28 changes: 27 additions & 1 deletion src/sync/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ where

/// Discards any cached value for the key.
///
/// If you need to get a the value that has been discarded, use the
/// [`remove`](#method.remove) method instead.
///
/// The key may be any borrowed form of the cache's key type, but `Hash` and `Eq`
/// on the borrowed form _must_ match those for the key type.
pub fn invalidate<Q>(&self, key: &Q)
Expand All @@ -476,7 +479,23 @@ where
Q: Hash + Eq + ?Sized,
{
let hash = self.inner.hash(key);
self.inner.select(hash).invalidate_with_hash(key, hash);
self.inner.select(hash).invalidate_with_hash(key, hash, false);
}

/// Discards any cached value for the key and returns a clone of the value.
///
/// If you do not need to get the value that has been discarded, use the
/// [`invalidate`](#method.invalidate) method instead.
///
/// The key may be any borrowed form of the cache's key type, but `Hash` and `Eq`
/// on the borrowed form _must_ match those for the key type.
pub fn remove<Q>(&self, key: &Q) -> Option<V>
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let hash = self.inner.hash(key);
self.inner.select(hash).invalidate_with_hash(key, hash, true)
}

/// Discards all cached values.
Expand Down Expand Up @@ -885,6 +904,13 @@ mod tests {
assert_eq_with_mode!(cache.get(&"b"), None, delivery_mode);
assert_with_mode!(!cache.contains_key(&"b"), delivery_mode);

assert!(cache.remove(&"b").is_none());
assert_eq!(cache.remove(&"d"), Some("dennis"));
expected.push((Arc::new("d"), "dennis", RemovalCause::Explicit));
cache.sync();
assert_eq!(cache.get(&"d"), None);
assert!(!cache.contains_key(&"d"));

verify_notification_vec(&cache, actual, &expected, delivery_mode);
}
}
Expand Down

0 comments on commit 7cf4ada

Please sign in to comment.