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

Reduce the size of the future returned by async get_with and friend methods (v0.10) #220

Merged
merged 5 commits into from
Feb 6, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 1 addition & 1 deletion src/future.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub use {
pub type PredicateId = String;

// Empty struct to be used in InitResult::InitErr to represent the Option None.
struct OptionallyNone;
pub(crate) struct OptionallyNone;

pub struct Iter<'i, K, V>(crate::sync_base::iter::Iter<'i, K, V>);

Expand Down
167 changes: 99 additions & 68 deletions src/future/cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,89 @@ use std::{
time::Duration,
};

//
// Macros to mitigate the compiler issue that inflates the size of the `init` future
// in `get_with` and friends methods:
//
// - https://github.com/moka-rs/moka/issues/212
// - https://swatinem.de/blog/future-size/
//
// These macros are used by `get_with`, etc. of this module. We are not big fans of
// macros as they make the code difficult to understand. However, we believe the
// benefits of reducing the future size outweigh this drawback.
//
macro_rules! insert_with_hash_and_fun_m(
( $self:ident, $key:expr, $hash:expr, $get:expr, $init:expr, $replace_if:expr, $need_key:expr ) => {
{
use futures_util::FutureExt;


let insert = |v| $self.insert_with_hash($key.clone(), $hash, v).boxed();

let k = if $need_key {
Some(Arc::clone(&$key))
} else {
None
};

let type_id = ValueInitializer::<K, V, S>::type_id_for_get_with();
let post_init = ValueInitializer::<K, V, S>::post_init_for_get_with;

match $self
.value_initializer
.try_init_or_read(&Arc::clone(&$key), type_id, $get, $init, insert, post_init)
.await
{
InitResult::Initialized(v) => {
crossbeam_epoch::pin().flush();
Entry::new(k, v, true)
}
InitResult::ReadExisting(v) => Entry::new(k, v, false),
InitResult::InitErr(_) => unreachable!(),
}
}
}
);

macro_rules! get_or_insert_with_hash_and_fun_m(
( $self:ident, $key:expr, $hash:expr, $init:expr, $replace_if:expr, $need_key:expr ) => {
{
let maybe_entry =
$self.base
.get_with_hash_but_ignore_if(&$key, $hash, $replace_if.as_mut(), $need_key);
if let Some(entry) = maybe_entry {
entry
} else {
let get = || {
$self.base
.get_with_hash_but_no_recording(&$key, $hash, $replace_if.as_mut())
};
insert_with_hash_and_fun_m!($self, $key, $hash, get, $init, $replace_if, $need_key)
}
}
}
);

macro_rules! get_or_insert_with_hash_by_ref_and_fun_m(
( $self:ident, $key:expr, $hash:expr, $init:expr, $replace_if:expr, $need_key:expr ) => {
{
let maybe_entry =
$self.base
.get_with_hash_but_ignore_if($key, $hash, $replace_if.as_mut(), $need_key);
if let Some(entry) = maybe_entry {
entry
} else {
let get = || {
$self.base
.get_with_hash_but_no_recording(&$key, $hash, $replace_if.as_mut())
};
let arc_key = Arc::new($key.to_owned());
insert_with_hash_and_fun_m!($self, arc_key, $hash, get, $init, $replace_if, $need_key)
}
}
}
);

/// A thread-safe, futures-aware concurrent in-memory cache.
///
/// `Cache` supports full concurrency of retrievals and a high expected concurrency
Expand Down Expand Up @@ -917,10 +1000,8 @@ where
pub async fn get_with(&self, key: K, init: impl Future<Output = V>) -> V {
let hash = self.base.hash(&key);
let key = Arc::new(key);
let replace_if = None as Option<fn(&V) -> bool>;
self.get_or_insert_with_hash_and_fun(key, hash, init, replace_if, false)
.await
.into_value()
let mut replace_if = None as Option<fn(&V) -> bool>;
get_or_insert_with_hash_and_fun_m!(self, key, hash, init, replace_if, false).into_value()
}

/// Similar to [`get_with`](#method.get_with), but instead of passing an owned
Expand All @@ -932,10 +1013,8 @@ where
Q: ToOwned<Owned = K> + Hash + Eq + ?Sized,
{
let hash = self.base.hash(key);
let replace_if = None as Option<fn(&V) -> bool>;

self.get_or_insert_with_hash_by_ref_and_fun(key, hash, init, replace_if, false)
.await
let mut replace_if = None as Option<fn(&V) -> bool>;
get_or_insert_with_hash_by_ref_and_fun_m!(self, key, hash, init, replace_if, false)
.into_value()
}

