pub struct LockManager { /* private fields */ }Expand description
The LockManager manages all locks in the system.
Locks are sharded across N_LOCK_TABLES tables, each protected by its own mutex. This allows concurrent lock operations on different LSNs.
§Architecture
- Each lock is identified by an LSN (packed u64)
- Locks are hashed to one of N lock tables
- Each table has its own mutex for fine-grained locking
- Lock objects start as Thin locks and mutate to Full locks when needed
§Blocking / waiting
When lock() cannot grant immediately it:
- Registers the calling thread as a waiter (inside the shard mutex) and
attaches a per-waiter
Arc<(Mutex<bool>, Condvar)>notify pair. - Checks for deadlocks using the
DeadlockDetectorbefore sleeping. - Releases the shard mutex and waits on the condvar for up to
lock_timeout_msmilliseconds. - On wakeup re-acquires the shard mutex and checks ownership.
- On timeout removes itself from the waiter list and returns
TxnError::LockTimeout.
This mirrors the flow in LockManager.lock() / waitForLock().
Implementations§
Source§impl LockManager
impl LockManager
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new LockManager with N_LOCK_TABLES shards and the default lock timeout of 500 ms (matching default).
Sourcepub fn with_lock_timeout(timeout_ms: u64) -> Self
pub fn with_lock_timeout(timeout_ms: u64) -> Self
Creates a new LockManager with a specific default lock timeout.
timeout_ms == 0 means wait forever (setLockTimeout(0, MILLISECONDS)).
Call this from EnvironmentImpl after reading EnvironmentConfig.lock_timeout_ms.
Sourcepub fn with_config(timeout_ms: u64, n_lock_tables: usize) -> Self
pub fn with_config(timeout_ms: u64, n_lock_tables: usize) -> Self
Creates a new LockManager with an explicit shard count (JE
LOCK_N_LOCK_TABLES / noxu.lock.nLockTables) and lock timeout.
Production (EnvironmentImpl) passes the configured value; 0 or
values are clamped to at least 1 shard.
Sourcepub fn register_locker_label(&self, locker_id: i64, label: &'static str)
pub fn register_locker_label(&self, locker_id: i64, label: &'static str)
Registers a diagnostic label for locker_id.
Stored in Self::locker_labels and looked up by
Self::format_locker when building deadlock / lock-timeout error
messages. Typical labels are "txn" (explicit transaction),
"auto-txn" (synthetic auto-commit transaction created by
TxnManager::begin_auto_txn), and "cleaner" (cleaner-locker IDs).
Re-registering the same locker_id overwrites the previous label.
Lockers without a registered label are reported as "locker:<id>",
which preserves backward compatibility with callers that never
registered.
Sourcepub fn unregister_locker_label(&self, locker_id: i64)
pub fn unregister_locker_label(&self, locker_id: i64)
Removes the diagnostic label for locker_id.
Called when a transaction (explicit or synthetic auto-commit) terminates so the registry does not grow without bound. Idempotent — removing an unknown id is a no-op.
Sourcepub fn format_locker(&self, locker_id: i64) -> String
pub fn format_locker(&self, locker_id: i64) -> String
Returns a typed identifier string for locker_id.
Looks up the label registered via Self::register_locker_label and
returns "<label>:<id>"; if no label is registered, returns
"locker:<id>".
Used to format the requester and owner fields of
TxnError::LockTimeout and the message body of
TxnError::Deadlock so a mixed auto-commit/explicit-txn deadlock
reports e.g. "auto-txn:42" and "txn:17" rather than two opaque
integers — closing the second F12 residual.
Sourcepub fn format_lockers(&self, locker_ids: &[i64]) -> String
pub fn format_lockers(&self, locker_ids: &[i64]) -> String
Returns a comma-separated typed identifier list for locker_ids.
Convenience wrapper used in deadlock error messages.
Sourcepub fn set_lock_timeout(&self, timeout_ms: u64)
pub fn set_lock_timeout(&self, timeout_ms: u64)
Updates the default lock timeout.
Thread-safe; takes effect for subsequent lock() calls.
Sourcepub fn get_lock_timeout_ms(&self) -> u64
pub fn get_lock_timeout_ms(&self) -> u64
Returns the current default lock timeout in milliseconds.
Sourcepub fn lock(
&self,
lsn: u64,
locker_id: i64,
lock_type: LockType,
non_blocking: bool,
jump_ahead_of_waiters: bool,
) -> Result<LockGrantType, TxnError>
pub fn lock( &self, lsn: u64, locker_id: i64, lock_type: LockType, non_blocking: bool, jump_ahead_of_waiters: bool, ) -> Result<LockGrantType, TxnError>
Acquires a lock on the given LSN for the given locker, blocking the calling thread if necessary.
§Arguments
lsn- The LSN to lock (packed u64)locker_id- The ID of the requesting lockerlock_type- The type of lock requestednon_blocking- If true, returnLockNotAvailableinstead of waitingjump_ahead_of_waiters- If true, skip ahead of existing waiterslock_timeout_ms- How long to wait; 0 = wait forever
§Returns
The LockGrantType on success:
New/Promotion/Existing— lock held immediatelyNoneNeeded—lock_typewasNone
§Errors
TxnError::RangeRestartiflock_typeisRestartTxnError::LockNotAvailableifnon_blockingand lock unavailableTxnError::LockTimeoutif the timeout expired while waitingTxnError::Deadlockif a wait-for cycle is detected before waiting
Sourcepub fn lock_with_timeout(
&self,
lsn: u64,
locker_id: i64,
lock_type: LockType,
non_blocking: bool,
jump_ahead_of_waiters: bool,
timeout_ms: u64,
) -> Result<LockGrantType, TxnError>
pub fn lock_with_timeout( &self, lsn: u64, locker_id: i64, lock_type: LockType, non_blocking: bool, jump_ahead_of_waiters: bool, timeout_ms: u64, ) -> Result<LockGrantType, TxnError>
Like lock() but the caller supplies the timeout in milliseconds.
timeout_ms == 0 means wait forever.
Sourcepub fn lock_importunate_with_timeout(
&self,
lsn: u64,
locker_id: i64,
lock_type: LockType,
non_blocking: bool,
timeout_ms: u64,
) -> Result<LockGrantType, TxnError>
pub fn lock_importunate_with_timeout( &self, lsn: u64, locker_id: i64, lock_type: LockType, non_blocking: bool, timeout_ms: u64, ) -> Result<LockGrantType, TxnError>
Importunate lock acquisition for HA replay (ReplayTxn).
JE LockManager.waitForLock: an importunate locker that would block
steals the lock from preemptable owners instead of waiting
(if (isImportunate) { result = stealLock(...); if (result.success) break; continue; }, LockManager.java:552-557), and deadlock
detection is skipped for it (LockManager.java:472).
Strategy: attempt non-blocking; if granted, done. Otherwise steal
from preemptable owners and re-attempt (stealLockInternal,
LockManager.java:1599). If the steal grants the lock, done. If a
remaining owner is non-preemptable the steal fails, so fall back to a
normal blocking wait (mirroring JE’s continue — wait for the
non-preemptable holder to release, then retry).
Sourcepub fn release(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>
pub fn release(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>
Releases a lock on the given LSN for the given locker.
Promotes compatible waiters to owners, signals their condvars so they wake up, and removes the lock entry when it becomes empty.
Sourcepub fn release_all_for_locker(&self, locker_id: i64) -> usize
pub fn release_all_for_locker(&self, locker_id: i64) -> usize
Releases every lock currently held by locker_id, across all
shards. Returns the number of (lsn, lock) entries the locker
actually owned and released.
Equivalent to a manual for lsn in lockers_locks(id): release(lsn, id),
but does not require the caller to track which LSNs the locker
has touched. The cleaner uses this in three situations:
- Reaping abandoned cleaner-locker IDs.
migrate_ln_slotallocates a fresh locker id per migration attempt (next_cleaner_locker_id), takes a non-blocking read lock, and releases. Ifrelease()fails for any reason the entry would otherwise leak, since the locker id is never reused. The cleaner can call this method when its run terminates to sweep up anything its short-lived locker ids left behind. - Catastrophic per-locker abort. When a deadlock-detector
victim is too far along to drain its own per-txn write_locks
map (e.g. it is in the middle of
commit_inner_after_read_drainand the panic handler needs to clean up), this method guarantees the lock-manager view drops the locker even if the per-txn view is corrupt. - Test cleanup. Many integration tests hold a
LockManageracross multiple txns and need a quick “drop everything for locker N” without re-creating the manager.
Errors from individual Lock::release calls are logged and
the sweep continues; the count returned is the number of
release attempts (each removing the locker from one Lock),
not the number that succeeded — losing one lock release leaks
one entry, but losing the whole sweep would defeat the
purpose.
Sourcepub fn demote(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>
pub fn demote(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>
Downgrades a write lock to a read lock.
Sourcepub fn steal_lock(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>
pub fn steal_lock(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>
Steals a lock for the given locker.
Used by the HA replayer to forcibly acquire locks, removing all other preemptable owners.
Sourcepub fn is_owned_write_lock(&self, lsn: u64, locker_id: i64) -> bool
pub fn is_owned_write_lock(&self, lsn: u64, locker_id: i64) -> bool
Returns true if the given locker owns a write lock on the LSN.
Sourcepub fn get_owned_lock_type(&self, lsn: u64, locker_id: i64) -> Option<LockType>
pub fn get_owned_lock_type(&self, lsn: u64, locker_id: i64) -> Option<LockType>
Returns the lock type owned by the locker, or None.
Sourcepub fn get_lock_info(&self, lsn: u64) -> (usize, usize)
pub fn get_lock_info(&self, lsn: u64) -> (usize, usize)
Returns the owner count and waiter count for a given LSN.
Sourcepub fn n_total_locks(&self) -> usize
pub fn n_total_locks(&self) -> usize
Returns the number of lock entries across all tables.
Sourcepub fn register_locker_sharing(&self, locker_id: i64, group_id: i64)
pub fn register_locker_sharing(&self, locker_id: i64, group_id: i64)
Registers a locker in the sharing registry with the given group ID.
All lockers sharing the same group_id bypass conflict detection with
each other (Locker.sharesLocksWith(other)).
Called by ThreadLocker::new() (group = thread_id) and by
HandleLocker::with_buddy() (group = buddy_locker_id).
Sourcepub fn unregister_locker_sharing(&self, locker_id: i64)
pub fn unregister_locker_sharing(&self, locker_id: i64)
Removes a locker from the sharing registry.
Called by ThreadLocker::drop() and HandleLocker::drop().
Sourcepub fn register_non_preemptable(&self, locker_id: i64)
pub fn register_non_preemptable(&self, locker_id: i64)
Marks locker_id as non-preemptable (its locks cannot be stolen).
Called for importunate lockers (HA ReplayTxn). JE tracks this per
Locker via getPreemptable(); the lock manager mirrors it so the
importunate steal in the wait loop can honor a non-preemptable owner
(LockManager.java:556).
Sourcepub fn unregister_non_preemptable(&self, locker_id: i64)
pub fn unregister_non_preemptable(&self, locker_id: i64)
Clears the non-preemptable mark for locker_id.
Returns true if a and b are in the same lock-sharing group.
Used by ThreadLocker::shares_locks_with() and
HandleLocker::shares_locks_with().
Sourcepub fn lock_with_sharing(
&self,
lsn: u64,
locker_id: i64,
lock_type: LockType,
non_blocking: bool,
jump_ahead_of_waiters: bool,
) -> Result<LockGrantType, TxnError>
pub fn lock_with_sharing( &self, lsn: u64, locker_id: i64, lock_type: LockType, non_blocking: bool, jump_ahead_of_waiters: bool, ) -> Result<LockGrantType, TxnError>
Deprecated alias for lock_with_timeout().
TXN-F2 fix: the plain lock() / lock_with_timeout() path now
consults the sharing registry on every acquisition (JE
LockImpl.tryLock checks sharesLocksWith, LockImpl.java:647-648),
so a separate sharing entry point is redundant. Kept as a thin
forwarder so existing callers keep compiling; new code should call
lock / lock_with_timeout directly.
Sourcepub fn lock_with_sharing_and_timeout(
&self,
lsn: u64,
locker_id: i64,
lock_type: LockType,
non_blocking: bool,
jump_ahead_of_waiters: bool,
timeout_ms: u64,
) -> Result<LockGrantType, TxnError>
pub fn lock_with_sharing_and_timeout( &self, lsn: u64, locker_id: i64, lock_type: LockType, non_blocking: bool, jump_ahead_of_waiters: bool, timeout_ms: u64, ) -> Result<LockGrantType, TxnError>
Deprecated alias for lock_with_timeout(); see lock_with_sharing.
Sourcepub fn n_lock_tables(&self) -> usize
pub fn n_lock_tables(&self) -> usize
Returns the lock table index for a given LSN.
Returns the configured number of lock-table shards.