public async Task <IDisposable> TryAcquireAsync(int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, IDisposable contextHandle = null) { if (contextHandle != null) { return(await this.CreateContextLock(contextHandle).TryAcquireAsync(timeoutMillis, mode, cancellationToken, contextHandle: null).ConfigureAwait(false)); } IDisposable result = null; var connection = new SqlConnection(this.connectionString); SqlTransaction transaction = null; try { await connection.OpenAsync(cancellationToken).ConfigureAwait(false); // when creating a transaction, the isolation level doesn't matter, since we're using sp_getapplock transaction = connection.BeginTransaction(); if (await SqlApplicationLock.ExecuteAcquireCommandAsync(transaction, this.lockName, timeoutMillis, mode, cancellationToken).ConfigureAwait(false)) { result = new LockScope(transaction); } } finally { // if we fail to acquire or throw, make sure to clean up if (result == null) { transaction?.Dispose(); connection.Dispose(); } } return(result); }
public async Task <IDisposable> TryAcquireAsync(int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, IDisposable contextHandle) { if (contextHandle != null) { return(await this.CreateContextLock(contextHandle).TryAcquireAsync(timeoutMillis, mode, cancellationToken, contextHandle: null).ConfigureAwait(false)); } IDisposable result = null; var connection = new SqlConnection(this.connectionString); try { await connection.OpenAsync(cancellationToken).ConfigureAwait(false); if (await SqlApplicationLock.ExecuteAcquireCommandAsync(connection, this.lockName, timeoutMillis, mode, cancellationToken).ConfigureAwait(false)) { result = new LockScope(connection, this.lockName); } } finally { // if we fail to acquire or throw, make sure to clean up the connection if (result == null) { connection.Dispose(); } } return(result); }
public IDisposable TryAcquire(int timeoutMillis, SqlApplicationLock.Mode mode, IDisposable contextHandle) { if (contextHandle != null) { return(this.CreateContextLock(contextHandle).TryAcquire(timeoutMillis, mode, contextHandle: null)); } IDisposable result = null; var connection = new SqlConnection(this.connectionString); SqlTransaction transaction = null; try { connection.Open(); // when creating a transaction, the isolation level doesn't matter, since we're using sp_getapplock transaction = connection.BeginTransaction(); if (SqlApplicationLock.ExecuteAcquireCommand(transaction, this.lockName, timeoutMillis, mode)) { result = new LockScope(transaction); } } finally { // if we fail to acquire or throw, make sure to clean up if (result == null) { transaction?.Dispose(); connection.Dispose(); } } return(result); }
public IDisposable TryAcquire(int timeoutMillis, SqlApplicationLock.Mode mode, IDisposable contextHandle) { if (contextHandle != null) { return(this.CreateContextLock(contextHandle).TryAcquire(timeoutMillis, mode, contextHandle: null)); } IDisposable result = null; var connection = new SqlConnection(this.connectionString); try { connection.Open(); if (SqlApplicationLock.ExecuteAcquireCommand(connection, this.lockName, timeoutMillis, mode)) { result = new LockScope(connection, this.lockName); } } finally { // if we fail to acquire or throw, make sure to clean up the connection if (result == null) { connection.Dispose(); } } return(result); }
public async Task <IDisposable> TryAcquireAsync(int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, IDisposable contextHandle) { this.CheckConnection(); return(await SqlApplicationLock.ExecuteAcquireCommandAsync(new ConnectionOrTransaction(this.transaction), this.lockName, timeoutMillis, mode, cancellationToken).ConfigureAwait(false) ? new LockScope(this) : null); }
public IDisposable TryAcquire(int timeoutMillis, SqlApplicationLock.Mode mode, IDisposable contextHandle) { this.CheckConnection(); return(SqlApplicationLock.ExecuteAcquireCommand(new ConnectionOrTransaction(this.transaction), this.lockName, timeoutMillis, mode) ? new LockScope(this) : null); }
public async Task <IDisposable> TryAcquireAsync(int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, IDisposable contextHandle) { if (contextHandle != null) { cancellationToken.ThrowIfCancellationRequested(); // if already canceled, exit immediately // if we are taking a nested lock, we don't want to start another keepalive on the same connection. // However, we do need to stop our current keepalive while we take the nested lock to avoid threading issues var lockScope = (LockScope)contextHandle; await lockScope.Keepalive.StopAsync().ConfigureAwait(false); try { var internalHandle = await lockScope.InternalLock.TryAcquireAsync(timeoutMillis, mode, cancellationToken, contextHandle : lockScope.InternalHandle).ConfigureAwait(false); return(internalHandle != null ? new LockScope(internalHandle, lockScope.InternalLock, lockScope.Keepalive, ownsKeepalive: false) : null); } finally { // always restart, even if the acquisition fails lockScope.Keepalive.Start(); } } var connection = new SqlConnection(this.connectionString); LockScope result = null; try { await connection.OpenAsync(cancellationToken).ConfigureAwait(false); var internalLock = new ConnectionScopedSqlDistributedLock(this.lockName, connection); var internalHandle = await internalLock.TryAcquireAsync(timeoutMillis, mode, cancellationToken, contextHandle : null).ConfigureAwait(false); if (internalHandle != null) { var keepalive = new KeepaliveHelper(connection); keepalive.Start(); result = new LockScope(internalHandle, internalLock, keepalive, ownsKeepalive: true); } } finally { if (result == null) { connection.Dispose(); } } return(result); }
public Task <IDisposable> TryAcquireAsync(int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, IDisposable contextHandle) { // cannot multiplex for updates, since we cannot predict whether or not there will be a request to elevate // to an exclusive lock which asks for a long timeout if (mode != SqlApplicationLock.Mode.Update && contextHandle == null) { return(LockPool.TryAcquireAsync(connectionString, lockName, timeoutMillis, mode, cancellationToken)); } // otherwise, fall back to our fallback lock return(this.fallbackLock.TryAcquireAsync(timeoutMillis, mode, cancellationToken, contextHandle)); }
public IDisposable TryAcquire(int timeoutMillis, SqlApplicationLock.Mode mode, IDisposable contextHandle) { if (contextHandle != null) { // if we are taking a nested lock, we don't want to start another keepalive on the same connection. // However, we do need to stop our current keepalive while we take the nested lock to avoid threading issues var lockScope = (LockScope)contextHandle; lockScope.Keepalive.Stop(); try { var internalHandle = lockScope.InternalLock.TryAcquire(timeoutMillis, mode, contextHandle: lockScope.InternalHandle); return(internalHandle != null ? new LockScope(internalHandle, lockScope.InternalLock, lockScope.Keepalive, ownsKeepalive: false) : null); } finally { // always restart, even if the acquisition fails lockScope.Keepalive.Start(); } } var connection = new SqlConnection(this.connectionString); LockScope result = null; try { connection.Open(); var internalLock = new ConnectionScopedSqlDistributedLock(this.lockName, connection); var internalHandle = internalLock.TryAcquire(timeoutMillis, mode, contextHandle: null); if (internalHandle != null) { var keepalive = new KeepaliveHelper(connection); keepalive.Start(); result = new LockScope(internalHandle, internalLock, keepalive, ownsKeepalive: true); } } finally { if (result == null) { connection.Dispose(); } } return(result); }
public async Task <Result> TryAcquireAsync( string lockName, int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, bool opportunistic) { if (!await this.mutex.WaitAsync(opportunistic ? TimeSpan.Zero : Timeout.InfiniteTimeSpan, cancellationToken).ConfigureAwait(false)) { // mutex wasn't free, so just give up return(this.GetFailureResultNoLock(Reason.MutexTimeout, opportunistic, timeoutMillis)); } try { if (this.outstandingHandles.ContainsKey(lockName)) { // 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(Reason.AlreadyHeld, opportunistic, timeoutMillis)); } if (this.connection.State != ConnectionState.Open) { await this.connection.OpenAsync(cancellationToken).ConfigureAwait(false); } if (await SqlApplicationLock.ExecuteAcquireCommandAsync(this.connection, lockName, opportunistic ? 0 : timeoutMillis, mode, cancellationToken).ConfigureAwait(false)) { var handle = new Handle(this, lockName); this.outstandingHandles.Add(lockName, new WeakReference <Handle>(handle)); 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(Reason.AcquireTimeout, opportunistic, timeoutMillis)); } finally { this.CloseConnectionIfNeededNoLock(); this.mutex.Release(); } }
private IDisposable TryAcquireWithAsyncCancellation(int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken, IDisposable contextHandle = null) { var tryAcquireTask = this.internalLock.TryAcquireAsync(timeoutMillis, mode, cancellationToken, contextHandle); try { return(tryAcquireTask.Result); } catch (AggregateException ex) { // attempt to prevent the throwing of aggregate exceptions if (ex.InnerExceptions.Count == 1) { // rethrow the inner exception without losing stack trace ExceptionDispatchInfo.Capture(ex.InnerException).Throw(); } throw; // otherwise just rethrow } }
public Result TryAcquire( string lockName, int timeoutMillis, SqlApplicationLock.Mode mode, bool opportunistic) { if (!this.mutex.Wait(opportunistic ? TimeSpan.Zero : Timeout.InfiniteTimeSpan)) { // mutex wasn't free, so just give up return(this.GetFailureResultNoLock(Reason.MutexTimeout, opportunistic, timeoutMillis)); } try { if (this.outstandingHandles.ContainsKey(lockName)) { // 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(Reason.AlreadyHeld, opportunistic, timeoutMillis)); } if (this.connection.State != ConnectionState.Open) { this.connection.Open(); } if (SqlApplicationLock.ExecuteAcquireCommand(this.connection, lockName, opportunistic ? 0 : timeoutMillis, mode)) { var handle = new Handle(this, lockName); this.outstandingHandles.Add(lockName, new WeakReference <Handle>(handle)); return(new Result(handle)); } return(this.GetFailureResultNoLock(Reason.AcquireTimeout, opportunistic, timeoutMillis)); } finally { this.CloseConnectionIfNeededNoLock(); this.mutex.Release(); } }
public async Task <IDisposable> TryAcquireAsync( string connectionString, string lockName, int timeoutMillis, SqlApplicationLock.Mode mode, CancellationToken cancellationToken) { // opportunistic phase: see if we can use a connection that is already holding a lock // to acquire the current lock var existingLock = this.GetExistingLockOrDefault(connectionString); if (existingLock != null) { try { var opportunisticResult = await existingLock.TryAcquireAsync(lockName, timeoutMillis, mode, cancellationToken, opportunistic : true).ConfigureAwait(false); if (opportunisticResult.Handle != null) { return(opportunisticResult.Handle); } switch (opportunisticResult.Retry) { case MultiplexedConnectionLockRetry.NoRetry: return(null); case MultiplexedConnectionLockRetry.RetryOnThisLock: var result = await existingLock.TryAcquireAsync(lockName, timeoutMillis, mode, cancellationToken, opportunistic : false).ConfigureAwait(false); return(result.Handle); case MultiplexedConnectionLockRetry.Retry: break; default: throw new InvalidOperationException("unexpected retry"); } } finally { // since we took this lock from the pool, always return it to the pool this.AddLockToPool(connectionString, existingLock); } } // normal phase: if we were not able to be opportunistic, ensure that we have a lock var @lock = new MultiplexedConnectionLock(connectionString); IDisposable handle = null; try { handle = (await @lock.TryAcquireAsync(lockName, timeoutMillis, mode, cancellationToken, opportunistic: false).ConfigureAwait(false)).Handle; } finally { // only store the lock on success; otherwise there's no reason to keep it around if (handle != null) { this.AddLockToPool(connectionString, @lock); } else { @lock.Dispose(); } } return(handle); }