Vendor dependencies

This commit is contained in:
2026-08-01 16:11:49 +03:00
parent 7f139a0241
commit 6b5e7f0f8b
29706 changed files with 9575646 additions and 0 deletions
+251
View File
@@ -0,0 +1,251 @@
// Copyright 2024 tison <wander4096@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Singleflight provides a duplicate function call suppression mechanism.
use std::borrow::Borrow;
use std::collections::HashMap;
use std::hash::BuildHasher;
use std::hash::Hash;
use std::hash::RandomState;
use std::sync::Arc;
use crate::internal::Mutex;
use crate::once::OnceCell;
#[cfg(test)]
mod tests;
/// Group represents a class of work and forms a namespace in which
/// units of work can be executed with duplicate suppression.
#[derive(Debug)]
pub struct Group<K, V, S = RandomState> {
map: Mutex<HashMap<K, Arc<OnceCell<V>>, S>>,
}
impl<K, V, S> Default for Group<K, V, S>
where
K: Eq + Hash + Clone,
V: Clone,
S: BuildHasher + Clone + Default,
{
fn default() -> Self {
Self::with_hasher(S::default())
}
}
impl<K, V> Group<K, V, RandomState>
where
K: Eq + Hash + Clone,
V: Clone,
{
/// Creates a new Group with the default hasher.
pub fn new() -> Self {
Self {
map: Mutex::new(HashMap::new()),
}
}
}
impl<K, V, S> Group<K, V, S>
where
K: Eq + Hash + Clone,
V: Clone,
S: BuildHasher + Clone,
{
/// Creates a new Group with the given hasher.
pub fn with_hasher(hasher: S) -> Self {
Self {
map: Mutex::new(HashMap::with_hasher(hasher)),
}
}
/// Executes and returns the results of the given function, making sure that only one execution
/// is in-flight for a given key at a time.
///
/// If a duplicate comes in, the duplicate caller waits for the original to complete and
/// receives the same results.
///
/// Once the function completes, the key, if not [`forgotten`], is removed from the group,
/// allowing future calls with the same key to execute the function again.
///
/// [`forgotten`]: Self::forget
///
/// # Examples
///
/// ```
/// use std::sync::Arc;
/// use std::sync::atomic::AtomicUsize;
/// use std::sync::atomic::Ordering;
/// use std::time::Duration;
///
/// use mea::singleflight::Group;
///
/// # #[tokio::main]
/// # async fn main() {
/// let group = Group::new();
/// let counter = Arc::new(AtomicUsize::new(0));
///
/// let c1 = counter.clone();
/// let fut1 = group.work("key", || async move {
/// c1.fetch_add(1, Ordering::SeqCst);
/// // simulate heavy work to avoid immediate completion
/// tokio::time::sleep(Duration::from_millis(100)).await;
/// "result"
/// });
///
/// let c2 = counter.clone();
/// let fut2 = group.work("key", || async move {
/// c2.fetch_add(1, Ordering::SeqCst);
/// // simulate heavy work to avoid immediate completion
/// tokio::time::sleep(Duration::from_millis(100)).await;
/// "result"
/// });
///
/// let (r1, r2) = tokio::join!(fut1, fut2);
///
/// assert_eq!(r1, "result");
/// assert_eq!(r2, "result");
/// assert_eq!(counter.load(Ordering::SeqCst), 1);
/// # }
/// ```
pub async fn work<F>(&self, key: K, func: F) -> V
where
F: AsyncFnOnce() -> V,
{
// 1. Get or create the OnceCell.
let cell = {
let mut map = self.map.lock();
map.entry(key.clone())
.or_insert_with(|| Arc::new(OnceCell::new()))
.clone()
};
// 2. Try to initialize the cell.
// OnceCell::get_or_init guarantees that only one task executes the closure.
let res = cell
.get_or_init(async || {
let result = func().await;
// Cleanup: remove the key from the map.
// We must ensure we remove the entry corresponding to *this* cell.
let mut map = self.map.lock();
if let Some(existing) = map.get(&key) {
// Check if the map still points to our cell.
if Arc::ptr_eq(&cell, existing) {
map.remove(&key);
}
}
result
})
.await;
res.clone()
}
/// Executes and returns the results of the given function, making sure that only one execution
/// is in-flight for a given key at a time.
///
/// If a duplicate comes in, the duplicate caller waits for the original to complete and
/// receives the same results.
///
/// If the computation fails, the error is returned for the caller. Other tasks waiting for the
/// result will retry the computation.
///
/// Once the function completes successfully, the key, if not [`forgotten`], is removed from
/// the group, allowing future calls with the same key to execute the function again.
///
/// [`forgotten`]: Self::forget
///
/// # Examples
///
/// ```
/// use std::sync::Arc;
/// use std::sync::atomic::AtomicUsize;
/// use std::sync::atomic::Ordering;
/// use std::time::Duration;
///
/// use mea::singleflight::Group;
///
/// # #[tokio::main]
/// # async fn main() {
/// let group = Group::new();
///
/// let fut1 = group.try_work("key", || async move {
/// // simulate heavy work to avoid immediate completion
/// tokio::time::sleep(Duration::from_millis(100)).await;
/// Err::<_, &'static str>("fut1")
/// });
///
/// let fut2 = group.try_work("key", || async move {
/// // simulate heavy work to avoid immediate completion
/// tokio::time::sleep(Duration::from_millis(200)).await;
/// Ok::<_, &'static str>("fut2")
/// });
///
/// let (r1, r2) = tokio::join!(fut1, fut2);
///
/// assert_eq!(r1, Err("fut1"));
/// assert_eq!(r2, Ok("fut2"));
/// # }
/// ```
pub async fn try_work<E, F>(&self, key: K, func: F) -> Result<V, E>
where
F: AsyncFnOnce() -> Result<V, E>,
{
// 1. Get or create the OnceCell.
let cell = {
let mut map = self.map.lock();
map.entry(key.clone())
.or_insert_with(|| Arc::new(OnceCell::new()))
.clone()
};
// 2. Try to initialize the cell.
// OnceCell::get_or_try_init guarantees that only one task executes the closure.
let res = cell
.get_or_try_init(async || {
let result = func().await?;
// Cleanup: remove the key from the map.
// We must ensure we remove the entry corresponding to *this* cell.
let mut map = self.map.lock();
if let Some(existing) = map.get(&key) {
// Check if the map still points to our cell.
if Arc::ptr_eq(&cell, existing) {
map.remove(&key);
}
}
Ok(result)
})
.await?;
Ok(res.clone())
}
/// Forgets about the given key.
///
/// Future calls to `work` for this key will call the function rather than waiting for an
/// earlier call to complete. Existing calls to `work` for this key are not affected.
pub fn forget<Q>(&self, key: &Q)
where
K: Borrow<Q>,
Q: Hash + Eq + ?Sized,
{
let mut map = self.map.lock();
map.remove(key);
}
}
+234
View File
@@ -0,0 +1,234 @@
// Copyright 2024 tison <wander4096@gmail.com>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::sync::Arc;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::time::Duration;
use crate::singleflight::Group;
#[tokio::test]
async fn test_simple() {
let group = Group::new();
let res = group.work("key", || async { "val" }).await;
assert_eq!(res, "val");
}
#[tokio::test]
async fn test_coalescing() {
let group = Arc::new(Group::new());
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..10 {
let group = group.clone();
let counter = counter.clone();
handles.push(tokio::spawn(async move {
group
.work("key", || async move {
tokio::time::sleep(Duration::from_millis(100)).await;
counter.fetch_add(1, Ordering::SeqCst);
"val"
})
.await
}));
}
for handle in handles {
assert_eq!(handle.await.unwrap(), "val");
}
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[tokio::test]
async fn test_multiple_keys() {
let group = Arc::new(Group::new());
let counter = Arc::new(AtomicUsize::new(0));
let g1 = group.clone();
let c1 = counter.clone();
let h1 = tokio::spawn(async move {
g1.work("key1", || async move {
tokio::time::sleep(Duration::from_millis(50)).await;
c1.fetch_add(1, Ordering::SeqCst);
"val1"
})
.await
});
let g2 = group.clone();
let c2 = counter.clone();
let h2 = tokio::spawn(async move {
g2.work("key2", || async move {
tokio::time::sleep(Duration::from_millis(50)).await;
c2.fetch_add(1, Ordering::SeqCst);
"val2"
})
.await
});
assert_eq!(h1.await.unwrap(), "val1");
assert_eq!(h2.await.unwrap(), "val2");
assert_eq!(counter.load(Ordering::SeqCst), 2);
}
#[tokio::test]
async fn test_forget() {
let group = Arc::new(Group::new());
let counter = Arc::new(AtomicUsize::new(0));
let g1 = group.clone();
let c1 = counter.clone();
let h1 = tokio::spawn(async move {
g1.work("key", || async move {
tokio::time::sleep(Duration::from_millis(100)).await;
c1.fetch_add(1, Ordering::SeqCst);
"val1"
})
.await
});
// Wait a bit to ensure the first call is established
tokio::time::sleep(Duration::from_millis(10)).await;
group.forget(&"key");
let g2 = group.clone();
let c2 = counter.clone();
let h2 = tokio::spawn(async move {
g2.work("key", || async move {
tokio::time::sleep(Duration::from_millis(100)).await;
c2.fetch_add(1, Ordering::SeqCst);
"val2"
})
.await
});
assert_eq!(h1.await.unwrap(), "val1");
assert_eq!(h2.await.unwrap(), "val2");
assert_eq!(counter.load(Ordering::SeqCst), 2);
}
#[tokio::test]
async fn test_panic_safe() {
let group = Arc::new(Group::<&str, String>::new());
// Task that panics
let g1 = group.clone();
let h1 = tokio::spawn(async move {
g1.work("key", || async {
panic!("oops");
})
.await
});
// Wait for h1 to panic and exit
let err = h1.await.unwrap_err();
assert!(err.is_panic());
// Next task should succeed (new attempt)
let res = group.work("key", || async { "success".to_string() }).await;
assert_eq!(res, "success");
}
#[tokio::test]
async fn test_try_work_simple() {
let group = Group::new();
let res = group
.try_work("key", || async { Ok::<&str, ()>("val") })
.await;
assert_eq!(res, Ok("val"));
// Should be removed from map, so next call executes again
let res2 = group
.try_work("key", || async { Ok::<&str, ()>("val2") })
.await;
assert_eq!(res2, Ok("val2"));
}
#[tokio::test]
async fn test_try_work_coalescing() {
let group = Arc::new(Group::new());
let counter = Arc::new(AtomicUsize::new(0));
let mut handles = Vec::new();
for _ in 0..10 {
let group = group.clone();
let counter = counter.clone();
handles.push(tokio::spawn(async move {
group
.try_work("key", || async move {
tokio::time::sleep(Duration::from_millis(100)).await;
counter.fetch_add(1, Ordering::SeqCst);
Ok::<&str, ()>("val")
})
.await
}));
}
for handle in handles {
assert_eq!(handle.await.unwrap(), Ok("val"));
}
assert_eq!(counter.load(Ordering::SeqCst), 1);
}
#[tokio::test]
async fn test_try_work_failure() {
let group = Group::new();
let res = group
.try_work("key", || async { Err::<&str, &str>("error") })
.await;
assert_eq!(res, Err("error"));
// Retry should work
let res2 = group
.try_work("key", || async { Ok::<&str, ()>("success") })
.await;
assert_eq!(res2, Ok("success"));
}
#[tokio::test]
async fn test_try_work_wait_and_retry() {
let group = Arc::new(Group::new());
let counter = Arc::new(AtomicUsize::new(0));
let g1 = group.clone();
let c1 = counter.clone();
let h1 = tokio::spawn(async move {
g1.try_work("key", || async move {
c1.fetch_add(1, Ordering::SeqCst);
tokio::time::sleep(Duration::from_millis(100)).await;
Err::<&str, &str>("fail")
})
.await
});
let g2 = group.clone();
let c2 = counter.clone();
let h2 = tokio::spawn(async move {
// Ensure h1 starts first
tokio::time::sleep(Duration::from_millis(10)).await;
g2.try_work("key", || async move {
c2.fetch_add(1, Ordering::SeqCst);
Ok::<&str, ()>("success")
})
.await
});
assert_eq!(h1.await.unwrap(), Err("fail"));
assert_eq!(h2.await.unwrap(), Ok("success"));
assert_eq!(counter.load(Ordering::SeqCst), 2);
}