Skip to main content

LockManager

Struct LockManager 

Source
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:

  1. Registers the calling thread as a waiter (inside the shard mutex) and attaches a per-waiter Arc<(Mutex<bool>, Condvar)> notify pair.
  2. Checks for deadlocks using the DeadlockDetector before sleeping.
  3. Releases the shard mutex and waits on the condvar for up to lock_timeout_ms milliseconds.
  4. On wakeup re-acquires the shard mutex and checks ownership.
  5. On timeout removes itself from the waiter list and returns TxnError::LockTimeout.

This mirrors the flow in LockManager.lock() / waitForLock().

Implementations§

Source§

impl LockManager

Source

pub fn new() -> Self

Creates a new LockManager with N_LOCK_TABLES shards and the default lock timeout of 500 ms (matching default).

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub fn set_lock_timeout(&self, timeout_ms: u64)

Updates the default lock timeout.

Thread-safe; takes effect for subsequent lock() calls.

Source

pub fn get_lock_timeout_ms(&self) -> u64

Returns the current default lock timeout in milliseconds.

Source

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 locker
  • lock_type - The type of lock requested
  • non_blocking - If true, return LockNotAvailable instead of waiting
  • jump_ahead_of_waiters - If true, skip ahead of existing waiters
  • lock_timeout_ms - How long to wait; 0 = wait forever
§Returns

The LockGrantType on success:

  • New / Promotion / Existing — lock held immediately
  • NoneNeededlock_type was None
§Errors
  • TxnError::RangeRestart if lock_type is Restart
  • TxnError::LockNotAvailable if non_blocking and lock unavailable
  • TxnError::LockTimeout if the timeout expired while waiting
  • TxnError::Deadlock if a wait-for cycle is detected before waiting
Source

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.

Source

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).

Source

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.

Source

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_slot allocates a fresh locker id per migration attempt (next_cleaner_locker_id), takes a non-blocking read lock, and releases. If release() 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_drain and 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 LockManager across 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.

Source

pub fn demote(&self, lsn: u64, locker_id: i64) -> Result<(), TxnError>

Downgrades a write lock to a read lock.

Source

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.

Source

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.

Source

pub fn get_owned_lock_type(&self, lsn: u64, locker_id: i64) -> Option<LockType>

Returns the lock type owned by the locker, or None.

Source

pub fn get_lock_info(&self, lsn: u64) -> (usize, usize)

Returns the owner count and waiter count for a given LSN.

Source

pub fn get_stats(&self) -> LockStats

Returns current lock statistics.

Source

pub fn n_total_locks(&self) -> usize

Returns the number of lock entries across all tables.

Source

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).

Source

pub fn unregister_locker_sharing(&self, locker_id: i64)

Removes a locker from the sharing registry.

Called by ThreadLocker::drop() and HandleLocker::drop().

Source

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).

Source

pub fn unregister_non_preemptable(&self, locker_id: i64)

Clears the non-preemptable mark for locker_id.

Source

pub fn same_share_group(&self, a: i64, b: i64) -> bool

Returns true if a and b are in the same lock-sharing group.

Used by ThreadLocker::shares_locks_with() and HandleLocker::shares_locks_with().

Source

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.

Source

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.

Source

pub fn n_lock_tables(&self) -> usize

Returns the lock table index for a given LSN.

Returns the configured number of lock-table shards.

Trait Implementations§

Source§

impl Default for LockManager

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.