Esempio n. 1
0
        /// <summary>
        /// Attempts to acquire the lock synchronously. Usage:
        /// <code>
        ///     using (var handle = myLock.TryAcquire(...))
        ///     {
        ///         if (handle != null) { /* we have the lock! */ }
        ///     }
        ///     // dispose releases the lock if we took it
        /// </code>
        /// </summary>
        /// <param name="lock"></param>
        /// <param name="timeout">How long to wait before giving up on acquiring the lock. Defaults to 0</param>
        /// <returns>An <see cref="IDisposable"/> "handle" which can be used to release the lock, or null if the lock was not taken</returns>
        public LockAcquisitionResult TryAcquire(string @lock, TimeSpan timeout = default(TimeSpan))
        {
            // synchronous mode
            var timeoutMillis = timeout.ToInt32Timeout();

            // calculate safe lock name
            var lockName = ToSafeLockName(@lock, MaxLockNameLength, s => s);

            DbConnection acquireConnection = null;
            var          cleanup           = true;

            try
            {
                acquireConnection = GetConnection();

                if (_connectionString != null)
                {
                    acquireConnection.Open();
                }
                else if (acquireConnection == null)
                {
                    throw new InvalidOperationException("The transaction had been disposed");
                }
                else if (acquireConnection.State != ConnectionState.Open)
                {
                    throw new InvalidOperationException("The connection is not open");
                }

                var checkCommand = SqlHelpers.CreateCheckLockAvailabilityCommand(acquireConnection, timeoutMillis, lockName);
                var exists       = (int)checkCommand.ExecuteScalar() > 0;
                if (exists)
                {
                    return(LockAcquisitionResult.Fail);
                }

                var id = Guid.NewGuid();

                SqlParameter insertReturnValue;
                using (var insertCommand = SqlHelpers.CreateInsertApplicationLockCommand(acquireConnection, id,
                                                                                         timeoutMillis, lockName, Utc, out insertReturnValue))
                {
                    insertCommand.ExecuteNonQuery();
                }

                var ret = (int)insertReturnValue.Value;
                cleanup = ret == 0;
                var success = ret == 0;
                var owner   = string.Empty;

                if (success)
                {
                    // hash owner
                    owner = _encryptor.Encrypt(id.ToString());

                    // check no duplicates.
                    var checkDuplicateCommand = SqlHelpers.CreateCheckLockAvailabilityCommand(acquireConnection, timeoutMillis, lockName);
                    var duplicatesExist       = (int)checkDuplicateCommand.ExecuteScalar() > 1;

                    if (duplicatesExist)
                    {
                        // delete current lock
                        ReleaseLock(lockName, owner);
                        return(LockAcquisitionResult.Fail);
                    }
                }

                return(new LockAcquisitionResult
                {
                    Success = success,
                    LockOwner = owner
                });
            }
            catch
            {
                // in case we fail to create lock scope or something
                cleanup = true;
                throw;
            }
            finally
            {
                if (cleanup)
                {
                    Cleanup(acquireConnection);
                }
            }
        }