public IDistributedLock AcquireLock(string name, TimeSpan?maxValidFor, TimeSpan?timeout)
 {
     try {
         DistributedLock result = AcquireLockInternal(name, maxValidFor, timeout, throwOnTimeout: true);
         Logger.Debug("Successfully acquired lock '{0}'.", name);
         return(result);
     }
     catch (Exception ex) {
         Logger.Error(ex, "Error while trying to acquire lock '{0}'.", name);
         throw;
     }
 }
        private DistributedLock AcquireLockInternal(string name, TimeSpan?maxValidFor, TimeSpan?timeout, bool throwOnTimeout)
        {
            var internalName   = GetInternalLockName(name);
            var monitorTimeout = timeout.HasValue ? timeout.Value : TimeSpan.FromMilliseconds(-1); // -1 ms is .NET magic number for "infinite".
            var monitorObj     = String.Intern(String.Format("{0}:{1}", _applicationEnvironment.GetEnvironmentIdentifier(), internalName));

            if (!Monitor.TryEnter(monitorObj, monitorTimeout))
            {
                Logger.Debug("Could not enter local monitor for lock '{0}' within the specified timeout ({1}).", internalName, timeout);

                if (throwOnTimeout)
                {
                    throw new TimeoutException(String.Format("Failed to acquire lock '{0}' within the specified timeout ({1}).", internalName, timeout));
                }

                return(null);
            }

            Logger.Debug("Successfully entered local monitor for lock '{0}'.", internalName);

            try {
                DistributedLock dLock = null;

                // If there's already a distributed lock object in our dictionary, that means
                // this acquisition is a reentrance. Use the existing lock object from the
                // dictionary but increment its count.
                if (_locks.TryGetValue(monitorObj, out dLock))
                {
                    Logger.Debug("Current thread is re-entering lock '{0}'; incrementing count.", internalName);
                    dLock.Increment();
                }
                else
                {
                    // No distributed lock object existed in our dictionary. Try to take ownership
                    // of database record until timeout expires, and if successful create a distributed
                    // lock object and add it to our dictionary.
                    var success = RepeatUntilTimeout(timeout, _defaultRepeatInterval, () => {
                        if (EnsureDistributedLockRecord(internalName, maxValidFor))
                        {
                            Logger.Debug("Record for lock '{0}' already owned by current machine or was successfully created; creating lock object.", internalName);

                            dLock = new DistributedLock(name, internalName, releaseLockAction: () => {
                                Monitor.Exit(monitorObj);
                                DeleteDistributedLockRecord(internalName);
                            });

                            _locks.Add(monitorObj, dLock);
                            return(true);
                        }

                        return(false);
                    });

                    if (!success)
                    {
                        Logger.Debug("Record for lock '{0}' could not be created for current machine within the specified timeout ({1}).", internalName, timeout);

                        if (throwOnTimeout)
                        {
                            throw new TimeoutException(String.Format("Failed to acquire lock '{0}' within the specified timeout ({1}).", internalName, timeout));
                        }

                        return(null);
                    }
                }

                return(dLock);
            }
            catch (Exception ex) {
                Monitor.Exit(monitorObj);

                Logger.Error(ex, "An error occurred while trying to acquire lock '{0}'.", internalName);
                throw;
            }
        }