1use std::collections::{HashMap, HashSet, VecDeque};
8use std::sync::{
9 Condvar, Mutex, MutexGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, WaitTimeoutResult,
10};
11use std::time::{Duration, Instant};
12
13pub type TxnId = u64;
15
16fn read_unpoisoned<'a, T>(lock: &'a RwLock<T>) -> RwLockReadGuard<'a, T> {
17 match lock.read() {
18 Ok(guard) => guard,
19 Err(poisoned) => poisoned.into_inner(),
20 }
21}
22
23fn write_unpoisoned<'a, T>(lock: &'a RwLock<T>) -> RwLockWriteGuard<'a, T> {
24 match lock.write() {
25 Ok(guard) => guard,
26 Err(poisoned) => poisoned.into_inner(),
27 }
28}
29
30fn mutex_unpoisoned<'a, T>(lock: &'a Mutex<T>) -> MutexGuard<'a, T> {
31 match lock.lock() {
32 Ok(guard) => guard,
33 Err(poisoned) => poisoned.into_inner(),
34 }
35}
36
37fn wait_timeout_unpoisoned<'a, T>(
38 condvar: &Condvar,
39 guard: MutexGuard<'a, T>,
40 timeout: Duration,
41) -> (MutexGuard<'a, T>, WaitTimeoutResult) {
42 match condvar.wait_timeout(guard, timeout) {
43 Ok(result) => result,
44 Err(poisoned) => poisoned.into_inner(),
45 }
46}
47
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
50pub enum LockMode {
51 Shared,
53 Exclusive,
55 IntentShared,
57 IntentExclusive,
59 SharedIntentExclusive,
61}
62
63impl LockMode {
64 pub fn is_compatible(&self, other: &LockMode) -> bool {
66 use LockMode::*;
67 matches!(
68 (self, other),
69 (Shared, Shared)
70 | (Shared, IntentShared)
71 | (IntentShared, Shared)
72 | (IntentShared, IntentShared)
73 | (IntentShared, IntentExclusive)
74 | (IntentExclusive, IntentShared)
75 | (IntentExclusive, IntentExclusive)
76 )
77 }
78
79 pub fn can_upgrade_to(&self, target: &LockMode) -> bool {
81 use LockMode::*;
82 matches!(
83 (self, target),
84 (Shared, Exclusive)
85 | (IntentShared, IntentExclusive)
86 | (IntentShared, SharedIntentExclusive)
87 | (Shared, SharedIntentExclusive)
88 )
89 }
90}
91
92#[derive(Debug, Clone, PartialEq, Eq)]
94pub enum LockResult {
95 Granted,
97 Waiting,
99 Deadlock(Vec<TxnId>),
101 Timeout,
103 Upgraded,
105 AlreadyHeld,
107 TxnNotFound,
109 LockLimitExceeded,
111}
112
113#[derive(Debug, Clone)]
115pub struct LockWaiter {
116 pub txn_id: TxnId,
118 pub mode: LockMode,
120 pub start_time: Instant,
122 pub timeout: Duration,
124}
125
126impl LockWaiter {
127 pub fn new(txn_id: TxnId, mode: LockMode, timeout: Duration) -> Self {
129 Self {
130 txn_id,
131 mode,
132 start_time: Instant::now(),
133 timeout,
134 }
135 }
136
137 pub fn is_timed_out(&self) -> bool {
139 self.start_time.elapsed() > self.timeout
140 }
141}
142
143#[derive(Debug)]
145struct Lock {
146 resource: Vec<u8>,
148 holders: HashMap<TxnId, LockMode>,
150 waiters: VecDeque<LockWaiter>,
152}
153
154impl Lock {
155 fn new(resource: Vec<u8>) -> Self {
156 Self {
157 resource,
158 holders: HashMap::new(),
159 waiters: VecDeque::new(),
160 }
161 }
162
163 fn can_grant(&self, txn_id: TxnId, mode: LockMode) -> bool {
165 if let Some(held_mode) = self.holders.get(&txn_id) {
167 if *held_mode == mode {
168 return true; }
170 if held_mode.can_upgrade_to(&mode) {
172 return self
174 .holders
175 .iter()
176 .all(|(id, m)| *id == txn_id || mode.is_compatible(m));
177 }
178 return false;
179 }
180
181 self.holders.values().all(|m| mode.is_compatible(m))
183 }
184
185 fn grant(&mut self, txn_id: TxnId, mode: LockMode) {
187 self.holders.insert(txn_id, mode);
188 }
189
190 fn release(&mut self, txn_id: TxnId) -> Option<LockMode> {
192 self.holders.remove(&txn_id)
193 }
194
195 fn add_waiter(&mut self, waiter: LockWaiter) {
197 self.waiters.push_back(waiter);
198 }
199
200 fn process_waiters(&mut self) -> Vec<TxnId> {
202 let mut granted = Vec::new();
203
204 self.waiters.retain(|w| !w.is_timed_out());
206
207 let mut i = 0;
209 while i < self.waiters.len() {
210 let waiter = &self.waiters[i];
211 if self.can_grant(waiter.txn_id, waiter.mode) {
212 if let Some(waiter) = self.waiters.remove(i) {
213 self.grant(waiter.txn_id, waiter.mode);
214 granted.push(waiter.txn_id);
215 }
216 } else {
217 i += 1;
218 }
219 }
220
221 granted
222 }
223}
224
225#[derive(Debug, Clone)]
227pub struct LockConfig {
228 pub default_timeout: Duration,
230 pub deadlock_detection: bool,
232 pub detection_interval: Duration,
234 pub max_locks_per_txn: usize,
236}
237
238impl Default for LockConfig {
239 fn default() -> Self {
240 Self {
241 default_timeout: Duration::from_secs(30),
242 deadlock_detection: true,
243 detection_interval: Duration::from_millis(100),
244 max_locks_per_txn: 10000,
245 }
246 }
247}
248
249#[derive(Debug, Clone, Default)]
251pub struct LockStats {
252 pub requests: u64,
254 pub granted: u64,
256 pub waited: u64,
258 pub deadlocks: u64,
260 pub timeouts: u64,
262 pub active_locks: u64,
264 pub waiting: u64,
266}
267
268pub struct LockManager {
270 config: LockConfig,
272 locks: RwLock<HashMap<Vec<u8>, Lock>>,
274 txn_locks: RwLock<HashMap<TxnId, HashSet<Vec<u8>>>>,
276 wait_graph: RwLock<HashMap<TxnId, HashSet<TxnId>>>,
278 waiter_cv: Condvar,
280 waiter_mutex: Mutex<()>,
282 stats: RwLock<LockStats>,
284}
285
286impl LockManager {
287 pub fn new(config: LockConfig) -> Self {
289 Self {
290 config,
291 locks: RwLock::new(HashMap::new()),
292 txn_locks: RwLock::new(HashMap::new()),
293 wait_graph: RwLock::new(HashMap::new()),
294 waiter_cv: Condvar::new(),
295 waiter_mutex: Mutex::new(()),
296 stats: RwLock::new(LockStats::default()),
297 }
298 }
299
300 pub fn with_defaults() -> Self {
302 Self::new(LockConfig::default())
303 }
304
305 pub fn acquire(&self, txn_id: TxnId, resource: &[u8], mode: LockMode) -> LockResult {
307 self.acquire_with_timeout(txn_id, resource, mode, self.config.default_timeout)
308 }
309
310 pub fn acquire_with_timeout(
312 &self,
313 txn_id: TxnId,
314 resource: &[u8],
315 mode: LockMode,
316 timeout: Duration,
317 ) -> LockResult {
318 {
320 let mut stats = write_unpoisoned(&self.stats);
321 stats.requests += 1;
322 }
323
324 let resource_key = resource.to_vec();
325
326 {
328 let txn_locks = read_unpoisoned(&self.txn_locks);
329 if let Some(locks) = txn_locks.get(&txn_id) {
330 if locks.len() >= self.config.max_locks_per_txn && !locks.contains(&resource_key) {
331 return LockResult::LockLimitExceeded;
332 }
333 }
334 }
335
336 {
338 let mut locks = write_unpoisoned(&self.locks);
339 let lock = locks
340 .entry(resource_key.clone())
341 .or_insert_with(|| Lock::new(resource_key.clone()));
342
343 if lock.can_grant(txn_id, mode) {
344 let already_held = lock.holders.contains_key(&txn_id);
345 lock.grant(txn_id, mode);
346
347 let mut txn_locks = write_unpoisoned(&self.txn_locks);
349 txn_locks.entry(txn_id).or_default().insert(resource_key);
350
351 let mut stats = write_unpoisoned(&self.stats);
352 stats.granted += 1;
353 stats.active_locks = locks.values().map(|l| l.holders.len() as u64).sum();
354
355 return if already_held {
356 LockResult::Upgraded
357 } else {
358 LockResult::Granted
359 };
360 }
361
362 let waiting_for: HashSet<TxnId> = lock.holders.keys().copied().collect();
364
365 if self.config.deadlock_detection {
366 let mut wait_graph = Self::build_wait_graph_from_locks(&locks);
367 wait_graph
368 .entry(txn_id)
369 .or_default()
370 .extend(waiting_for.iter().copied());
371
372 if self.detect_deadlock_inner(txn_id, &wait_graph) {
373 let cycle: Vec<TxnId> = waiting_for.iter().copied().collect();
374 let mut stats = write_unpoisoned(&self.stats);
375 stats.deadlocks += 1;
376 return LockResult::Deadlock(cycle);
377 }
378
379 *write_unpoisoned(&self.wait_graph) = wait_graph;
380 }
381
382 if let Some(lock) = locks.get_mut(&resource_key) {
384 lock.add_waiter(LockWaiter::new(txn_id, mode, timeout));
385 }
386
387 let mut stats = write_unpoisoned(&self.stats);
388 stats.waited += 1;
389 stats.waiting += 1;
390 }
391
392 let start = Instant::now();
394 loop {
395 let guard = mutex_unpoisoned(&self.waiter_mutex);
397 let (_guard, _wait_result) =
398 wait_timeout_unpoisoned(&self.waiter_cv, guard, Duration::from_millis(10));
399
400 let holders: Option<HashSet<TxnId>> = {
402 let locks = read_unpoisoned(&self.locks);
403 if let Some(lock) = locks.get(&resource_key) {
404 if lock.holders.contains_key(&txn_id) {
405 if self.config.deadlock_detection {
407 let mut wait_graph = write_unpoisoned(&self.wait_graph);
408 wait_graph.remove(&txn_id);
409 }
410
411 let mut stats = write_unpoisoned(&self.stats);
412 stats.waiting -= 1;
413
414 return LockResult::Granted;
415 }
416
417 Some(lock.holders.keys().copied().collect())
418 } else {
419 None
420 }
421 };
422
423 if self.config.deadlock_detection {
424 let locks = read_unpoisoned(&self.locks);
425 let wait_graph = Self::build_wait_graph_from_locks(&locks);
426 drop(locks);
427
428 if self.detect_deadlock_inner(txn_id, &wait_graph) {
429 let mut stats = write_unpoisoned(&self.stats);
430 stats.deadlocks += 1;
431 stats.waiting -= 1;
432 return LockResult::Deadlock(holders.unwrap_or_default().into_iter().collect());
433 }
434
435 *write_unpoisoned(&self.wait_graph) = wait_graph;
436 }
437
438 if start.elapsed() > timeout {
440 {
442 let mut locks = write_unpoisoned(&self.locks);
443 if let Some(lock) = locks.get_mut(&resource_key) {
444 lock.waiters.retain(|w| w.txn_id != txn_id);
445 }
446 }
447
448 if self.config.deadlock_detection {
450 let mut wait_graph = write_unpoisoned(&self.wait_graph);
451 wait_graph.remove(&txn_id);
452 }
453
454 let mut stats = write_unpoisoned(&self.stats);
455 stats.timeouts += 1;
456 stats.waiting -= 1;
457
458 return LockResult::Timeout;
459 }
460 }
461 }
462
463 pub fn release(&self, txn_id: TxnId, resource: &[u8]) -> bool {
465 let resource_key = resource.to_vec();
466
467 let granted = {
468 let mut locks = write_unpoisoned(&self.locks);
469
470 if let Some(lock) = locks.get_mut(&resource_key) {
471 if lock.release(txn_id).is_some() {
472 let mut txn_locks = write_unpoisoned(&self.txn_locks);
474 if let Some(resources) = txn_locks.get_mut(&txn_id) {
475 resources.remove(&resource_key);
476 }
477
478 let granted = lock.process_waiters();
480
481 if self.config.deadlock_detection && !granted.is_empty() {
483 let mut wait_graph = write_unpoisoned(&self.wait_graph);
484 for txn in &granted {
485 wait_graph.remove(txn);
486 }
487 }
488
489 if lock.holders.is_empty() && lock.waiters.is_empty() {
491 locks.remove(&resource_key);
492 }
493
494 self.waiter_cv.notify_all();
496
497 return true;
498 }
499 }
500
501 false
502 };
503
504 granted
505 }
506
507 pub fn release_all(&self, txn_id: TxnId) -> usize {
509 let resources: Vec<Vec<u8>> = {
510 let txn_locks = read_unpoisoned(&self.txn_locks);
511 txn_locks
512 .get(&txn_id)
513 .map(|r| r.iter().cloned().collect())
514 .unwrap_or_default()
515 };
516
517 let count = resources.len();
518
519 for resource in resources {
520 self.release(txn_id, &resource);
521 }
522
523 {
525 let mut txn_locks = write_unpoisoned(&self.txn_locks);
526 txn_locks.remove(&txn_id);
527 }
528
529 if self.config.deadlock_detection {
531 let mut wait_graph = write_unpoisoned(&self.wait_graph);
532 wait_graph.remove(&txn_id);
533 }
534
535 count
536 }
537
538 pub fn holds_lock(&self, txn_id: TxnId, resource: &[u8]) -> Option<LockMode> {
540 let locks = read_unpoisoned(&self.locks);
541 locks
542 .get(resource)
543 .and_then(|lock| lock.holders.get(&txn_id).copied())
544 }
545
546 pub fn get_locks(&self, txn_id: TxnId) -> Vec<(Vec<u8>, LockMode)> {
548 let txn_locks = read_unpoisoned(&self.txn_locks);
549 let locks = read_unpoisoned(&self.locks);
550
551 txn_locks
552 .get(&txn_id)
553 .map(|resources| {
554 resources
555 .iter()
556 .filter_map(|r| {
557 locks
558 .get(r)
559 .and_then(|l| l.holders.get(&txn_id).map(|m| (r.clone(), *m)))
560 })
561 .collect()
562 })
563 .unwrap_or_default()
564 }
565
566 fn detect_deadlock_inner(
568 &self,
569 start: TxnId,
570 wait_graph: &HashMap<TxnId, HashSet<TxnId>>,
571 ) -> bool {
572 let mut visited = HashSet::new();
573 let mut stack = HashSet::new();
574
575 Self::dfs_cycle(start, &mut visited, &mut stack, wait_graph)
576 }
577
578 fn build_wait_graph_from_locks(
579 locks: &HashMap<Vec<u8>, Lock>,
580 ) -> HashMap<TxnId, HashSet<TxnId>> {
581 let mut graph: HashMap<TxnId, HashSet<TxnId>> = HashMap::new();
582
583 for lock in locks.values() {
584 if lock.holders.is_empty() {
585 continue;
586 }
587 let holders: HashSet<TxnId> = lock.holders.keys().copied().collect();
588 for waiter in &lock.waiters {
589 graph
590 .entry(waiter.txn_id)
591 .or_default()
592 .extend(holders.iter().copied());
593 }
594 }
595
596 graph
597 }
598
599 fn dfs_cycle(
600 node: TxnId,
601 visited: &mut HashSet<TxnId>,
602 stack: &mut HashSet<TxnId>,
603 wait_graph: &HashMap<TxnId, HashSet<TxnId>>,
604 ) -> bool {
605 if stack.contains(&node) {
606 return true; }
608 if visited.contains(&node) {
609 return false; }
611
612 visited.insert(node);
613 stack.insert(node);
614
615 if let Some(waiting_for) = wait_graph.get(&node) {
616 for &next in waiting_for {
617 if Self::dfs_cycle(next, visited, stack, wait_graph) {
618 return true;
619 }
620 }
621 }
622
623 stack.remove(&node);
624 false
625 }
626
627 pub fn stats(&self) -> LockStats {
629 read_unpoisoned(&self.stats).clone()
630 }
631
632 pub fn config(&self) -> &LockConfig {
634 &self.config
635 }
636}
637
638impl Default for LockManager {
639 fn default() -> Self {
640 Self::with_defaults()
641 }
642}
643
644#[cfg(test)]
645mod tests {
646 use super::*;
647
648 #[test]
649 fn test_lock_mode_compatibility() {
650 assert!(LockMode::Shared.is_compatible(&LockMode::Shared));
651 assert!(!LockMode::Shared.is_compatible(&LockMode::Exclusive));
652 assert!(!LockMode::Exclusive.is_compatible(&LockMode::Exclusive));
653 assert!(LockMode::IntentShared.is_compatible(&LockMode::IntentShared));
654 }
655
656 #[test]
657 fn test_lock_acquire_release() {
658 let lm = LockManager::with_defaults();
659
660 let result = lm.acquire(1, b"resource1", LockMode::Shared);
662 assert_eq!(result, LockResult::Granted);
663
664 let result = lm.acquire(2, b"resource1", LockMode::Shared);
666 assert_eq!(result, LockResult::Granted);
667
668 assert!(lm.release(1, b"resource1"));
670 assert!(lm.release(2, b"resource1"));
671 }
672
673 #[test]
674 fn test_exclusive_lock() {
675 let lm = LockManager::with_defaults();
676
677 let result = lm.acquire(1, b"resource1", LockMode::Exclusive);
679 assert_eq!(result, LockResult::Granted);
680
681 assert_eq!(lm.holds_lock(1, b"resource1"), Some(LockMode::Exclusive));
683
684 lm.release_all(1);
686 assert_eq!(lm.holds_lock(1, b"resource1"), None);
687 }
688
689 #[test]
690 fn test_release_all() {
691 let lm = LockManager::with_defaults();
692
693 lm.acquire(1, b"r1", LockMode::Shared);
695 lm.acquire(1, b"r2", LockMode::Exclusive);
696 lm.acquire(1, b"r3", LockMode::Shared);
697
698 let count = lm.release_all(1);
700 assert_eq!(count, 3);
701 }
702
703 #[test]
704 fn test_lock_limit_exceeded() {
705 let config = LockConfig {
706 max_locks_per_txn: 1,
707 ..LockConfig::default()
708 };
709 let lm = LockManager::new(config);
710
711 let result = lm.acquire(1, b"r1", LockMode::Shared);
712 assert_eq!(result, LockResult::Granted);
713
714 let result = lm.acquire(1, b"r2", LockMode::Shared);
715 assert_eq!(result, LockResult::LockLimitExceeded);
716 }
717
718 #[test]
719 fn test_lock_limit_allows_upgrade() {
720 let config = LockConfig {
721 max_locks_per_txn: 1,
722 ..LockConfig::default()
723 };
724 let lm = LockManager::new(config);
725
726 let result = lm.acquire(1, b"r1", LockMode::Shared);
727 assert_eq!(result, LockResult::Granted);
728
729 let result = lm.acquire(1, b"r1", LockMode::Exclusive);
730 assert_eq!(result, LockResult::Upgraded);
731 }
732
733 #[test]
734 fn test_get_locks() {
735 let lm = LockManager::with_defaults();
736
737 lm.acquire(1, b"r1", LockMode::Shared);
738 lm.acquire(1, b"r2", LockMode::Exclusive);
739
740 let locks = lm.get_locks(1);
741 assert_eq!(locks.len(), 2);
742 }
743
744 #[test]
745 fn test_waiter_timeout() {
746 let waiter = LockWaiter::new(1, LockMode::Shared, Duration::from_millis(1));
747 std::thread::sleep(Duration::from_millis(5));
748 assert!(waiter.is_timed_out());
749 }
750}