use std::{collections::HashMap, sync::Arc, time};
use super::{Entry, EntryList};
use foundation_nostd::comp::basic::RwLock;
static DEFAULT_READINESS_WAIT: std::time::Duration = std::time::Duration::from_millis(10);
pub trait Waker {
fn wake(&self);
}
#[derive(Debug)]
pub struct DurationWaker<T> {
pub handle: T,
pub from: time::Instant,
pub how_long: time::Duration,
}
#[derive(Debug)]
pub struct DurationStore<T> {
store: Arc<RwLock<EntryList<DurationWaker<T>>>>,
}
impl<T> Clone for DurationStore<T> {
fn clone(&self) -> Self {
Self {
store: self.store.clone(),
}
}
}
impl<T> Default for DurationStore<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> DurationStore<T> {
#[must_use]
pub fn new() -> Self {
Self {
store: Arc::new(RwLock::new(EntryList::new())),
}
}
}
impl<T: std::fmt::Debug> DurationStore<T> {
pub fn insert(&self, wakeable: DurationWaker<T>) -> Entry {
self.store.write().unwrap().insert(wakeable)
}
pub fn update(&self, handle: &Entry, wakeable: DurationWaker<T>) -> Option<DurationWaker<T>> {
self.store.write().unwrap().update(handle, wakeable)
}
#[must_use]
pub fn remove(&self, handle: &Entry) -> Option<DurationWaker<T>> {
self.store.write().unwrap().take(handle)
}
#[must_use]
pub fn has_pending_tasks(&self) -> bool {
self.store.read().unwrap().active_slots() > 0
}
#[must_use]
pub fn count(&self) -> usize {
self.store.read().unwrap().active_slots()
}
#[must_use]
pub fn get_matured(&self) -> Vec<DurationWaker<T>> {
self.store
.write()
.unwrap()
.select_take(|t| t.is_ready(None))
}
#[must_use]
pub fn min_duration(&self) -> Option<time::Duration> {
self.store
.read()
.unwrap()
.map_with(DurationWaker::remaining)
.iter()
.min()
.copied()
}
#[must_use]
pub fn max_duration(&self) -> Option<time::Duration> {
self.store
.read()
.unwrap()
.map_with(DurationWaker::remaining)
.iter()
.max()
.copied()
}
}
pub trait Timeable {
fn remaining_duration(&self) -> Option<time::Duration>;
}
impl<T: Waker> Timeable for DurationWaker<T> {
fn remaining_duration(&self) -> Option<time::Duration> {
self.remaining()
}
}
pub trait Waiter {
fn is_ready(&self, dur: Option<std::time::Duration>) -> bool;
}
impl<T> Waiter for DurationWaker<T> {
fn is_ready(&self, _dur: Option<std::time::Duration>) -> bool {
self.try_is_ready().unwrap_or(false)
}
}
impl<T: Waker> Waker for DurationWaker<T> {
fn wake(&self) {
self.handle.wake();
}
}
impl<T> DurationWaker<T> {
pub fn new(handle: T, from: time::Instant, how_long: time::Duration) -> Self {
Self {
handle,
from,
how_long,
}
}
pub fn from_now(handle: T, how_long: time::Duration) -> Self {
Self::new(handle, time::Instant::now(), how_long)
}
pub fn remaining(&self) -> Option<time::Duration> {
let now = std::time::Instant::now();
match self.from.checked_add(self.how_long) {
Some(when_ready) => when_ready.checked_duration_since(now),
None => None,
}
}
pub fn try_is_ready(&self) -> Option<bool> {
let now = std::time::Instant::now();
self.from
.checked_add(self.how_long)
.map(|when_ready| when_ready.checked_duration_since(now).is_none())
}
pub fn deadline(&self) -> time::Instant {
self.from + self.how_long
}
}
#[derive(Debug)]
pub struct Sleepers<T: Waiter> {
sleepers: Arc<RwLock<HashMap<Entry, T>>>,
max_readiness_wait: std::time::Duration,
}
pub trait Timing {
fn min_duration(&self) -> Option<time::Duration>;
fn max_duration(&self) -> Option<time::Duration>;
}
impl<T: Timeable + Waiter + std::fmt::Debug> Timing for Sleepers<T> {
fn min_duration(&self) -> Option<time::Duration> {
let sleepers = self.sleepers.read().unwrap();
sleepers
.values()
.filter_map(|sleeper| sleeper.remaining_duration())
.min()
}
fn max_duration(&self) -> Option<time::Duration> {
let sleepers = self.sleepers.read().unwrap();
sleepers
.values()
.filter_map(|sleeper| sleeper.remaining_duration())
.max()
}
}
impl<T: Waker + Waiter + std::fmt::Debug> Waker for Sleepers<T> {
fn wake(&self) {
for sleeper in self.get_matured() {
sleeper.wake();
}
}
}
impl<T: Waiter + std::fmt::Debug> Clone for Sleepers<T> {
fn clone(&self) -> Self {
Self {
sleepers: self.sleepers.clone(),
max_readiness_wait: self.max_readiness_wait,
}
}
}
impl<T: Waiter + std::fmt::Debug> Default for Sleepers<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Waiter + std::fmt::Debug> Sleepers<T> {
#[must_use]
pub fn new() -> Self {
Self {
sleepers: Arc::new(RwLock::new(HashMap::new())),
max_readiness_wait: DEFAULT_READINESS_WAIT,
}
}
#[must_use]
pub fn with_max_readiness_wait(dur: std::time::Duration) -> Self {
Self {
sleepers: Arc::new(RwLock::new(HashMap::new())),
max_readiness_wait: dur,
}
}
pub fn set_max_readiness_wait(&mut self, dur: std::time::Duration) {
self.max_readiness_wait = dur;
}
pub fn insert(&self, entry: Entry, wakeable: T) {
self.sleepers.write().unwrap().insert(entry, wakeable);
}
pub fn update(&self, handle: &Entry, wakeable: T) -> Option<T> {
self.sleepers.write().unwrap().insert(*handle, wakeable)
}
#[must_use]
pub fn remove(&self, handle: &Entry) -> Option<T> {
self.sleepers.write().unwrap().remove(handle)
}
#[must_use]
pub fn contains(&self, handle: &Entry) -> bool {
self.sleepers.read().unwrap().contains_key(handle)
}
#[must_use]
pub fn has_pending_tasks(&self) -> bool {
!self.sleepers.read().unwrap().is_empty()
}
#[must_use]
pub fn count(&self) -> usize {
self.sleepers.read().unwrap().len()
}
#[must_use]
pub fn get_matured(&self) -> Vec<T> {
let mut sleepers = self.sleepers.write().unwrap();
let matured_entries: Vec<Entry> = sleepers
.iter()
.filter(|(_, sleeper)| sleeper.is_ready(Some(self.max_readiness_wait)))
.map(|(entry, _)| *entry)
.collect();
let mut matured = Vec::new();
for entry in matured_entries {
if let Some(sleeper) = sleepers.remove(&entry) {
matured.push(sleeper);
}
}
matured
}
pub fn minimum_deadline(&self) -> Option<time::Instant>
where
T: crate::synca::sleepers::Timeable,
{
let sleepers = self.sleepers.read().unwrap();
sleepers
.values()
.filter_map(|sleeper| sleeper.remaining_duration())
.map(|remaining| time::Instant::now() + remaining)
.min()
}
pub fn time_until_next(&self) -> Option<time::Duration>
where
T: crate::synca::sleepers::Timeable,
{
self.minimum_deadline().map(|deadline| {
let now = time::Instant::now();
if deadline > now {
deadline - now
} else {
time::Duration::ZERO
}
})
}
}