lock_db/lib.rs
1//! # lock-db
2//!
3//! Lock manager and deadlock detection for Rust databases — row/range locks,
4//! multiple granularities, and wait-for cycle detection.
5//!
6//! A lock manager is the component that lets many transactions touch shared
7//! data at once without corrupting it. Each transaction asks for a lock on a
8//! resource in a [`LockMode`]; the manager grants it only when the mode is
9//! compatible with what every other transaction already holds. That single
10//! rule — the compatibility matrix — is what keeps concurrent reads and writes
11//! correct.
12//!
13//! ## What is in this release
14//!
15//! This is the v0.2.0 milestone. It provides the lock-table core:
16//!
17//! - [`LockMode`] — shared and exclusive modes and their compatibility matrix.
18//! - [`LockManager`] — a sharded, non-blocking lock table with acquire,
19//! release, bulk release, and shared-to-exclusive upgrade.
20//! - [`TxnId`] and [`ResourceId`] — opaque identifiers the caller assigns.
21//! - [`LockError`] — the small, exhaustive set of ways an operation can fail.
22//!
23//! Acquisition is non-blocking: a request that cannot be granted returns
24//! [`LockError::Conflict`] instead of waiting. Blocking acquisition with wait
25//! queues, hierarchical and range locks, and wait-for deadlock detection land
26//! across later 0.x releases (see `dev/ROADMAP.md`).
27//!
28//! ## Example
29//!
30//! ```
31//! use lock_db::prelude::*;
32//!
33//! let lm = LockManager::new();
34//! let row = ResourceId::new(1);
35//! let (writer, reader) = (TxnId::new(1), TxnId::new(2));
36//!
37//! // The writer takes the row exclusively.
38//! lm.try_acquire(writer, row, LockMode::Exclusive).unwrap();
39//!
40//! // A concurrent reader is refused while the write lock is held.
41//! assert_eq!(lm.try_acquire(reader, row, LockMode::Shared), Err(LockError::Conflict));
42//!
43//! // Once the writer commits and releases, the reader gets in.
44//! lm.release(writer, row).unwrap();
45//! lm.try_acquire(reader, row, LockMode::Shared).unwrap();
46//! ```
47
48#![cfg_attr(not(feature = "std"), no_std)]
49#![cfg_attr(docsrs, feature(doc_cfg))]
50#![deny(missing_docs)]
51#![deny(unused_must_use)]
52#![deny(clippy::unwrap_used)]
53#![deny(clippy::expect_used)]
54#![deny(clippy::todo)]
55#![deny(clippy::unimplemented)]
56#![deny(clippy::dbg_macro)]
57#![deny(clippy::print_stdout)]
58#![deny(clippy::print_stderr)]
59#![forbid(unsafe_code)]
60
61mod error;
62mod id;
63mod mode;
64
65#[cfg(feature = "std")]
66mod manager;
67
68pub use crate::error::LockError;
69pub use crate::id::{ResourceId, TxnId};
70pub use crate::mode::LockMode;
71
72#[cfg(feature = "std")]
73pub use crate::manager::LockManager;
74
75/// The crate's common imports.
76///
77/// Glob-import this to bring the lock manager, the mode enum, the identifiers,
78/// and the error type into scope in one line:
79///
80/// ```
81/// use lock_db::prelude::*;
82///
83/// let lm = LockManager::new();
84/// lm.try_acquire(TxnId::new(1), ResourceId::new(1), LockMode::Shared).unwrap();
85/// ```
86pub mod prelude {
87 pub use crate::error::LockError;
88 pub use crate::id::{ResourceId, TxnId};
89 pub use crate::mode::LockMode;
90
91 #[cfg(feature = "std")]
92 pub use crate::manager::LockManager;
93}