fxcp_core/consistency/
serialization.rs1use std::collections::HashMap;
9use std::sync::Arc;
10use crate::consistency::sequencer::{GlobalSequencer, SequenceBarrier};
11use std::path::PathBuf;
12use tracing::{debug, warn};
13use tokio::sync::Notify;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum OpKind {
18 Write,
19 Rename,
20}
21
22pub struct OpGuard {
24 engine: Arc<SerializationEngine>,
25 inode: u64,
26 ticket: u64,
27}
28
29impl Drop for OpGuard {
30 fn drop(&mut self) {
31 self.engine.complete_op(self.inode, self.ticket);
32 }
33}
34
35pub struct PathGuard {
37 engine: Arc<SerializationEngine>,
38 path: PathBuf,
39}
40
41impl Drop for PathGuard {
42 fn drop(&mut self) {
43 self.engine.release_path(&self.path);
44 }
45}
46
47struct InodeState {
48 sequencer: GlobalSequencer,
49 barrier: SequenceBarrier,
50}
51
52impl InodeState {
53 fn new() -> Self {
54 Self {
55 sequencer: GlobalSequencer::new(0),
56 barrier: SequenceBarrier::new(0),
57 }
58 }
59}
60
61pub struct SerializationEngine {
63 state: std::sync::Mutex<HashMap<u64, Arc<InodeState>>>,
64 path_locks: std::sync::Mutex<HashMap<PathBuf, Arc<Notify>>>,
65}
66
67impl SerializationEngine {
68 pub fn new() -> Arc<Self> {
70 Arc::new(Self {
71 state: std::sync::Mutex::new(HashMap::new()),
72 path_locks: std::sync::Mutex::new(HashMap::new()),
73 })
74 }
75
76 pub fn is_active(&self, inode: u64) -> bool {
78 let state = self.state.lock().unwrap_or_else(|poisoned| {
79 warn!("Mutex poisoned in SerializationEngine::is_active, recovering: {}", poisoned);
80 poisoned.into_inner()
81 });
82 state.contains_key(&inode)
83 }
84
85 pub async fn acquire_barrier(self: &Arc<Self>, inode: u64, _kind: OpKind) -> Result<OpGuard, std::io::Error> {
89 let (inode_state, ticket) = {
91 let mut map = self.state.lock().unwrap_or_else(|poisoned| {
92 warn!("Mutex poisoned in SerializationEngine::acquire_barrier, recovering: {}", poisoned);
93 poisoned.into_inner()
94 });
95 let state = map.entry(inode).or_insert_with(|| Arc::new(InodeState::new())).clone();
96
97 let ticket = state.sequencer.next();
102
103 (state, ticket)
104 };
105
106 let dependency = ticket - 1;
110
111 if dependency > 0 {
112 inode_state.barrier.wait_for(dependency).await;
115 }
116
117 Ok(OpGuard {
118 engine: self.clone(),
119 inode,
120 ticket,
121 })
122 }
123
124 pub async fn acquire_path_barrier(self: &Arc<Self>, path: &PathBuf) -> Result<PathGuard, std::io::Error> {
126 loop {
127 let wait_notify = {
128 let mut locks = self.path_locks.lock().unwrap_or_else(|poisoned| {
129 warn!("Mutex poisoned in SerializationEngine::acquire_path_barrier, recovering: {}", poisoned);
130 poisoned.into_inner()
131 });
132 if let Some(notify) = locks.get(path) {
133 Some(notify.clone())
134 } else {
135 locks.insert(path.clone(), Arc::new(Notify::new()));
136 None
137 }
138 };
139
140 if let Some(notify) = wait_notify {
141 notify.notified().await;
142 } else {
143 return Ok(PathGuard {
144 engine: self.clone(),
145 path: path.clone(),
146 });
147 }
148 }
149 }
150
151 fn release_path(&self, path: &PathBuf) {
152 let mut locks = self.path_locks.lock().unwrap_or_else(|poisoned| {
153 warn!("Mutex poisoned in SerializationEngine::release_path, recovering: {}", poisoned);
154 poisoned.into_inner()
155 });
156 if let Some(notify) = locks.remove(path) {
157 notify.notify_waiters();
158 }
159 }
160
161 pub fn check_sequence(&self, _inode: u64, _seq: u64) -> bool {
165 true
166 }
167
168 pub fn update_sequence(&self, _inode: u64, _seq: u64) {
170 }
172
173 fn complete_op(&self, inode: u64, ticket: u64) {
174 let state_opt = {
176 let map = self.state.lock().unwrap_or_else(|poisoned| {
177 warn!("Mutex poisoned in SerializationEngine::complete_op, recovering: {}", poisoned);
178 poisoned.into_inner()
179 });
180 map.get(&inode).cloned()
181 };
182
183 if let Some(state) = state_opt {
184 debug!("Serialization: Completed ticket {} for inode {}", ticket, inode);
185 state.barrier.complete(ticket);
186
187 } else {
193 warn!("Serialization: Attempted to complete op for unknown inode {}", inode);
194 }
195 }
196}