use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct Tag(pub u64);
impl Tag {
pub const NONE: Tag = Tag(0);
}
thread_local! {
static UNIQUE_TAG: Cell<u64> = const { Cell::new(1) };
}
pub fn next_tag() -> Tag {
UNIQUE_TAG.with(|c| {
let t = c.get();
c.set(t.wrapping_add(1));
Tag(t)
})
}
static GLOBAL_UNIQUE_TAG: AtomicU64 = AtomicU64::new(1);
pub fn next_tag_global() -> Tag {
Tag(GLOBAL_UNIQUE_TAG.fetch_add(1, Ordering::Relaxed))
}
#[derive(Debug)]
pub struct TaggedCell {
tag: Cell<Tag>,
}
impl TaggedCell {
pub fn new() -> Self {
Self {
tag: Cell::new(next_tag()),
}
}
pub fn tag(&self) -> Tag {
self.tag.get()
}
pub fn has_changed(&self, comparison_tag: Tag) -> bool {
self.tag.get() != comparison_tag
}
pub fn bump(&self) {
self.tag.set(next_tag());
}
}
impl Default for TaggedCell {
fn default() -> Self {
Self::new()
}
}
pub trait TaggedObject {
fn get_tag(&self) -> Tag;
}
impl TaggedObject for TaggedCell {
fn get_tag(&self) -> Tag {
self.tag()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fresh_tags_are_distinct() {
let a = next_tag();
let b = next_tag();
assert_ne!(a, b);
}
#[test]
fn bump_changes_tag() {
let c = TaggedCell::new();
let t0 = c.tag();
assert!(!c.has_changed(t0));
c.bump();
assert!(c.has_changed(t0));
let t1 = c.tag();
assert_ne!(t0, t1);
}
#[test]
fn none_never_matches_a_real_tag() {
let c = TaggedCell::new();
assert!(c.has_changed(Tag::NONE));
}
}