1use std::sync::atomic::{AtomicUsize, AtomicU64, Ordering};
10use std::sync::Arc;
11use crossbeam::queue::SegQueue;
12use std::time::Instant;
13use dashmap::DashMap;
14use std::sync::atomic::AtomicBool;
15use tracing::warn;
16
17static BATCH_TOKENS: AtomicU64 = AtomicU64::new(crate::constants::WAL_BATCH_TOKENS_INITIAL as u64);
18const MAX_WRITERS_BEFORE_BARRIER: usize = crate::constants::WAL_MAX_WRITERS_BEFORE_BARRIER;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum WalOpKind {
22 Write,
23 Rename,
24 Truncate,
25 Metadata,
26 Barrier,
27}
28
29#[derive(Debug)]
30pub struct WalEntry {
32 pub id: u64,
33 pub inode: u64,
34 pub kind: WalOpKind,
35 pub seq: u64,
36 pub timestamp: Instant,
37 pub completed: AtomicBool,
38}
39
40struct InodeState {
41 active_writers: AtomicUsize,
42 pending_barriers: AtomicUsize,
43 exclusive_locked: AtomicBool,
44 _last_seq: AtomicU64,
45}
46
47pub struct InMemoryWal {
49 log_queue: SegQueue<Arc<WalEntry>>,
50 inode_states: DashMap<u64, Arc<InodeState>>,
51 global_seq: AtomicU64,
52}
53
54impl InMemoryWal {
55 pub fn new() -> Arc<Self> {
57 Arc::new(Self {
58 log_queue: SegQueue::new(),
59 inode_states: DashMap::new(),
60 global_seq: AtomicU64::new(0),
61 })
62 }
63
64 pub fn get_next_seq(&self) -> u64 {
66 let seq = self.global_seq.fetch_add(1, Ordering::SeqCst);
67 if seq >= u64::MAX - crate::constants::WAL_SEQUENCE_OVERFLOW_THRESHOLD {
68 warn!("WAL sequence approaching overflow. Resetting.");
69 self.global_seq.store(1, Ordering::SeqCst);
70 return 1;
71 }
72 seq
73 }
74
75 pub async fn acquire_barrier(&self, inode: u64, kind: WalOpKind) -> WalGuard<'_> {
77 let state = self.inode_states.entry(inode).or_insert_with(|| Arc::new(InodeState {
78 active_writers: AtomicUsize::new(0),
79 pending_barriers: AtomicUsize::new(0),
80 exclusive_locked: AtomicBool::new(false),
81 _last_seq: AtomicU64::new(0),
82 })).clone();
83
84 let backoff = crossbeam::utils::Backoff::new();
85
86 loop {
87 if state.exclusive_locked.load(Ordering::Acquire) {
89 backoff.snooze();
90 if backoff.is_completed() {
91 tokio::task::yield_now().await;
92 backoff.reset();
93 }
94 continue;
95 }
96
97 match kind {
98 WalOpKind::Write | WalOpKind::Metadata => {
99 let pending = state.pending_barriers.load(Ordering::Acquire);
101 if pending > 0 {
102 let active = state.active_writers.load(Ordering::Acquire);
104 if active >= MAX_WRITERS_BEFORE_BARRIER {
105 backoff.snooze();
106 if backoff.is_completed() {
107 tokio::task::yield_now().await;
108 backoff.reset();
109 }
110 continue;
111 }
112 }
113
114 state.active_writers.fetch_add(1, Ordering::SeqCst);
115 if state.exclusive_locked.load(Ordering::Acquire) {
117 state.active_writers.fetch_sub(1, Ordering::SeqCst);
118 continue;
119 }
120 break;
121 },
122 WalOpKind::Rename | WalOpKind::Truncate | WalOpKind::Barrier => {
123 state.pending_barriers.fetch_add(1, Ordering::SeqCst);
125
126 loop {
127 if state.active_writers.load(Ordering::Acquire) == 0
128 && state.exclusive_locked.compare_exchange(
129 false, true, Ordering::AcqRel, Ordering::Relaxed
130 ).is_ok() {
131 if state.active_writers.load(Ordering::Acquire) != 0 {
134 state.exclusive_locked.store(false, Ordering::Release);
135 continue;
136 }
137 break;
138 }
139
140 backoff.snooze();
141 if backoff.is_completed() {
142 tokio::task::yield_now().await;
145 backoff.reset();
146 }
147 }
148 state.pending_barriers.fetch_sub(1, Ordering::SeqCst);
150 break;
151 },
152 }
153 }
154
155 let id = self.get_next_seq();
156 let entry = Arc::new(WalEntry {
157 id,
158 inode,
159 kind,
160 seq: 0,
161 timestamp: Instant::now(),
162 completed: AtomicBool::new(false),
163 });
164
165 self.log_queue.push(entry.clone());
166 self.trigger_batch_check();
167
168 WalGuard {
169 wal: self,
170 inode,
171 kind,
172 entry,
173 }
174 }
175
176 fn trigger_batch_check(&self) -> bool {
177 let tokens = BATCH_TOKENS.fetch_sub(1, Ordering::SeqCst);
178 if tokens == 0 {
179 BATCH_TOKENS.store(crate::constants::WAL_BATCH_TOKENS_INITIAL as u64, Ordering::SeqCst);
180 return true;
181 }
182 false
183 }
184
185 fn release(&self, inode: u64, kind: WalOpKind, entry: &Arc<WalEntry>) {
186 entry.completed.store(true, Ordering::Release);
187 if let Some(state) = self.inode_states.get(&inode) {
188 match kind {
189 WalOpKind::Write | WalOpKind::Metadata => {
190 state.active_writers.fetch_sub(1, Ordering::SeqCst);
191 },
192 WalOpKind::Rename | WalOpKind::Truncate | WalOpKind::Barrier => {
193 state.exclusive_locked.store(false, Ordering::SeqCst);
194 },
195 }
196 }
197 }
198}
199
200pub struct WalGuard<'a> {
202 wal: &'a InMemoryWal,
203 inode: u64,
204 kind: WalOpKind,
205 entry: Arc<WalEntry>,
206}
207
208impl<'a> Drop for WalGuard<'a> {
209 fn drop(&mut self) {
210 self.wal.release(self.inode, self.kind, &self.entry);
211 }
212}
213
214#[cfg(test)]
215#[allow(clippy::unwrap_used, clippy::expect_used)]
216mod wal_tests {
217 use super::*;
218
219 fn rt() -> tokio::runtime::Runtime {
220 tokio::runtime::Builder::new_multi_thread()
221 .worker_threads(2)
222 .enable_all()
223 .build()
224 .unwrap()
225 }
226
227 #[test]
228 fn test_shared_write_lock_permits_concurrent_access() {
229 let wal = InMemoryWal::new();
230 rt().block_on(async {
231 let guard1 = wal.acquire_barrier(1, WalOpKind::Write).await;
232 let guard2 = wal.acquire_barrier(1, WalOpKind::Write).await;
233 assert_eq!(guard1.kind, WalOpKind::Write);
235 assert_eq!(guard2.kind, WalOpKind::Write);
236 drop(guard1);
237 drop(guard2);
238 });
239 }
240
241 #[test]
242 fn test_write_lock_increments_active_writers() {
243 let wal = InMemoryWal::new();
244 rt().block_on(async {
245 let guard = wal.acquire_barrier(1, WalOpKind::Write).await;
246 let state = wal.inode_states.get(&1).unwrap();
248 assert_eq!(state.active_writers.load(Ordering::SeqCst), 1);
249 drop(guard);
250 let state = wal.inode_states.get(&1).unwrap();
252 assert_eq!(state.active_writers.load(Ordering::SeqCst), 0);
253 });
254 }
255
256 #[test]
257 fn test_exclusive_lock_release_cycle() {
258 let wal = InMemoryWal::new();
259 wal.inode_states.insert(50, Arc::new(InodeState {
262 active_writers: AtomicUsize::new(0),
263 pending_barriers: AtomicUsize::new(0),
264 exclusive_locked: AtomicBool::new(true),
265 _last_seq: AtomicU64::new(0),
266 }));
267 let entry = Arc::new(WalEntry {
268 id: 1,
269 inode: 50,
270 kind: WalOpKind::Rename,
271 seq: 0,
272 timestamp: Instant::now(),
273 completed: AtomicBool::new(false),
274 });
275 wal.release(50, WalOpKind::Rename, &entry);
277 let state = wal.inode_states.get(&50).unwrap();
278 assert!(!state.exclusive_locked.load(Ordering::SeqCst));
279 assert!(entry.completed.load(Ordering::SeqCst));
280 }
281
282 #[test]
283 fn test_guard_drop_releases() {
284 let wal = InMemoryWal::new();
285 rt().block_on(async {
286 let guard = wal.acquire_barrier(3, WalOpKind::Write).await;
287 let entry_clone = guard.entry.clone();
288 assert!(!entry_clone.completed.load(Ordering::SeqCst));
289 drop(guard);
290 assert!(entry_clone.completed.load(Ordering::SeqCst));
291 let guard2 = wal.acquire_barrier(3, WalOpKind::Write).await;
293 assert_eq!(guard2.kind, WalOpKind::Write);
294 drop(guard2);
295 });
296 }
297
298 #[test]
299 fn test_different_inodes_independent() {
300 let wal = InMemoryWal::new();
301 rt().block_on(async {
302 let guard_a = wal.acquire_barrier(10, WalOpKind::Write).await;
303 let guard_b = wal.acquire_barrier(20, WalOpKind::Write).await;
304 assert_eq!(guard_a.inode, 10);
305 assert_eq!(guard_b.inode, 20);
306 let state_a = wal.inode_states.get(&10).unwrap();
307 let state_b = wal.inode_states.get(&20).unwrap();
308 assert_eq!(state_a.active_writers.load(Ordering::SeqCst), 1);
309 assert_eq!(state_b.active_writers.load(Ordering::SeqCst), 1);
310 drop(guard_a);
311 drop(guard_b);
312 });
313 }
314
315 #[test]
316 fn test_metadata_increments_active_writers() {
317 let wal = InMemoryWal::new();
318 rt().block_on(async {
319 let guard = wal.acquire_barrier(30, WalOpKind::Metadata).await;
320 assert_eq!(guard.kind, WalOpKind::Metadata);
321 let state = wal.inode_states.get(&30).unwrap();
322 assert!(!state.exclusive_locked.load(Ordering::SeqCst));
323 assert_eq!(state.active_writers.load(Ordering::SeqCst), 1);
324 drop(guard);
325 let state = wal.inode_states.get(&30).unwrap();
326 assert_eq!(state.active_writers.load(Ordering::SeqCst), 0);
327 });
328 }
329
330 #[test]
331 fn test_metadata_respects_barriers() {
332 let wal = InMemoryWal::new();
333 rt().block_on(async {
334 let rename_guard = wal.acquire_barrier(40, WalOpKind::Rename).await;
335 {
336 let state = wal.inode_states.get(&40).unwrap();
337 assert!(state.exclusive_locked.load(Ordering::SeqCst));
338 }
339
340 drop(rename_guard);
341
342 let meta_guard = wal.acquire_barrier(40, WalOpKind::Metadata).await;
343 {
344 let state = wal.inode_states.get(&40).unwrap();
345 assert_eq!(state.active_writers.load(Ordering::SeqCst), 1);
346 }
347
348 let meta_guard2 = wal.acquire_barrier(40, WalOpKind::Metadata).await;
349 {
350 let state = wal.inode_states.get(&40).unwrap();
351 assert_eq!(state.active_writers.load(Ordering::SeqCst), 2);
352 }
353
354 drop(meta_guard);
355 drop(meta_guard2);
356 {
357 let state = wal.inode_states.get(&40).unwrap();
358 assert_eq!(state.active_writers.load(Ordering::SeqCst), 0);
359 }
360 });
361 }
362
363 #[test]
364 fn test_metadata_then_rename_sequential() {
365 let wal = InMemoryWal::new();
366 rt().block_on(async {
367 let meta_guard = wal.acquire_barrier(50, WalOpKind::Metadata).await;
368 {
369 let state = wal.inode_states.get(&50).unwrap();
370 assert_eq!(state.active_writers.load(Ordering::SeqCst), 1);
371 }
372 drop(meta_guard);
373 {
374 let state = wal.inode_states.get(&50).unwrap();
375 assert_eq!(state.active_writers.load(Ordering::SeqCst), 0);
376 }
377
378 let rename_guard = wal.acquire_barrier(50, WalOpKind::Rename).await;
379 {
380 let state = wal.inode_states.get(&50).unwrap();
381 assert!(state.exclusive_locked.load(Ordering::SeqCst));
382 }
383 drop(rename_guard);
384 {
385 let state = wal.inode_states.get(&50).unwrap();
386 assert!(!state.exclusive_locked.load(Ordering::SeqCst));
387 }
388 });
389 }
390
391 #[test]
392 fn test_get_next_seq_increments() {
393 let wal = InMemoryWal::new();
394 let s1 = wal.get_next_seq();
395 let s2 = wal.get_next_seq();
396 assert_eq!(s1, 0);
397 assert_eq!(s2, 1);
398 }
399
400 #[test]
404 fn test_wal_barrier_no_concurrent_writer() {
405 use std::sync::atomic::AtomicBool;
406
407 let wal = Arc::new(InMemoryWal::new());
408 let violation = Arc::new(AtomicBool::new(false));
409 let inode: u64 = 9999;
410
411 let runtime = tokio::runtime::Builder::new_multi_thread()
412 .worker_threads(8)
413 .enable_all()
414 .build()
415 .unwrap();
416
417 runtime.block_on(async {
418 let mut handles = Vec::new();
419
420 {
421 let wal = wal.clone();
422 let violation = violation.clone();
423 handles.push(tokio::spawn(async move {
424 for _ in 0..5 {
425 let guard = wal.acquire_barrier(inode, WalOpKind::Write).await;
426 if wal.inode_states.get(&inode).unwrap()
427 .exclusive_locked.load(Ordering::Acquire)
428 {
429 violation.store(true, Ordering::Release);
430 }
431 tokio::task::yield_now().await;
432 drop(guard);
433 tokio::task::yield_now().await;
434 }
435 }));
436 }
437
438 {
439 let wal = wal.clone();
440 let violation = violation.clone();
441 handles.push(tokio::spawn(async move {
442 for _ in 0..5 {
443 let guard = wal.acquire_barrier(inode, WalOpKind::Rename).await;
444 if wal.inode_states.get(&inode).unwrap()
445 .active_writers.load(Ordering::Acquire) != 0
446 {
447 violation.store(true, Ordering::Release);
448 }
449 tokio::task::yield_now().await;
450 drop(guard);
451 tokio::task::yield_now().await;
452 }
453 }));
454 }
455
456 for h in handles {
457 h.await.unwrap();
458 }
459 });
460
461 assert!(
462 !violation.load(Ordering::Acquire),
463 "TOCTOU violation: exclusive lock held while writers active, or writer running under exclusive lock"
464 );
465 }
466
467 #[test]
468 fn test_wal_truncate_takes_exclusive_lock() {
469 let wal = InMemoryWal::new();
470 rt().block_on(async {
471 let guard = wal.acquire_barrier(100, WalOpKind::Truncate).await;
472 assert_eq!(guard.kind, WalOpKind::Truncate);
473 let state = wal.inode_states.get(&100).unwrap();
474 assert!(state.exclusive_locked.load(Ordering::SeqCst));
475 assert_eq!(state.active_writers.load(Ordering::SeqCst), 0);
476 drop(guard);
477 let state = wal.inode_states.get(&100).unwrap();
478 assert!(!state.exclusive_locked.load(Ordering::SeqCst));
479 });
480 }
481
482 #[test]
483 fn test_wal_barrier_takes_exclusive_lock() {
484 let wal = InMemoryWal::new();
485 rt().block_on(async {
486 let guard = wal.acquire_barrier(200, WalOpKind::Barrier).await;
487 assert_eq!(guard.kind, WalOpKind::Barrier);
488 let state = wal.inode_states.get(&200).unwrap();
489 assert!(state.exclusive_locked.load(Ordering::SeqCst));
490 assert_eq!(state.active_writers.load(Ordering::SeqCst), 0);
491 drop(guard);
492 let state = wal.inode_states.get(&200).unwrap();
493 assert!(!state.exclusive_locked.load(Ordering::SeqCst));
494 });
495 }
496
497 #[test]
498 fn test_wal_sequence_overflow_resets() {
499 let wal = InMemoryWal::new();
500 wal.global_seq.store(u64::MAX - 10000, Ordering::SeqCst);
502 let seq = wal.get_next_seq();
504 assert_eq!(seq, 1, "overflow should trigger reset and return 1");
505 let _seq2 = wal.get_next_seq();
507 let seq3 = wal.get_next_seq();
508 assert!(seq3 < 100, "post-overflow sequences should be small, got {}", seq3);
509 }
510}