use std::sync::atomic::{
AtomicI32,
Ordering::{AcqRel, Acquire, Release},
};
use event_listener::Event;
use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Error)]
#[error("网络发送节流器已关闭")]
pub struct ThrottleClosed;
pub struct NetworkSenderThrottle {
throttle_max: i32,
throttle_count: AtomicI32,
event: Event,
}
impl NetworkSenderThrottle {
pub fn new(throttle_max: usize) -> Self {
Self {
throttle_max: (throttle_max.max(1)) as i32,
throttle_count: AtomicI32::new(0),
event: Event::new(),
}
}
pub async fn enter_send(&self) -> Result<(), ThrottleClosed> {
loop {
let current = self.throttle_count.load(Acquire);
if current < 0 {
return Err(ThrottleClosed);
}
if current < self.throttle_max {
if self
.throttle_count
.compare_exchange_weak(current, current + 1, AcqRel, Acquire)
.is_ok()
{
return Ok(());
}
continue;
}
let listener = self.event.listen();
let recheck = self.throttle_count.load(Acquire);
if recheck < 0 {
return Err(ThrottleClosed);
}
if recheck < self.throttle_max {
continue;
}
listener.await;
}
}
pub fn exit_send(&self) {
let prev = self.throttle_count.fetch_sub(1, AcqRel);
if prev >= self.throttle_max {
self.event.notify(1);
}
}
pub fn close(&self) {
self.throttle_count.store(i32::MIN / 2, Release);
self.event.notify(usize::MAX);
}
#[inline]
pub fn is_closed(&self) -> bool {
self.throttle_count.load(Acquire) < 0
}
#[inline]
pub fn in_flight(&self) -> i32 {
self.throttle_count.load(Acquire).max(0)
}
}
#[cfg(test)]
mod tests {
use std::{
future::Future,
pin::pin,
sync::Arc,
task::{Context, Poll, Waker},
thread,
time::Duration,
};
use super::*;
fn block_on<F: Future>(f: F) -> F::Output {
let waker = Waker::noop();
let mut cx = Context::from_waker(waker);
let mut f = pin!(f);
loop {
if let Poll::Ready(res) = f.as_mut().poll(&mut cx) {
return res;
}
thread::yield_now();
}
}
#[test]
fn test_throttle_basic() {
let throttle = NetworkSenderThrottle::new(2);
assert_eq!(throttle.in_flight(), 0);
assert!(block_on(throttle.enter_send()).is_ok());
assert_eq!(throttle.in_flight(), 1);
assert!(block_on(throttle.enter_send()).is_ok());
assert_eq!(throttle.in_flight(), 2);
throttle.exit_send();
assert_eq!(throttle.in_flight(), 1);
throttle.exit_send();
assert_eq!(throttle.in_flight(), 0);
}
#[test]
fn test_throttle_close() {
let throttle = Arc::new(NetworkSenderThrottle::new(1));
assert!(block_on(throttle.enter_send()).is_ok());
let t = throttle.clone();
thread::spawn(move || {
thread::sleep(Duration::from_millis(20));
t.close();
});
let result = block_on(throttle.enter_send());
assert_eq!(result, Err(ThrottleClosed));
assert!(throttle.is_closed());
}
}