Skip to content

Commit

Permalink
avoid clones by using hashbrown directly and utilizing their EntryRef…
Browse files Browse the repository at this point in the history
…-api
  • Loading branch information
djugei committed Oct 17, 2024
1 parent 536ccfb commit 7841a81
Show file tree
Hide file tree
Showing 3 changed files with 13 additions and 7 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ debug=1
[patch.crates-io]
ddelta = { git = "https://github.com/djugei/ddelta-rs" }
indicatif = { git = "https://github.com/djugei/indicatif", branch = "heuristic" }
hashbrown = { git = "https://github.com/djugei/hashbrown" }
1 change: 1 addition & 0 deletions async_file_cache/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ edition = "2021"
[dependencies]
tokio = { version = "*", features = ["sync", "fs", "io-util", "macros"] }
tracing = "*"
hashbrown = "*"

[dev-dependencies]
tempfile = "*"
Expand Down
18 changes: 11 additions & 7 deletions async_file_cache/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use core::future::Future;
use std::ffi::OsString;
use std::{collections::HashMap, hash::Hash, io::ErrorKind, path::PathBuf, pin::pin};
use std::{hash::Hash, io::ErrorKind, path::PathBuf, pin::pin};
use tracing::{debug, trace};

use hashbrown::HashMap;
use tokio::io::AsyncSeekExt;
use tokio::{fs::File, sync::Mutex, sync::Semaphore};

Expand Down Expand Up @@ -64,23 +65,26 @@ impl<State: Cacheable> FileCache<State> {

loop {
let mut in_flight = self.in_flight.lock().await;
use std::collections::hash_map::Entry;
match (*in_flight).entry(key.clone()) {
Entry::Occupied(mut entry) => {
use hashbrown::hash_map::EntryRef;
match (*in_flight).entry_ref(&key) {
EntryRef::Occupied(mut entry) => {
let mut e = entry.get_mut().clone();
drop(in_flight);
let path = State::key_to_path(&key);
debug!("waiting {:?}", path);
match e.changed().await {
Ok(()) => {
// weird but ok(ok()))
// todo: only open the file once, ever
// this is a slight race-condition where the file could be deleted from the fs in between this and the previous line currently
return Ok(Ok(read.open(path).await?));
}
Err(_) => {
// sender has been dropped which means either the generation function
// paniced internally or the whole future has been dropped in flight.
// we need to remove the entry and file and try again.

// fixme: doesn't this file need to be deleted while locking the hashmap?
if let Err(e) = tokio::fs::remove_file(path).await {
// not found is fine thats what we want
if e.kind() != ErrorKind::NotFound {
Expand All @@ -93,7 +97,7 @@ impl<State: Cacheable> FileCache<State> {
// other ones get either an empty entry or a new entry with a different
// channel
let mut in_flight = self.in_flight.lock().await;
if let Entry::Occupied(entry) = (*in_flight).entry(key.clone()) {
if let EntryRef::Occupied(entry) = (*in_flight).entry_ref(&key) {
if e.same_channel(entry.get()) {
entry.remove();
}
Expand All @@ -103,7 +107,7 @@ impl<State: Cacheable> FileCache<State> {
}
}
}
Entry::Vacant(entry) => {
EntryRef::Vacant(entry) => {
let path = State::key_to_path(&key);
match read.open(&path).await {
Ok(f) => {
Expand All @@ -117,7 +121,7 @@ impl<State: Cacheable> FileCache<State> {
} else {
debug!("generating {:?}", path);
let (tx, rx) = tokio::sync::watch::channel(());
entry.insert(rx);
entry.insert_clone(rx);

let mut part_path = path.clone();
part_path.as_mut_os_string().push(OsString::from(".part"));
Expand Down

0 comments on commit 7841a81

Please sign in to comment.