anode 0.1.0

Concurrency library for Rust.
Documentation
use std::time::Duration;
use std::{thread};
use std::ops::Range;
use crate::inf_iterator::{InfIterator, IntoInfIterator};
use crate::rand::{RandDuration};

#[derive(Debug, Clone, Eq, PartialEq, Copy)]
pub struct NonzeroDuration(Duration);

impl NonzeroDuration {
    #[inline(always)]
    pub fn new(duration: Duration) -> Self {
        assert!(duration > Duration::ZERO);
        Self(duration)
    }
}

impl Default for NonzeroDuration {
    #[inline(always)]
    fn default() -> Self {
        Self(Duration::new(0, 1))
    }
}

impl From<Duration> for NonzeroDuration {
    #[inline(always)]
    fn from(duration: Duration) -> Self {
        Self::new(duration)
    }
}

impl From<NonzeroDuration> for Duration {
    #[inline(always)]
    fn from(duration: NonzeroDuration) -> Self {
        duration.0
    }
}

#[derive(Debug, Clone)]
pub struct ExpBackoff {
    pub spin_iters: u64,
    pub yield_iters: u64,
    pub min_sleep: NonzeroDuration,
    pub max_sleep: NonzeroDuration,
}

impl ExpBackoff {
    pub fn spinny() -> Self {
        Self {
            spin_iters: u64::MAX,
            yield_iters: 0,
            min_sleep: NonzeroDuration::default(),
            max_sleep: NonzeroDuration::default()
        }
    }

    pub fn yieldy() -> Self {
        Self {
            spin_iters: 0,
            yield_iters: u64::MAX,
            min_sleep: NonzeroDuration::default(),
            max_sleep: NonzeroDuration::default()
        }
    }

    pub fn sleepy() -> Self {
        Self {
            spin_iters: 0,
            yield_iters: 0,
            min_sleep: Duration::from_micros(100).into(),
            max_sleep: Duration::from_millis(10).into()
        }
    }
}

impl IntoInfIterator for &ExpBackoff {
    type Item = ExpBackoffAction;
    type IntoInfIter = ExpBackoffIter;

    #[inline(always)]
    fn into_inf_iter(self) -> Self::IntoInfIter {
        Self::IntoInfIter {
            spin_limit: self.spin_iters,
            yield_limit: self.spin_iters.saturating_add(self.yield_iters),
            max_sleep: self.max_sleep.into(),
            iterations: 0,
            current_sleep: self.min_sleep.into(),
        }
    }
}

pub struct ExpBackoffIter {
    spin_limit: u64,
    yield_limit: u64,
    max_sleep: Duration,
    iterations: u64,
    current_sleep: Duration,
}

#[derive(Debug, PartialEq, Eq)]
pub enum ExpBackoffAction {
    Nop,
    Yield,
    Sleep(Duration),
}

impl ExpBackoffAction {
    #[inline(always)]
    pub fn act<'a, R, D>(&self, randomness: D) where R: RandDuration + 'a, D: FnOnce() -> &'a mut R  {
        match self {
            ExpBackoffAction::Nop => (),
            ExpBackoffAction::Yield => thread::yield_now(),
            ExpBackoffAction::Sleep(duration) => {
                let range = Range {
                    start: Duration::ZERO,
                    end: *duration,
                };
                let rng = randomness();
                thread::sleep(rng.gen_range(range));
            }
        }
    }
}

impl InfIterator for ExpBackoffIter {
    type Item = ExpBackoffAction;

    #[inline(always)]
    fn next(&mut self) -> Self::Item {
        self.iterations += 1;
        if self.iterations <= self.spin_limit {
            return ExpBackoffAction::Nop;
        }

        if self.iterations <= self.yield_limit {
            return ExpBackoffAction::Yield;
        }

        let current_sleep = self.current_sleep;
        let new_sleep = self.current_sleep * 2;
        self.current_sleep = if new_sleep <= self.max_sleep { new_sleep } else { self.max_sleep };
        ExpBackoffAction::Sleep(current_sleep)
    }
}

#[cfg(test)]
mod tests;