/// <summary> /// Asynchronously acquire a lock on the specified TLockableObject. /// This will wait the specified amount of time before either throwing an exception (if throwOnFailure is true) or retuning false. /// </summary> /// <param name="obj">The object to take a lock on.</param> /// <param name="timeout">The amount of time to spend attempting to acquire the lock.</param> /// <param name="throwOnFailure">Throw an exception if the acquisition fails.</param> /// <param name="cancellationToken">The cancellation token to use during lock acquisition.</param> /// <returns>A <see cref="bool"/> indicating if the lock attempt was successful.</returns> public async Task <TLockableObject> AcquireLockAsync(TLockableObject obj, TimeSpan timeout, bool throwOnFailure = false, CancellationToken cancellationToken = default(CancellationToken)) { var timeoutDate = DateTime.UtcNow.Add(timeout); _lockedObjectType = obj.GetType(); _lockedObjectId = obj.Id; // TODO: Check if the supplied TLockableObject implements IEnumerable and that they supplied an IEnumerable<TLockableObject> selector. TLockableObject lockedObj = null; while (lockedObj == null && !cancellationToken.IsCancellationRequested && DateTime.UtcNow < timeoutDate) { lockedObj = await _store.AcquireLockAsync(obj.Id, obj, _staleLockMultiplier, cancellationToken); if (lockedObj == null) { await Task.Delay(TimeSpan.FromSeconds(1), cancellationToken); } else { LockedObjectLockId = lockedObj.LockId; LockedObjectId = lockedObj.Id; } } _sharpLockLogger.Trace("Lock attempt complete on {0} with LockId: {1}. Lock Acquired? {2}", _lockedObjectType, _lockedObjectId, LockAcquired); if (lockedObj == null && throwOnFailure) { throw new AcquireDistributedLockException("Failed to acquire lock."); } return(lockedObj); }