use crate::core::types::LockId;
use fxhash::{FxHashMap, FxHashSet};
use std::collections::VecDeque;
#[derive(Debug, Clone)]
struct CacheEntry {
generation: u64,
result: Option<Vec<LockId>>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LockOrderEdge {
pub before: LockId,
pub after: LockId,
}
#[derive(Debug)]
pub struct LockOrderGraph {
edges: FxHashMap<LockId, FxHashSet<LockId>>,
reverse_edges: FxHashMap<LockId, FxHashSet<LockId>>,
all_edges: FxHashSet<LockOrderEdge>,
cycle_cache: FxHashMap<(LockId, LockId), CacheEntry>,
generation: u64,
}
impl LockOrderGraph {
pub fn new() -> Self {
LockOrderGraph {
edges: FxHashMap::default(),
reverse_edges: FxHashMap::default(),
all_edges: FxHashSet::default(),
cycle_cache: FxHashMap::default(),
generation: 0,
}
}
pub fn add_edge(&mut self, before: LockId, after: LockId) -> Option<Vec<LockId>> {
if before == after {
return None;
}
let cache_key = (before, after);
if let Some(cached) = self.cycle_cache.get(&cache_key) {
if cached.generation == self.generation {
return cached.result.clone();
}
}
let cycle_result = if let Some(cycle) = self.find_path(after, before) {
let mut full_cycle = cycle;
full_cycle.push(after); Some(full_cycle)
} else {
None
};
self.cycle_cache.insert(
cache_key,
CacheEntry {
generation: self.generation,
result: cycle_result.clone(),
},
);
if cycle_result.is_none() {
let edge = LockOrderEdge { before, after };
if self.all_edges.insert(edge) {
self.edges.entry(before).or_default().insert(after);
self.reverse_edges.entry(after).or_default().insert(before);
self.generation = self.generation.wrapping_add(1);
if self.cycle_cache.len() > 1000 {
self.cycle_cache.clear();
}
}
}
cycle_result
}
fn find_path(&self, start: LockId, end: LockId) -> Option<Vec<LockId>> {
if start == end {
return Some(vec![start]);
}
if !self.edges.contains_key(&start) {
return None;
}
let mut queue = VecDeque::new();
let mut visited = FxHashSet::default();
let mut parent: FxHashMap<LockId, LockId> = FxHashMap::default();
queue.push_back(start);
visited.insert(start);
while let Some(current) = queue.pop_front() {
if let Some(neighbors) = self.edges.get(¤t) {
for &neighbor in neighbors {
if !visited.contains(&neighbor) {
visited.insert(neighbor);
parent.insert(neighbor, current);
if neighbor == end {
let mut path = vec![end];
let mut node = end;
while let Some(&prev) = parent.get(&node) {
path.push(prev);
node = prev;
}
path.reverse();
return Some(path);
}
queue.push_back(neighbor);
}
}
}
}
None
}
pub fn remove_lock(&mut self, lock_id: LockId) {
if let Some(successors) = self.edges.remove(&lock_id) {
for successor in successors {
if let Some(preds) = self.reverse_edges.get_mut(&successor) {
preds.remove(&lock_id);
}
self.all_edges.remove(&LockOrderEdge {
before: lock_id,
after: successor,
});
}
}
if let Some(predecessors) = self.reverse_edges.remove(&lock_id) {
for predecessor in predecessors {
if let Some(succs) = self.edges.get_mut(&predecessor) {
succs.remove(&lock_id);
}
self.all_edges.remove(&LockOrderEdge {
before: predecessor,
after: lock_id,
});
}
}
}
}
impl Default for LockOrderGraph {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_cycle_detection() {
let mut graph = LockOrderGraph::new();
assert!(graph.add_edge(1, 2).is_none());
if let Some(cycle) = graph.add_edge(2, 1) {
assert!(cycle.len() >= 2); assert!(cycle.contains(&1)); assert!(cycle.contains(&2)); } else {
panic!("Should have detected cycle");
}
}
#[test]
fn test_direct_cycle() {
let mut graph = LockOrderGraph::new();
assert!(graph.add_edge(1, 2).is_none());
assert!(graph.add_edge(2, 1).is_some());
}
#[test]
fn test_no_false_cycles() {
let mut graph = LockOrderGraph::new();
assert!(graph.add_edge(1, 2).is_none());
assert!(graph.add_edge(2, 3).is_none());
assert!(graph.add_edge(1, 3).is_none()); }
#[test]
fn test_cache_behavior() {
let mut graph = LockOrderGraph::new();
assert!(graph.add_edge(1, 2).is_none());
assert!(graph.add_edge(1, 2).is_none());
assert!(graph.add_edge(3, 4).is_none());
assert!(graph.add_edge(3, 4).is_none());
}
}