use super::data_type::DataType;
use crate::core::{
collections::FacetToCellsMap,
triangulation_data_structure::{Tds, TdsError},
};
use crate::geometry::traits::coordinate::ScalarAccumulative;
use arc_swap::ArcSwapOption;
use std::sync::{
Arc,
atomic::{AtomicU64, Ordering},
};
pub trait FacetCacheProvider<T, U, V, const D: usize>
where
T: ScalarAccumulative,
U: DataType,
V: DataType,
{
fn facet_cache(&self) -> &ArcSwapOption<FacetToCellsMap>;
fn cached_generation(&self) -> &AtomicU64;
fn try_build_cache_with_rcu(
&self,
tds: &Tds<T, U, V, D>,
) -> Result<Option<Arc<FacetToCellsMap>>, TdsError> {
let mut built: Option<Result<Arc<FacetToCellsMap>, TdsError>> = None;
let old_cache = self.facet_cache().rcu(|old| {
if let Some(existing) = old {
return Some(existing.clone());
}
#[expect(clippy::option_if_let_else)]
match built.get_or_insert_with(|| tds.build_facet_to_cells_map().map(Arc::new)) {
Ok(arc) => Some(arc.clone()),
Err(_) => None, }
});
match built {
Some(Ok(_)) => {
Ok(None)
}
Some(Err(e)) => {
Err(e)
}
None => {
Ok(old_cache)
}
}
}
fn try_get_or_build_facet_cache(
&self,
tds: &Tds<T, U, V, D>,
) -> Result<Arc<FacetToCellsMap>, TdsError> {
let mut current_generation = tds.generation();
loop {
let cached_generation = self.cached_generation().load(Ordering::Acquire);
if current_generation == cached_generation {
if let Some(existing_cache) = self.facet_cache().load_full() {
return Ok(existing_cache);
}
let built_cache = self.try_build_cache_with_rcu(tds)?;
if tds.generation() != current_generation {
current_generation = tds.generation();
continue;
}
if built_cache.is_none() && self.facet_cache().load_full().is_some() {
self.cached_generation()
.store(current_generation, Ordering::Release);
}
if let Some(cache) = self.facet_cache().load_full() {
return Ok(cache);
}
}
current_generation = tds.generation();
self.facet_cache().store(None);
let _old = self.try_build_cache_with_rcu(tds)?;
let rebuilt_generation = tds.generation();
if rebuilt_generation != current_generation {
current_generation = rebuilt_generation;
continue;
}
if let Some(cache) = self.facet_cache().load_full() {
self.cached_generation()
.store(current_generation, Ordering::Release);
return Ok(cache);
}
let new_cache = tds.build_facet_to_cells_map()?;
let new_cache_arc = Arc::new(new_cache);
self.facet_cache().store(Some(new_cache_arc.clone()));
let rebuilt_generation = tds.generation();
if rebuilt_generation != current_generation {
current_generation = rebuilt_generation;
continue;
}
self.cached_generation()
.store(current_generation, Ordering::Release);
return Ok(new_cache_arc);
}
}
fn invalidate_facet_cache(&self) {
self.facet_cache().store(None);
self.cached_generation().store(0, Ordering::Release);
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::delaunay_triangulation::DelaunayTriangulation;
use crate::core::triangulation_data_structure::Tds;
use crate::core::vertex;
use crate::geometry::kernel::AdaptiveKernel;
use std::sync::Arc;
use std::sync::Barrier;
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::thread;
use std::time::Duration;
struct TestCacheProvider {
facet_to_cells_cache: ArcSwapOption<FacetToCellsMap>,
cached_generation: AtomicU64,
}
impl TestCacheProvider {
fn new() -> Self {
Self {
facet_to_cells_cache: ArcSwapOption::empty(),
cached_generation: AtomicU64::new(0),
}
}
}
impl FacetCacheProvider<f64, (), (), 3> for TestCacheProvider {
fn facet_cache(&self) -> &ArcSwapOption<FacetToCellsMap> {
&self.facet_to_cells_cache
}
fn cached_generation(&self) -> &AtomicU64 {
&self.cached_generation
}
}
fn create_test_triangulation() -> DelaunayTriangulation<AdaptiveKernel<f64>, (), (), 3> {
let vertices = vec![
vertex!([0.0, 0.0, 0.0]),
vertex!([1.0, 0.0, 0.0]),
vertex!([0.0, 1.0, 0.0]),
vertex!([0.0, 0.0, 1.0]),
];
DelaunayTriangulation::new(&vertices).expect("Failed to create test triangulation")
}
#[test]
fn test_initial_cache_state() {
let provider = TestCacheProvider::new();
assert!(
provider.facet_cache().load().is_none(),
"Cache should be empty initially"
);
assert_eq!(
provider.cached_generation().load(Ordering::Relaxed),
0,
"Generation should be 0 initially"
);
}
#[test]
fn test_cache_building() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(
!cache.is_empty(),
"Cache should not be empty after building"
);
assert!(
provider.facet_cache().load().is_some(),
"Cache should be stored after building"
);
let tds_generation = dt.tds().generation();
let cached_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_eq!(
cached_generation, tds_generation,
"Cached generation should match TDS generation"
);
}
#[test]
fn test_cache_reuse() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let ptr1 = Arc::as_ptr(&cache1);
let ptr2 = Arc::as_ptr(&cache2);
assert_eq!(ptr1, ptr2, "Cache should be reused when generation matches");
assert_eq!(
cache1.len(),
cache2.len(),
"Cache content should be identical"
);
}
#[test]
fn test_cache_invalidation_on_generation_change() {
let provider = TestCacheProvider::new();
let mut dt = create_test_triangulation();
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let ptr1 = Arc::as_ptr(&cache1);
let initial_generation = dt.tds().generation();
let new_vertex = vertex!([0.2, 0.2, 0.2]);
dt.insert(new_vertex).expect("Failed to add vertex");
let new_generation = dt.tds().generation();
assert!(
new_generation > initial_generation,
"Generation should increase after adding vertex"
);
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let ptr2 = Arc::as_ptr(&cache2);
assert_ne!(
ptr1, ptr2,
"Cache should be rebuilt when generation changes"
);
assert!(!cache1.is_empty(), "Original cache should not be empty");
assert!(!cache2.is_empty(), "New cache should not be empty");
let new_cached_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_eq!(
new_cached_generation, new_generation,
"Cached generation should be updated after rebuild"
);
}
#[test]
fn test_manual_cache_invalidation() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(
provider.facet_cache().load().is_some(),
"Cache should exist after building"
);
let original_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_ne!(
original_generation, 0,
"Generation should not be 0 after caching"
);
provider.invalidate_facet_cache();
assert!(
provider.facet_cache().load().is_none(),
"Cache should be cleared after invalidation"
);
let reset_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_eq!(
reset_generation, 0,
"Generation should be reset to 0 after invalidation"
);
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let ptr1 = Arc::as_ptr(&cache1);
let ptr2 = Arc::as_ptr(&cache2);
assert_ne!(
ptr1, ptr2,
"Cache should be rebuilt after manual invalidation"
);
}
#[test]
fn test_cache_lifecycle() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
assert!(
provider.facet_cache().load().is_none(),
"Should start empty"
);
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(!cache1.is_empty(), "Cache should be built");
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert_eq!(
Arc::as_ptr(&cache1),
Arc::as_ptr(&cache2),
"Cache should be reused"
);
provider.invalidate_facet_cache();
assert!(
provider.facet_cache().load().is_none(),
"Cache should be invalidated"
);
let cache3 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert_ne!(
Arc::as_ptr(&cache1),
Arc::as_ptr(&cache3),
"Cache should be rebuilt"
);
assert_eq!(
cache1.len(),
cache3.len(),
"Content should be consistent after rebuild"
);
}
#[test]
fn test_concurrent_cache_access() {
let provider = Arc::new(TestCacheProvider::new());
let dt = Arc::new(create_test_triangulation());
let barrier = Arc::new(Barrier::new(4));
let mut handles = vec![];
for i in 0..4 {
let provider_clone = provider.clone();
let dt_clone = dt.clone();
let barrier_clone = barrier.clone();
let handle = thread::spawn(move || {
barrier_clone.wait();
let mut caches = vec![];
for _ in 0..5 {
let cache = provider_clone
.try_get_or_build_facet_cache(dt_clone.tds())
.unwrap();
caches.push(cache);
thread::sleep(Duration::from_millis(1)); }
(i, caches.len(), caches[0].len())
});
handles.push(handle);
}
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(results.len(), 4, "All threads should complete successfully");
let expected_size = results[0].2;
for (thread_id, cache_count, cache_size) in results {
assert_eq!(cache_count, 5, "Thread {thread_id} should get 5 caches");
assert_eq!(
cache_size, expected_size,
"Thread {thread_id} should get consistent cache size"
);
}
}
#[test]
fn test_build_cache_with_rcu() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
assert!(
provider.facet_cache().load().is_none(),
"Cache should be empty initially"
);
let old_value = provider.try_build_cache_with_rcu(dt.tds()).unwrap();
assert!(
old_value.is_none(),
"Old value should be None on first build"
);
let cached = provider.facet_cache().load_full();
assert!(
cached.is_some(),
"Cache should exist after build_cache_with_rcu"
);
let unwrapped_cache = cached.unwrap();
assert!(
!unwrapped_cache.is_empty(),
"Built cache should not be empty"
);
let old_value2 = provider.try_build_cache_with_rcu(dt.tds()).unwrap();
assert!(
old_value2.is_some(),
"Old value should be Some on second build"
);
let old_arc = old_value2.unwrap();
assert_eq!(
Arc::as_ptr(&old_arc),
Arc::as_ptr(&unwrapped_cache),
"Old value should be the previously built cache"
);
let cached2 = provider.facet_cache().load_full().unwrap();
assert_eq!(
Arc::as_ptr(&cached2),
Arc::as_ptr(&unwrapped_cache),
"Cache should not be rebuilt when it already exists"
);
}
#[test]
fn test_build_cache_with_rcu_concurrent() {
let provider = Arc::new(TestCacheProvider::new());
let dt = Arc::new(create_test_triangulation());
let barrier = Arc::new(Barrier::new(4));
let success_count = Arc::new(AtomicUsize::new(0));
let mut handles = vec![];
for i in 0..4 {
let provider_clone = provider.clone();
let dt_clone = dt.clone();
let barrier_clone = barrier.clone();
let success_count_clone = success_count.clone();
let handle = thread::spawn(move || {
barrier_clone.wait();
let cache_result = provider_clone.try_get_or_build_facet_cache(dt_clone.tds());
let success = cache_result.is_ok();
if success {
success_count_clone.fetch_add(1, Ordering::Relaxed);
}
(i, success)
});
handles.push(handle);
}
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
assert_eq!(results.len(), 4, "All threads should complete");
let success_count_final = success_count.load(Ordering::Relaxed);
assert_eq!(
success_count_final, 4,
"All threads should successfully get the cache"
);
for (thread_id, success) in results {
assert!(success, "Thread {thread_id} should succeed");
}
let final_cache = provider.facet_cache().load_full();
assert!(
final_cache.is_some(),
"Cache should exist after concurrent builds"
);
assert!(
!final_cache.unwrap().is_empty(),
"Cache should not be empty"
);
}
#[test]
fn test_generation_overflow_handling() {
let provider = TestCacheProvider::new();
let mut dt = create_test_triangulation();
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let initial_gen = dt.tds().generation();
let ptr1 = Arc::as_ptr(&cache1);
let operations = [
vertex!([0.2, 0.2, 0.2]),
vertex!([0.3, 0.3, 0.3]),
vertex!([0.4, 0.4, 0.4]),
];
for vertex in operations {
let prev_gen = dt.tds().generation();
let _ = dt.insert(vertex);
let new_gen = dt.tds().generation();
assert!(
new_gen >= prev_gen,
"Generation should not go backwards after operation"
);
}
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let ptr2 = Arc::as_ptr(&cache2);
let final_gen = dt.tds().generation();
assert_ne!(
ptr1, ptr2,
"Cache should be rebuilt after generation changes"
);
assert!(
final_gen > initial_gen,
"Generation should have increased after operations"
);
assert_eq!(
provider.cached_generation().load(Ordering::Relaxed),
final_gen,
"Provider should track current generation"
);
let gen_before_clear = dt.tds().generation();
dt.tri.tds.clear_all_neighbors();
let gen_after_clear = dt.tds().generation();
assert!(
gen_after_clear > gen_before_clear,
"Clearing neighbors should bump generation"
);
let cache3 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let ptr3 = Arc::as_ptr(&cache3);
assert_ne!(
ptr2, ptr3,
"Cache should be rebuilt after clear_all_neighbors"
);
}
#[test]
fn test_empty_triangulation() {
let provider = TestCacheProvider::new();
let tds: Tds<f64, (), (), 3> = Tds::empty();
let cache = provider.try_get_or_build_facet_cache(&tds).unwrap();
assert!(
cache.is_empty(),
"Cache should be empty for empty triangulation"
);
let tds_generation = tds.generation();
let cached_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_eq!(
cached_generation, tds_generation,
"Generation should be tracked even for empty triangulation"
);
}
#[test]
fn test_cache_content_correctness() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let provider_cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let reference_cache = dt.tds().build_facet_to_cells_map().unwrap();
assert_eq!(
provider_cache.len(),
reference_cache.len(),
"Provider cache should match reference cache size"
);
for key in reference_cache.keys() {
assert!(
provider_cache.contains_key(key),
"Provider cache should contain all reference keys"
);
}
for (key, reference_cells) in &reference_cache {
if let Some(provider_cells) = provider_cache.get(key) {
assert_eq!(
provider_cells.len(),
reference_cells.len(),
"Cell count should match for facet key {key}"
);
for cell in reference_cells {
assert!(
provider_cells.contains(cell),
"Provider cache should contain cell {cell:?} for key {key}"
);
}
} else {
panic!("Provider cache missing key {key} found in reference");
}
}
}
#[test]
fn test_strict_try_get_or_build_facet_cache() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let cache_result = provider.try_get_or_build_facet_cache(dt.tds());
assert!(cache_result.is_ok(), "Strict method should succeed");
let cache = cache_result.unwrap();
assert!(!cache.is_empty(), "Cache should be built and not empty");
assert!(
provider.facet_cache().load().is_some(),
"Cache should be stored after building"
);
let tds_generation = dt.tds().generation();
let cached_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_eq!(
cached_generation, tds_generation,
"Cached generation should match TDS generation"
);
let second_cache_result = provider.try_get_or_build_facet_cache(dt.tds());
assert!(
second_cache_result.is_ok(),
"Second call should also succeed"
);
let cache2 = second_cache_result.unwrap();
assert_eq!(
Arc::as_ptr(&cache),
Arc::as_ptr(&cache2),
"Cache should be reused when generation matches"
);
}
#[test]
fn test_strict_try_build_cache_with_rcu() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
assert!(
provider.facet_cache().load().is_none(),
"Cache should be empty initially"
);
let old_value_result = provider.try_build_cache_with_rcu(dt.tds());
assert!(
old_value_result.is_ok(),
"try_build_cache_with_rcu should succeed"
);
let old_value = old_value_result.unwrap();
assert!(
old_value.is_none(),
"Old value should be None on first build"
);
let cached = provider.facet_cache().load_full();
assert!(
cached.is_some(),
"Cache should exist after try_build_cache_with_rcu"
);
let unwrapped_cache = cached.unwrap();
assert!(
!unwrapped_cache.is_empty(),
"Built cache should not be empty"
);
let second_old_result = provider.try_build_cache_with_rcu(dt.tds());
assert!(
second_old_result.is_ok(),
"Second try_build_cache_with_rcu should succeed"
);
let old_value2 = second_old_result.unwrap();
assert!(
old_value2.is_some(),
"Old value should be Some on second build"
);
let old_arc = old_value2.unwrap();
assert_eq!(
Arc::as_ptr(&old_arc),
Arc::as_ptr(&unwrapped_cache),
"Old value should be the previously built cache"
);
}
#[test]
fn test_strict_cache_invalidation_behavior() {
let provider = TestCacheProvider::new();
let mut dt = create_test_triangulation();
let cache1_result = provider.try_get_or_build_facet_cache(dt.tds());
assert!(cache1_result.is_ok(), "Initial cache build should succeed");
let cache1 = cache1_result.unwrap();
let ptr1 = Arc::as_ptr(&cache1);
let initial_generation = dt.tds().generation();
let new_vertex = vertex!([0.2, 0.2, 0.2]);
dt.insert(new_vertex).expect("Failed to add vertex");
let new_generation = dt.tds().generation();
assert!(
new_generation > initial_generation,
"Generation should increase after adding vertex"
);
let cache2_result = provider.try_get_or_build_facet_cache(dt.tds());
assert!(cache2_result.is_ok(), "Cache rebuild should succeed");
let cache2 = cache2_result.unwrap();
let ptr2 = Arc::as_ptr(&cache2);
assert_ne!(
ptr1, ptr2,
"Cache should be rebuilt when generation changes"
);
let new_cached_generation = provider.cached_generation().load(Ordering::Relaxed);
assert_eq!(
new_cached_generation, new_generation,
"Cached generation should be updated after rebuild"
);
}
#[test]
fn test_cache_methods_can_be_called_multiple_times() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let _cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let cache_strict_result = provider.try_get_or_build_facet_cache(dt.tds());
assert!(
cache_strict_result.is_ok(),
"Cache getter should succeed on repeated calls"
);
provider.invalidate_facet_cache(); let _old = provider.try_build_cache_with_rcu(dt.tds()).unwrap();
let old_strict_result = provider.try_build_cache_with_rcu(dt.tds());
assert!(
old_strict_result.is_ok(),
"Cache builder should succeed on repeated calls"
);
}
#[test]
fn test_cache_invalidation_idempotence() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let _cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(provider.facet_cache().load().is_some());
provider.invalidate_facet_cache();
assert!(provider.facet_cache().load().is_none());
assert_eq!(provider.cached_generation().load(Ordering::Relaxed), 0);
provider.invalidate_facet_cache();
assert!(provider.facet_cache().load().is_none());
assert_eq!(provider.cached_generation().load(Ordering::Relaxed), 0);
provider.invalidate_facet_cache();
assert!(provider.facet_cache().load().is_none());
assert_eq!(provider.cached_generation().load(Ordering::Relaxed), 0);
let cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(!cache.is_empty());
}
#[test]
fn test_cache_race_condition_on_invalidation() {
let provider = Arc::new(TestCacheProvider::new());
let dt = Arc::new(create_test_triangulation());
let barrier = Arc::new(Barrier::new(2));
let _initial_cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let provider_clone = Arc::clone(&provider);
let dt_clone = Arc::clone(&dt);
let barrier_clone = Arc::clone(&barrier);
let getter_thread = thread::spawn(move || {
barrier_clone.wait();
for _ in 0..100 {
provider_clone
.try_get_or_build_facet_cache(dt_clone.tds())
.expect("cache retrieval should succeed during invalidation race");
thread::sleep(Duration::from_micros(10));
}
});
let invalidator_thread = thread::spawn(move || {
barrier.wait();
for _ in 0..50 {
provider.invalidate_facet_cache();
thread::sleep(Duration::from_micros(20));
}
});
getter_thread.join().expect("Getter thread should complete");
invalidator_thread
.join()
.expect("Invalidator thread should complete");
}
#[test]
fn test_cache_with_modified_tds_during_build() {
let provider = TestCacheProvider::new();
let mut dt = create_test_triangulation();
let initial_gen = dt.tds().generation();
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let test_vertices = [
vertex!([0.2, 0.2, 0.2]),
vertex!([0.3, 0.3, 0.1]), vertex!([0.2, 0.1, 0.3]), ];
for vertex in test_vertices {
dt.insert(vertex).expect("Failed to add vertex");
}
let final_gen = dt.tds().generation();
assert!(final_gen > initial_gen, "Generation should have increased");
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert_ne!(
Arc::as_ptr(&cache1),
Arc::as_ptr(&cache2),
"Cache should be different after TDS modifications"
);
assert_eq!(
provider.cached_generation().load(Ordering::Relaxed),
final_gen,
"Cached generation should match final TDS generation"
);
}
#[test]
fn test_cache_methods_succeed_on_valid_tds() {
let provider = TestCacheProvider::new();
let dt = create_test_triangulation();
let cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(
!cache.is_empty(),
"Cache getter should return a non-empty cache"
);
provider.invalidate_facet_cache();
let old_value = provider.try_build_cache_with_rcu(dt.tds()).unwrap();
assert!(old_value.is_none(), "Should return None for first build");
assert!(provider.facet_cache().load().is_some());
}
#[test]
fn test_cache_size_consistency_after_operations() {
let provider = TestCacheProvider::new();
let mut dt = create_test_triangulation();
let cache1 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let size1 = cache1.len();
provider.invalidate_facet_cache();
let cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert_eq!(
cache2.len(),
size1,
"Size should be consistent after invalidate/rebuild"
);
dt.insert(vertex!([0.2, 0.2, 0.2]))
.expect("Failed to add vertex");
let cache3 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
assert!(
cache3.len() >= size1,
"Cache size should grow or stay same after adding vertex"
);
}
#[test]
fn test_cache_generation_ordering_semantics() {
let provider = Arc::new(TestCacheProvider::new());
let dt = Arc::new(create_test_triangulation());
let _cache = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let gen_after_build = provider.cached_generation().load(Ordering::Acquire);
assert_eq!(gen_after_build, dt.tds().generation());
provider.invalidate_facet_cache();
let gen_after_invalidate = provider.cached_generation().load(Ordering::Acquire);
assert_eq!(
gen_after_invalidate, 0,
"Generation should be reset after invalidate"
);
let _cache2 = provider.try_get_or_build_facet_cache(dt.tds()).unwrap();
let gen_after_rebuild = provider.cached_generation().load(Ordering::Acquire);
assert_eq!(gen_after_rebuild, dt.tds().generation());
}
}