public async ValueTask <IDistributedSynchronizationHandle?> TryAcquireAsync <TLockCookie>( TimeoutValue timeout, IDbSynchronizationStrategy <TLockCookie> strategy, CancellationToken cancellationToken, IDistributedSynchronizationHandle?contextHandle) where TLockCookie : class { IDistributedSynchronizationHandle?result = null; IAsyncDisposable?connectionResource = null; try { DatabaseConnection connection; if (contextHandle != null) { connection = GetContextHandleConnection <TLockCookie>(contextHandle); } else { connectionResource = connection = this._connectionFactory(); if (connection.IsExernallyOwned) { if (!connection.CanExecuteQueries) { throw new InvalidOperationException("The connection and/or transaction are disposed or closed"); } } else { await connection.OpenAsync(cancellationToken).ConfigureAwait(false); if (this._transactionScopedIfPossible) // for an internally-owned connection, we must create the transaction { await connection.BeginTransactionAsync().ConfigureAwait(false); } } } var lockCookie = await strategy.TryAcquireAsync(connection, this._name, timeout, cancellationToken).ConfigureAwait(false); if (lockCookie != null) { result = new Handle <TLockCookie>(connection, strategy, this._name, lockCookie, transactionScoped: this._transactionScopedIfPossible && connection.HasTransaction, connectionResource); if (!this._keepaliveCadence.IsInfinite) { connection.SetKeepaliveCadence(this._keepaliveCadence); } } } finally { // if we fail to acquire or throw, make sure to clean up the connection if (result == null && connectionResource != null) { await connectionResource.DisposeAsync().ConfigureAwait(false); } } return(result); }
public async ValueTask <Result> TryAcquireAsync <TLockCookie>( string name, TimeoutValue timeout, IDbSynchronizationStrategy <TLockCookie> strategy, TimeoutValue keepaliveCadence, CancellationToken cancellationToken, bool opportunistic) where TLockCookie : class { using var mutextHandle = await this._mutex.TryAcquireAsync(opportunistic?TimeSpan.Zero : Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(false); if (mutextHandle == null) { // mutex wasn't free, so just give up Invariant.Require(opportunistic); // The current lock is busy so we allow retry but on a different lock instance. We can't safely dispose // since we never acquired the mutex so we can't check _heldLocks return(new Result(MultiplexedConnectionLockRetry.Retry, canSafelyDispose: false)); } try { if (this._heldLocksToKeepaliveCadences.ContainsKey(name)) { // we won't try to hold the same lock twice on one connection. At some point, we could // support this case in-memory using a counter for each multiply-held lock name and being careful // with modes return(this.GetFailureResultNoLock(isAlreadyHeld: true, opportunistic, timeout)); } if (!this._connection.CanExecuteQueries) { await this._connection.OpenAsync(cancellationToken).ConfigureAwait(false); } var lockCookie = await strategy.TryAcquireAsync(this._connection, name, opportunistic?TimeSpan.Zero : timeout, cancellationToken).ConfigureAwait(false); if (lockCookie != null) { var handle = new ManagedFinalizationDistributedLockHandle(new Handle <TLockCookie>(this, strategy, name, lockCookie)); this._heldLocksToKeepaliveCadences.Add(name, keepaliveCadence); if (!keepaliveCadence.IsInfinite) { this.SetKeepaliveCadenceNoLock(); } return(new Result(handle)); } // we failed to acquire the lock, so we should retry if we were being opportunistic and artificially // shortened the timeout return(this.GetFailureResultNoLock(isAlreadyHeld: false, opportunistic, timeout)); } finally { await this.CloseConnectionIfNeededNoLockAsync().ConfigureAwait(false); } }