use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Debug)]
pub struct PromotionRequest(Arc<AtomicU64>);
impl Default for PromotionRequest {
fn default() -> Self {
Self::new()
}
}
impl PromotionRequest {
pub const MANUAL: u64 = 1;
pub fn new() -> Self {
Self(Arc::new(AtomicU64::new(0)))
}
pub fn request(&self, min_epoch: u64) -> bool {
debug_assert!(min_epoch >= 1, "a promotion request must be non-zero");
self.0
.compare_exchange(0, min_epoch, Ordering::AcqRel, Ordering::Acquire)
.is_ok()
}
#[inline]
pub fn is_requested(&self) -> bool {
self.0.load(Ordering::Acquire) != 0
}
pub fn pending(&self) -> Option<u64> {
match self.0.load(Ordering::Acquire) {
0 => None,
epoch => Some(epoch),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn starts_unrequested() {
let r = PromotionRequest::new();
assert!(!r.is_requested());
assert_eq!(r.pending(), None);
}
#[test]
fn first_request_wins_and_later_ones_are_ignored() {
let r = PromotionRequest::new();
assert!(r.request(7));
assert_eq!(r.pending(), Some(7));
assert!(!r.request(PromotionRequest::MANUAL));
assert_eq!(r.pending(), Some(7));
}
#[test]
fn manual_request_carries_the_manual_sentinel() {
let r = PromotionRequest::new();
assert!(r.request(PromotionRequest::MANUAL));
assert_eq!(r.pending(), Some(1));
assert!(r.is_requested());
}
#[test]
fn clones_share_the_request() {
let r = PromotionRequest::new();
let requester = r.clone();
assert!(requester.request(42));
assert_eq!(r.pending(), Some(42));
}
}