use crate::limits;
use std::{cell, mem};
pub(crate) struct Meter {
primary: cell::Cell<u64>,
mode: cell::Cell<Mode>,
permission: cell::Cell<Permission>,
}
#[derive(Clone, Copy)]
pub(super) enum Permission {
Denied,
Granted,
}
#[derive(Clone, Copy, Eq, PartialEq)]
#[repr(u8)]
enum Mode {
Idle = 0,
Primary = 2,
Cleanup = 3,
}
pub(super) enum Tick {
Continue,
Stop,
}
pub(crate) enum Quota {
Available,
Exhausted,
}
impl Meter {
pub(super) const fn new() -> Self {
Self {
primary: cell::Cell::new(0),
mode: cell::Cell::new(Mode::Idle),
permission: cell::Cell::new(Permission::Denied),
}
}
pub(super) fn reset(&self) {
self.mode.set(Mode::Idle);
self.permission.set(Permission::Denied);
}
pub(super) fn begin_call(&self, primary: limits::Progress) -> Result<(), crate::Error> {
self.begin(Mode::Primary, primary)
}
pub(super) fn cleanup(&self, quota: limits::Progress) -> Result<(), crate::Error> {
match self.mode.get() {
Mode::Cleanup => Ok(()),
Mode::Idle => self.begin(Mode::Cleanup, quota),
Mode::Primary => Err(crate::Error::ReentrantQuery),
}
}
pub(super) fn tick(&self) -> Tick {
match self.mode.get() {
Mode::Idle => return Tick::Continue,
Mode::Primary | Mode::Cleanup => {}
}
let left = self.primary.get();
if left == 0 {
return Tick::Stop;
}
let left = left - 1;
self.primary.set(left);
match left {
0 => Tick::Stop,
_ => Tick::Continue,
}
}
pub(super) fn call_quota(&self) -> Quota {
match self.primary.get() {
0 => Quota::Exhausted,
_ => Quota::Available,
}
}
pub(super) fn finish(&self) {
self.mode.set(Mode::Idle);
}
pub(crate) fn run<T>(
&self,
op: impl FnOnce() -> Result<T, crate::Error>,
) -> Result<T, crate::Error> {
self.permission.set(Permission::Granted);
let result = op();
self.permission.set(Permission::Denied);
result
}
pub(super) fn permission(&self) -> Permission {
self.permission.get()
}
fn begin(&self, mode: Mode, primary: limits::Progress) -> Result<(), crate::Error> {
if self.mode.get() != Mode::Idle {
return Err(crate::Error::ReentrantQuery);
}
self.primary.set(primary.get());
self.mode.set(mode);
Ok(())
}
}
const _: () = assert!(mem::size_of::<Meter>() <= 3 * mem::size_of::<u64>());