Skip to content

Commit

Permalink
Merge pull request #12 from moka-rs/invalidate-entries-if
Browse files Browse the repository at this point in the history
Add the `invalidate_entries_if` method
  • Loading branch information
tatsuya6502 authored Jun 20, 2021
2 parents e04d2ef + 7a192c1 commit 56ba3f0
Show file tree
Hide file tree
Showing 19 changed files with 1,493 additions and 115 deletions.
4 changes: 4 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"Kawano",
"MSRV",
"Moka",
"RUSTFLAGS",
"Ristretto",
"Tatsuya",
"Upsert",
Expand All @@ -21,11 +22,14 @@
"mpsc",
"nanos",
"nocapture",
"peekable",
"preds",
"runtimes",
"rustdoc",
"rustfmt",
"semver",
"structs",
"thiserror",
"toolchain",
"unsync",
"usize"
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
### Fixed

- **Breaking change**: Now `sync::{Cache, SegmentedCache}` and `future::Cache`
require `Send`, `Sync` and `'static` bounds for the generic parameters `K` (key),
require `Send`, `Sync` and `'static` for the generic parameters `K` (key),
`V` (value) and `S` (hasher state). This is necessary to prevent potential
undefined behaviors in applications using single-threaded async runtime such as
Actix-rt. ([#19][gh-pull-0019])

### Added

- Add `invalidate_entries_if` method to `sync`, `future` and `unsync` caches.
([#12][gh-pull-0012])

## Version 0.3.1

### Changed

- Stop skeptic from having to be compiled by all downstream users. ([#16][gh-pull-0016])


## Version 0.3.0

### Added
Expand Down Expand Up @@ -62,6 +67,7 @@

[gh-pull-0019]: https://github.com/moka-rs/moka/pull/19/
[gh-pull-0016]: https://github.com/moka-rs/moka/pull/16/
[gh-pull-0012]: https://github.com/moka-rs/moka/pull/12/
[gh-pull-0011]: https://github.com/moka-rs/moka/pull/11/
[gh-pull-0009]: https://github.com/moka-rs/moka/pull/9/
[gh-pull-0007]: https://github.com/moka-rs/moka/pull/7/
8 changes: 4 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "moka"
version = "0.3.1"
version = "0.4.0"
authors = ["Tatsuya Kawano <tatsuya@hibaridb.org>"]
edition = "2018"

Expand Down Expand Up @@ -29,10 +29,10 @@ crossbeam-channel = "0.5"
num_cpus = "1.13"
once_cell = "1.7"
parking_lot = "0.11"
# v0.7.1 or newer should be used as v0.7.0 will not compile on non-x86_64 platforms.
# https://github.com/metrics-rs/quanta/pull/38
quanta = "0.7.1"
quanta = "0.9"
scheduled-thread-pool = "0.2"
thiserror = "1.0"
uuid = { version = "0.8", features = ["v4"] }

# Optional dependencies
async-io = { version = "1", optional = true }
Expand Down
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ exceeded.
[deps-rs-badge]: https://deps.rs/repo/github/moka-rs/moka/status.svg
[coveralls-badge]: https://coveralls.io/repos/github/moka-rs/moka/badge.svg?branch=master
[license-badge]: https://img.shields.io/crates/l/moka.svg
[fossa-badge]: https://app.fossa.com/api/projects/git%2Bgithub.com%2Fmoka-rs%2Fmoka.svg?type=shield

[gh-actions]: https://github.com/moka-rs/moka/actions?query=workflow%3ACI
[crate]: https://crates.io/crates/moka
[docs]: https://docs.rs/moka
[deps-rs]: https://deps.rs/repo/github/moka-rs/moka
[coveralls]: https://coveralls.io/github/moka-rs/moka?branch=master
[fossa]: https://app.fossa.com/projects/git%2Bgithub.com%2Fmoka-rs%2Fmoka?ref=badge_shield

[caffeine-git]: https://github.com/ben-manes/caffeine
[ristretto-git]: https://github.com/dgraph-io/ristretto
Expand Down Expand Up @@ -59,14 +61,14 @@ Add this to your `Cargo.toml`:

```toml
[dependencies]
moka = "0.3"
moka = "0.4"
```

To use the asynchronous cache, enable a crate feature called "future".

```toml
[dependencies]
moka = { version = "0.3", features = ["future"] }
moka = { version = "0.4", features = ["future"] }
```


Expand Down Expand Up @@ -162,7 +164,7 @@ Here is a similar program to the previous example, but using asynchronous cache
// Cargo.toml
//
// [dependencies]
// moka = { version = "0.3", features = ["future"] }
// moka = { version = "0.4", features = ["future"] }
// tokio = { version = "1", features = ["rt-multi-thread", "macros" ] }
// futures = "0.3"
Expand Down Expand Up @@ -333,6 +335,18 @@ change.
-->


## Developing Moka

**Running All Tests**

To run all tests including `future` feature and doc tests on the README, use the
following command:

```console
$ RUSTFLAGS='--cfg skeptic' cargo test --all-features
```


## Road Map

- [x] `async` optimized caches. (`v0.2.0`)
Expand Down Expand Up @@ -360,13 +374,11 @@ at your option.

See [LICENSE-MIT](LICENSE-MIT) and [LICENSE-APACHE](LICENSE-APACHE) for details.

[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fmoka-rs%2Fmoka.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fmoka-rs%2Fmoka?ref=badge_large)

<!--
MEMO:
- Column width is 85. (Emacs: C-x f)
-->


[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fmoka-rs%2Fmoka.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2Fmoka-rs%2Fmoka?ref=badge_large)
1 change: 1 addition & 0 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use quanta::Instant;

pub(crate) mod deque;
pub(crate) mod error;
pub(crate) mod frequency_sketch;
pub(crate) mod thread_pool;
pub(crate) mod unsafe_weak_pointer;
Expand Down
152 changes: 147 additions & 5 deletions src/common/deque.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,18 @@ impl<T> DeqNode<T> {
}
}

/// Cursor is used to remember the current iterating position.
enum DeqCursor<T> {
Node(NonNull<DeqNode<T>>),
Done,
}

pub(crate) struct Deque<T> {
region: CacheRegion,
len: usize,
head: Option<NonNull<DeqNode<T>>>,
tail: Option<NonNull<DeqNode<T>>>,
cursor: Option<DeqCursor<T>>,
marker: PhantomData<Box<DeqNode<T>>>,
}

Expand Down Expand Up @@ -84,9 +91,10 @@ impl<T> Deque<T> {
pub(crate) fn new(region: CacheRegion) -> Self {
Self {
region,
len: 0,
head: None,
tail: None,
len: 0,
cursor: None,
marker: PhantomData::default(),
}
}
Expand All @@ -95,6 +103,10 @@ impl<T> Deque<T> {
&self.region
}

pub(crate) fn len(&self) -> usize {
self.len
}

pub(crate) fn contains(&self, node: &DeqNode<T>) -> bool {
self.region == node.region && (node.prev.is_some() || self.is_head(node))
}
Expand All @@ -110,6 +122,10 @@ impl<T> Deque<T> {
// This method takes care not to create mutable references to whole nodes,
// to maintain validity of aliasing pointers into `element`.
self.head.map(|node| unsafe {
if self.is_at_cursor(node.as_ref()) {
self.advance_cursor();
}

let mut node = Box::from_raw(node.as_ptr());
self.head = node.next;

Expand Down Expand Up @@ -155,12 +171,16 @@ impl<T> Deque<T> {
}
}

/// Panics:
pub(crate) unsafe fn move_to_back(&mut self, mut node: NonNull<DeqNode<T>>) {
if self.is_tail(node.as_ref()) {
// Already at the tail. Nothing to do.
return;
}

if self.is_at_cursor(node.as_ref()) {
self.advance_cursor();
}

let node = node.as_mut(); // this one is ours now, we can create an &mut.

// Not creating new mutable (unique!) references overlapping `element`.
Expand Down Expand Up @@ -197,6 +217,10 @@ impl<T> Deque<T> {
pub(crate) unsafe fn unlink(&mut self, mut node: NonNull<DeqNode<T>>) {
assert_eq!(self.region, node.as_ref().region);

if self.is_at_cursor(node.as_ref()) {
self.advance_cursor();
}

let node = node.as_mut(); // this one is ours now, we can create an &mut.

// Not creating new mutable (unique!) references overlapping `element`.
Expand All @@ -217,6 +241,30 @@ impl<T> Deque<T> {

self.len -= 1;
}

#[allow(unused)]
pub(crate) fn reset_cursor(&mut self) {
self.cursor = None;
}
}

impl<'a, T> Iterator for &'a mut Deque<T> {
type Item = &'a T;

fn next(&mut self) -> Option<Self::Item> {
if self.cursor.is_none() {
if let Some(head) = self.head {
self.cursor = Some(DeqCursor::Node(head));
}
}
let elem = if let Some(DeqCursor::Node(node)) = self.cursor {
unsafe { Some(&(*node.as_ptr()).element) }
} else {
None
};
self.advance_cursor();
elem
}
}

// Private function/methods
Expand All @@ -237,9 +285,28 @@ impl<T> Deque<T> {
}
}

#[cfg(test)]
fn len(&self) -> usize {
self.len
fn is_at_cursor(&self, node: &DeqNode<T>) -> bool {
if let Some(DeqCursor::Node(cur_node)) = self.cursor {
std::ptr::eq(unsafe { cur_node.as_ref() }, node)
} else {
false
}
}

fn advance_cursor(&mut self) {
match self.cursor.take() {
None => (),
Some(DeqCursor::Node(node)) => unsafe {
if let Some(next) = (*node.as_ptr()).next {
self.cursor = Some(DeqCursor::Node(next));
} else {
self.cursor = Some(DeqCursor::Done);
}
},
Some(DeqCursor::Done) => {
self.cursor = None;
}
}
}
}

Expand Down Expand Up @@ -508,6 +575,81 @@ mod tests {
assert!(tail_e.is_none());
}

#[test]
fn iter() {
let mut deque: Deque<String> = Deque::new(MainProbation);
assert!((&mut deque).next().is_none());

let node1 = DeqNode::new(MainProbation, "a".into());
deque.push_back(Box::new(node1));
let node2 = DeqNode::new(MainProbation, "b".into());
let node2_ptr = deque.push_back(Box::new(node2));
let node3 = DeqNode::new(MainProbation, "c".into());
let node3_ptr = deque.push_back(Box::new(node3));

// -------------------------------------------------------
// First iteration.
assert_eq!((&mut deque).next(), Some(&"a".into()));
assert_eq!((&mut deque).next(), Some(&"b".into()));
assert_eq!((&mut deque).next(), Some(&"c".into()));
assert!((&mut deque).next().is_none());

// -------------------------------------------------------
// Ensure the iterator restarts.
assert_eq!((&mut deque).next(), Some(&"a".into()));
assert_eq!((&mut deque).next(), Some(&"b".into()));
assert_eq!((&mut deque).next(), Some(&"c".into()));
assert!((&mut deque).next().is_none());

// -------------------------------------------------------
// Ensure reset_cursor works.
assert_eq!((&mut deque).next(), Some(&"a".into()));
assert_eq!((&mut deque).next(), Some(&"b".into()));
deque.reset_cursor();
assert_eq!((&mut deque).next(), Some(&"a".into()));
assert_eq!((&mut deque).next(), Some(&"b".into()));
assert_eq!((&mut deque).next(), Some(&"c".into()));
assert!((&mut deque).next().is_none());

// -------------------------------------------------------
// Try to move_to_back during iteration.
assert_eq!((&mut deque).next(), Some(&"a".into()));
// Next will be "b", but we move it to the back.
unsafe { deque.move_to_back(node2_ptr) };
// Now, next should be "c", and then "b".
assert_eq!((&mut deque).next(), Some(&"c".into()));
assert_eq!((&mut deque).next(), Some(&"b".into()));
assert!((&mut deque).next().is_none());

// -------------------------------------------------------
// Try to unlink during iteration.
assert_eq!((&mut deque).next(), Some(&"a".into()));
// Next will be "c", but we unlink it.
unsafe { deque.unlink(node3_ptr) };
// Now, next should be "b".
assert_eq!((&mut deque).next(), Some(&"b".into()));
assert!((&mut deque).next().is_none());

// -------------------------------------------------------
// Try pop_front during iteration.
let node3 = DeqNode::new(MainProbation, "c".into());
deque.push_back(Box::new(node3));

assert_eq!((&mut deque).next(), Some(&"a".into()));
// Next will be "b", but we call pop_front twice to remove "a" and "b".
deque.pop_front(); // "a"
deque.pop_front(); // "b"
// Now, next should be "c".
assert_eq!((&mut deque).next(), Some(&"c".into()));
assert!((&mut deque).next().is_none());

// -------------------------------------------------------
// Check iterating on an empty deque.
deque.pop_front(); // "c"
assert!((&mut deque).next().is_none());
assert!((&mut deque).next().is_none());
}

#[test]
fn drop() {
use std::{cell::RefCell, rc::Rc};
Expand Down
Loading

0 comments on commit 56ba3f0

Please sign in to comment.