Table of Contents

Interface ILockHandler

Namespace
Quartz.Impl.AdoJobStore
Assembly
Quartz.dll

An interface for providing thread/resource locking in order to protect resources from being altered by multiple threads at the same time.

public interface ILockHandler

Properties

RequiresConnection

Whether this lock handler requires a database connection for its lock management operations.

bool RequiresConnection { get; }

Property Value

bool
See Also

Methods

AcquireLock(Guid, ConnectionAndTransactionHolder?, SchedulerLock, CancellationToken)

Grants a lock on the identified resource to the calling context, waiting until it is available.

ValueTask<bool> AcquireLock(Guid requestorId, ConnectionAndTransactionHolder? conn, SchedulerLock lockKind, CancellationToken cancellationToken = default)

Parameters

requestorId Guid

Identifies the calling context, so that a re-entrant acquire can be told from a competing one.

conn ConnectionAndTransactionHolder

The unit of work to take the lock on. It is null when the store has not opened a connection yet, which it delays for a handler whose RequiresConnection is false.

lockKind SchedulerLock

Which of the two locks to take.

cancellationToken CancellationToken

The cancellation instruction.

Returns

ValueTask<bool>

true if this call took the lock and its caller must release it; false if requestorId already held it and must not.

Remarks

The answer is a release obligation rather than a report of success. true says this call took the lock and its caller is the one that has to give it back; false says requestorId already held it — a re-entrant acquire — so the outer caller's single release is what frees it, and releasing here would drop a lock that operation is still relying on. A handler that instead counts its holds may answer true to a re-entrant acquire, because there the inner release is the one that decrements; either way the caller releases exactly when it was told true.

Re-entry is the only thing false may mean. A handler that could not take the lock says so by throwing: LockException when the lock was refused, and OperationCanceledException when cancellationToken fired. Answering false on cancellation is not a harmless approximation — the job store reads it as "already held, do not release" and goes on to run the guarded operation with no lock at all, which is the whole point of taking one.

The store's own ordering must not be relied on to cover for a handler that gets this wrong. A handler answering false to RequiresConnection is followed immediately by a connection open on the same token, so a mistake here is masked today by that open throwing first. That is an accident of statement order rather than a guarantee, and it does not hold wherever the store reaches the lock in a different sequence.

A handler that gives up leaves nothing behind: an abandoned wait must not consume a handover meant for the next waiter, and a partially taken lock is released before the exception escapes, so that the next caller can still be served.

Exceptions

OperationCanceledException

cancellationToken fired before the lock was taken.

LockException

The lock could not be obtained.

Initialize(LockHandlerContext)

Called once by the job store before the lock handler is used, telling the handler which scheduler it locks for. The default implementation does nothing, which suits a handler that does not key its locks by scheduler identity.

void Initialize(LockHandlerContext context)

Parameters

context LockHandlerContext

ReleaseLock(Guid, SchedulerLock, CancellationToken)

Release the lock on the identified resource if it is held by the calling thread.

ValueTask ReleaseLock(Guid requestorId, SchedulerLock lockKind, CancellationToken cancellationToken = default)

Parameters

requestorId Guid
lockKind SchedulerLock
cancellationToken CancellationToken

Returns

ValueTask

Shutdown(CancellationToken)

Called once by the job store, after the store has released its own resources, telling the handler to release whatever it opened. The default implementation does nothing, which suits a handler that holds nothing of its own.

ValueTask Shutdown(CancellationToken cancellationToken = default)

Parameters

cancellationToken CancellationToken

The cancellation instruction.

Returns

ValueTask

Remarks

Named for Shutdown(CancellationToken) rather than for IAsyncDisposable, because it is the store's shutdown that reaches it and a handler is never used again afterwards. It is a default interface member, so a handler written against 4.0's first shape need not implement it — one that holds nothing has nothing to say here.

It runs after the lock this handler guards has stopped being asked for, so an implementation may assume no acquire is in flight. Throwing is not fatal: the store logs it and finishes shutting down, because a scheduler that cannot complete its shutdown is worse than a connection that outlives it.