mirror of
https://github.com/EasyTier/EasyTier.git
synced 2026-08-07 13:09:46 +00:00
refactor: introduce HedgeExt for task hedging; rewrite NatDstQuicConnector (#2229)
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
use delegate::delegate;
|
||||
use derivative::Derivative;
|
||||
use derive_more::{AsMut, AsRef, Deref, DerefMut, From, Into, IntoIterator};
|
||||
use std::fmt;
|
||||
use std::fmt::Display;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Derivative, Debug, From, Into, Deref, DerefMut, AsRef, AsMut, IntoIterator, Error)]
|
||||
#[derivative(Default(bound = ""))]
|
||||
#[as_ref(forward)]
|
||||
#[as_mut(forward)]
|
||||
#[into_iterator(owned, ref, ref_mut)]
|
||||
pub struct ErrorCollection<E> {
|
||||
pub errors: Vec<E>,
|
||||
}
|
||||
|
||||
impl<E> ErrorCollection<E> {
|
||||
delegate! {
|
||||
to Vec {
|
||||
#[into]
|
||||
pub fn new() -> Self;
|
||||
#[into]
|
||||
pub fn with_capacity(capacity: usize) -> Self;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, Item: Into<E>> FromIterator<Item> for ErrorCollection<E> {
|
||||
fn from_iter<I: IntoIterator<Item = Item>>(iter: I) -> Self {
|
||||
Self {
|
||||
errors: iter.into_iter().map(Into::into).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Extend<E> for ErrorCollection<E> {
|
||||
delegate! {
|
||||
to self.errors {
|
||||
fn extend<T: IntoIterator<Item = E>>(&mut self, iter: T);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Display> Display for ErrorCollection<E> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
if self.errors.is_empty() {
|
||||
return write!(f, "No errors");
|
||||
}
|
||||
|
||||
write!(f, "{} error(s) occurred:", self.errors.len())?;
|
||||
for (i, err) in self.errors.iter().enumerate() {
|
||||
writeln!(f)?;
|
||||
write!(f, " {}. {}", i + 1, err)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod error;
|
||||
pub mod panic;
|
||||
pub mod string;
|
||||
pub mod task;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
use crate::utils::error::ErrorCollection;
|
||||
use futures::StreamExt;
|
||||
use futures::stream::FuturesUnordered;
|
||||
use std::future::Future;
|
||||
use std::io;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll};
|
||||
use std::time::Duration;
|
||||
use tokio::task::JoinHandle;
|
||||
use tokio::time::sleep;
|
||||
use tokio_util::sync::CancellationToken;
|
||||
use tokio_util::task::AbortOnDropHandle;
|
||||
|
||||
@@ -78,3 +82,61 @@ impl<Output> Future for CancellableTask<Output> {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region HedgeExt
|
||||
|
||||
pub(crate) trait HedgeExt: Iterator + Sized {
|
||||
async fn hedge<T, E>(self, delay: Duration) -> Result<T, ErrorCollection<E>>
|
||||
where
|
||||
Self::Item: Future<Output = Result<T, E>>;
|
||||
}
|
||||
|
||||
impl<I> HedgeExt for I
|
||||
where
|
||||
I: Iterator,
|
||||
{
|
||||
async fn hedge<T, E>(mut self, delay: Duration) -> Result<T, ErrorCollection<E>>
|
||||
where
|
||||
Self::Item: Future<Output = Result<T, E>>,
|
||||
{
|
||||
let mut tasks = FuturesUnordered::new();
|
||||
let mut errors = ErrorCollection::new();
|
||||
let mut exhausted = false;
|
||||
|
||||
macro_rules! spawn {
|
||||
() => {
|
||||
if let Some(fut) = self.next() {
|
||||
tasks.push(fut);
|
||||
} else {
|
||||
exhausted = true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
spawn!();
|
||||
|
||||
while !tasks.is_empty() {
|
||||
tokio::select! {
|
||||
res = tasks.next() => {
|
||||
match res {
|
||||
Some(Ok(v)) => return Ok(v),
|
||||
Some(Err(e)) => errors.push(e),
|
||||
None => unreachable!(),
|
||||
}
|
||||
|
||||
if !exhausted {
|
||||
spawn!();
|
||||
}
|
||||
}
|
||||
|
||||
_ = sleep(delay), if !exhausted => {
|
||||
spawn!();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Err(errors)
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
Reference in New Issue
Block a user