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

Add the invalidate_entries_if method #12

Merged
merged 35 commits into from
Jun 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
7609326
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 Mar 4, 2021
3e3300c
Merge branch 'master' into invalidate-entries-if
tatsuya6502 Mar 5, 2021
2a64385
Merge branch 'master' into invalidate-entries-if
tatsuya6502 Apr 24, 2021
8ad0ecb
Merge branch 'master' into invalidate-entries-if
tatsuya6502 Apr 24, 2021
42eb1ca
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 Apr 26, 2021
7c01333
Bump the version to v0.4.0
tatsuya6502 Apr 26, 2021
688f72c
Apply cargo fmt
tatsuya6502 Apr 26, 2021
fe66656
Fix a Clippy warning
tatsuya6502 Apr 26, 2021
1c4a7ab
Bump the version to v0.4.0 (README and doc comments)
tatsuya6502 Apr 26, 2021
4e7b0a9
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 Apr 29, 2021
766334d
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 Apr 29, 2021
a127cf2
rustfmt
tatsuya6502 Apr 29, 2021
c0c9bf4
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 May 4, 2021
ab107f3
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 May 4, 2021
10c36a3
Implement the `invalidate_entries_if` method
tatsuya6502 May 4, 2021
f6cbff2
Implement the `invalidate_entries_if` method
tatsuya6502 May 4, 2021
b5aeb6e
Implement the `invalidate_entries_if` method
tatsuya6502 May 5, 2021
49b914f
Implement the `invalidate_entries_if` method (WIP)
tatsuya6502 May 5, 2021
2613a05
Implement the `invalidate_entries_if` method
tatsuya6502 May 5, 2021
1c82344
Implement the `invalidate_entries_if` method
tatsuya6502 May 5, 2021
869edb3
Implement the `invalidate_entries_if` method
tatsuya6502 May 5, 2021
55442ec
Implement the `invalidate_entries_if` method
tatsuya6502 May 5, 2021
96a3082
Merge branch 'master' into invalidate-entries-if
tatsuya6502 Jun 12, 2021
d3a640c
Fix a Clippy warning (clippy 0.1.53 (92752e94223 2021-06-09)
tatsuya6502 Jun 12, 2021
1449c66
Implement the `invalidate_entries_if` method
tatsuya6502 Jun 13, 2021
238eacb
Merge branch 'master' into invalidate-entries-if
tatsuya6502 Jun 13, 2021
adf1cb1
Implement the `invalidate_entries_if` method
tatsuya6502 Jun 13, 2021
387d306
Implement the `invalidate_entries_if` method
tatsuya6502 Jun 13, 2021
705fe3f
Merge branch 'master' into invalidate-entries-if
tatsuya6502 Jun 13, 2021
ba22b22
Implement the `invalidate_entries_if` method
tatsuya6502 Jun 16, 2021
d63b6ce
Implement the `invalidate_entries_if` method
tatsuya6502 Jun 19, 2021
3bb30f6
cargo fmt
tatsuya6502 Jun 19, 2021
5ae896e
Upgrade quanta to 0.9
tatsuya6502 Jun 19, 2021
e41fafc
Implement the `invalidate_entries_if` method
tatsuya6502 Jun 20, 2021
7a192c1
Fix a typo in the README
tatsuya6502 Jun 20, 2021
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
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