Expand All @@ -950,9 +1029,8 @@ where
) -> V {
let hash = self.base.hash(&key);
let key = Arc::new(key);
self.get_or_insert_with_hash_and_fun(key, hash, init, Some(replace_if), false)
.await
.into_value()
let mut replace_if = Some(replace_if);
get_or_insert_with_hash_and_fun_m!(self, key, hash, init, replace_if, false).into_value()
}

/// Returns a _clone_ of the value corresponding to the key. If the value does
Expand Down Expand Up @@ -1432,15 +1510,7 @@ where
mut replace_if: Option<impl FnMut(&V) -> bool>,
need_key: bool,
) -> Entry<K, V> {
let maybe_entry =
self.base
.get_with_hash_but_ignore_if(&key, hash, replace_if.as_mut(), need_key);
if let Some(entry) = maybe_entry {
entry
} else {
self.insert_with_hash_and_fun(key, hash, init, replace_if, need_key)
.await
}
get_or_insert_with_hash_and_fun_m!(self, key, hash, init, replace_if, need_key)
}

pub(crate) async fn get_or_insert_with_hash_by_ref_and_fun<Q>(
Expand All @@ -1455,52 +1525,7 @@ where
K: Borrow<Q>,
Q: ToOwned<Owned = K> + Hash + Eq + ?Sized,
{
let maybe_entry =
self.base
.get_with_hash_but_ignore_if(key, hash, replace_if.as_mut(), need_key);
if let Some(entry) = maybe_entry {
entry
} else {
let key = Arc::new(key.to_owned());
self.insert_with_hash_and_fun(key, hash, init, replace_if, need_key)
.await
}
}

async fn insert_with_hash_and_fun(
&self,
key: Arc<K>,
hash: u64,
init: impl Future<Output = V>,
mut replace_if: Option<impl FnMut(&V) -> bool>,
need_key: bool,
) -> Entry<K, V> {
use futures_util::FutureExt;

let get = || {
self.base
.get_with_hash_but_no_recording(&key, hash, replace_if.as_mut())
};
let insert = |v| self.insert_with_hash(key.clone(), hash, v).boxed();

let k = if need_key {
Some(Arc::clone(&key))
} else {
None
};

match self
.value_initializer
.init_or_read(Arc::clone(&key), get, init, insert)
.await
{
InitResult::Initialized(v) => {
crossbeam_epoch::pin().flush();
Entry::new(k, v, true)
}
InitResult::ReadExisting(v) => Entry::new(k, v, false),
InitResult::InitErr(_) => unreachable!(),
}
get_or_insert_with_hash_by_ref_and_fun_m!(self, key, hash, init, replace_if, need_key)
}

pub(crate) async fn get_or_insert_with_hash(
Expand Down Expand Up @@ -1608,9 +1633,12 @@ where
None
};

let type_id = ValueInitializer::<K, V, S>::type_id_for_optionally_get_with();
let post_init = ValueInitializer::<K, V, S>::post_init_for_optionally_get_with;

match self
.value_initializer
.optionally_init_or_read(Arc::clone(&key), get, init, insert)
.try_init_or_read(&Arc::clone(&key), type_id, get, init, insert, post_init)
.await
{
InitResult::Initialized(v) => {
Expand Down Expand Up @@ -1688,9 +1716,12 @@ where
None
};

let type_id = ValueInitializer::<K, V, S>::type_id_for_try_get_with::<E>();
let post_init = ValueInitializer::<K, V, S>::post_init_for_try_get_with;

match self
.value_initializer
.try_init_or_read(Arc::clone(&key), get, init, insert)
.try_init_or_read(&Arc::clone(&key), type_id, get, init, insert, post_init)
.await
{
InitResult::Initialized(v) => {
Expand Down
14 changes: 8 additions & 6 deletions src/future/entry_selector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,11 +521,9 @@ where
///
/// [get-with-method]: ./struct.Cache.html#method.get_with
pub async fn or_insert_with(self, init: impl Future<Output = V>) -> Entry<K, V> {
let owned_key: K = self.ref_key.to_owned();
let key = Arc::new(owned_key);
let replace_if = None as Option<fn(&V) -> bool>;
self.cache
.get_or_insert_with_hash_and_fun(key, self.hash, init, replace_if, true)
.get_or_insert_with_hash_by_ref_and_fun(self.ref_key, self.hash, init, replace_if, true)
.await
}

Expand All @@ -542,10 +540,14 @@ where
init: impl Future<Output = V>,
replace_if: impl FnMut(&V) -> bool,
) -> Entry<K, V> {
let owned_key: K = self.ref_key.to_owned();
let key = Arc::new(owned_key);
self.cache
.get_or_insert_with_hash_and_fun(key, self.hash, init, Some(replace_if), true)
.get_or_insert_with_hash_by_ref_and_fun(
self.ref_key,
self.hash,
init,
Some(replace_if),
true,
)
.await
}

Expand Down
Loading