use crate::LoadBalancer;
use std::sync::atomic::Ordering::{Acquire, Release};
use std::{
future::Future,
sync::{Arc, atomic::AtomicU64},
time::{Duration, Instant},
};
use tokio::{
spawn,
sync::{Mutex, RwLock},
task::{JoinHandle, yield_now},
time::sleep,
};
pub struct Entry<T>
where
T: Send + Sync + Clone + 'static,
{
pub max_count: u64,
pub count: AtomicU64,
pub value: T,
}
impl<T> Clone for Entry<T>
where
T: Send + Sync + Clone + 'static,
{
fn clone(&self) -> Self {
Self {
max_count: self.max_count.clone(),
count: self.count.load(Acquire).into(),
value: self.value.clone(),
}
}
}
pub struct Inner<T>
where
T: Send + Sync + Clone + 'static,
{
pub entries: RwLock<Vec<Entry<T>>>,
pub timer: Mutex<Option<JoinHandle<()>>>,
pub interval: RwLock<Duration>,
pub next_reset: RwLock<Instant>,
}
#[derive(Clone)]
pub struct Window<T>
where
T: Send + Sync + Clone + 'static,
{
inner: Arc<Inner<T>>,
}
impl<T> Window<T>
where
T: Send + Sync + Clone + 'static,
{
pub fn new(entries: Vec<(u64, T)>) -> Self {
Self::new_interval(entries, Duration::from_secs(1))
}
pub fn new_interval(entries: Vec<(u64, T)>, interval: Duration) -> Self {
Self {
inner: Arc::new(Inner {
entries: entries
.into_iter()
.map(|(max_count, value)| Entry {
max_count,
value,
count: 0.into(),
})
.collect::<Vec<_>>()
.into(),
timer: Mutex::new(None),
interval: interval.into(),
next_reset: RwLock::new(Instant::now() + interval),
}),
}
}
pub async fn update<F, R, N>(&self, handler: F) -> anyhow::Result<N>
where
F: Fn(Arc<Inner<T>>) -> R,
R: Future<Output = anyhow::Result<N>>,
{
handler(self.inner.clone()).await
}
pub async fn update_timer<F, R>(
&self,
handler: F,
interval: Duration,
) -> anyhow::Result<JoinHandle<()>>
where
F: Fn(Arc<Inner<T>>) -> R + Send + Sync + 'static,
R: Future<Output = anyhow::Result<()>> + Send,
{
handler(self.inner.clone()).await?;
let inner = self.inner.clone();
Ok(spawn(async move {
loop {
sleep(interval).await;
let _ = handler(inner.clone()).await;
}
}))
}
pub async fn alloc_skip(&self, index: usize) -> (usize, T) {
loop {
if let Some(v) = self.try_alloc_skip(index) {
return v;
}
let now = Instant::now();
let next = *self.inner.next_reset.read().await;
let remaining = if now < next {
next - now
} else {
Duration::ZERO
};
if remaining > Duration::ZERO {
sleep(remaining).await;
} else {
yield_now().await;
}
}
}
pub fn try_alloc_skip(&self, index: usize) -> Option<(usize, T)> {
if let Ok(mut timer_guard) = self.inner.timer.try_lock() {
if timer_guard.is_none() {
let this = self.inner.clone();
*timer_guard = Some(spawn(async move {
let mut interval = *this.interval.read().await;
*this.next_reset.write().await = Instant::now() + interval;
loop {
sleep(match this.interval.try_read() {
Ok(v) => {
interval = *v;
interval
}
Err(_) => interval,
})
.await;
let now = Instant::now();
let entries = this.entries.read().await;
for entry in entries.iter() {
entry.count.store(0, Release);
}
*this.next_reset.write().await = now + interval;
}
}));
}
}
if let Ok(entries) = self.inner.entries.try_read() {
for (i, n) in entries.iter().enumerate() {
if i == index {
continue;
}
let count = n.count.load(Acquire);
if n.max_count == 0
|| count < n.max_count
&& n.count
.compare_exchange(count, count + 1, Release, Acquire)
.is_ok()
{
return Some((i, n.value.clone()));
}
}
}
None
}
}
impl<T> LoadBalancer<T> for Window<T>
where
T: Send + Sync + Clone + 'static,
{
fn alloc(&self) -> impl Future<Output = T> + Send {
async move { self.alloc_skip(usize::MAX).await.1 }
}
fn try_alloc(&self) -> Option<T> {
self.try_alloc_skip(usize::MAX).map(|v| v.1)
}
}