/// <summary> /// Inits the guard with the action to be performed once /// disposal takes place. /// </summary> /// <param name="item">Encapsulates the locking information.</param> /// <param name="repository">The repository that issued the lock.</param> /// <exception cref="ArgumentNullException">If any of the parameters /// is a null reference.</exception> public ResourceLockGuard(LockItem item, IResourceLockRepository repository) { Ensure.ArgumentNotNull(item, "item"); Ensure.ArgumentNotNull(repository, "repository"); Item = item; Repository = repository; }
/// <summary> /// Tries to acquire an exclusive write /// for the resource, and specifies a timeout for the lock. If this operation /// succeeds, the lock must be released as soon /// as possible through the <see cref="ReleaseWriteLock"/> /// method. /// </summary> /// <param name="timeout">The timeout of the lock. /// A value of null requests a lock that doesn't time out.</param> /// <returns>A lock that corresponds to the request. If the lock was denied, the /// returned item's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> /// <exception cref="ArgumentOutOfRangeException">In case of a negative /// timeout.</exception> public LockItem TryGetWriteLock(TimeSpan?timeout) { lock (this) { if (HasWriteLock || HasReadLocks) { return(LockItem.CreateDenied(ResourceId)); } activeWriteLock = LockItem.CreateWrite(ResourceId, timeout); return(activeWriteLock); } }
/// <summary> /// Releases a previously acquired write lock. /// </summary> /// <param name="lockId">The <see cref="LockItem.LockId"/> /// which identifies the lock to be released.</param> /// <returns>True if a matching lock was found and released. /// If no lock was found, false.</returns> public bool ReleaseWriteLock(string lockId) { lock (this) { if (activeWriteLock != null && activeWriteLock.LockId == lockId) { //don't check whether lock has expired - invoking party is happy activeWriteLock = null; return(true); } return(false); } }
/// <summary> /// Removes expired locks. /// </summary> private void Cleanup() { cleanUpCounter++; if (cleanUpCounter % 100 == 0) { //TODO audit/log expired locks? readLocks.RemoveAll(li => li.Expiration.IsExpired()); if (activeWriteLock != null && activeWriteLock.Expiration.IsExpired()) { activeWriteLock = null; } cleanUpCounter = 1; } }
/// <summary> /// Releases a previously acquired lock for a given /// resource. /// </summary> /// <param name="item">The lock to be released.</param> /// <returns>True if the lock was released. False in case /// of an unknown (e.g. automatically removed) lock, or if /// the lock is ignored because it's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> public bool ReleaseLock(LockItem item) { switch (item.LockType) { case ResourceLockType.Read: return(ReleaseReadLock(item.ResourceId, item.LockId)); case ResourceLockType.Write: return(ReleaseWriteLock(item.ResourceId, item.LockId)); case ResourceLockType.Denied: return(false); default: throw new ArgumentOutOfRangeException("Unsupported lock type: " + item.LockType); } }
/// <summary> /// Tries to acquire a shared read lock /// for the resource, and specifies a timeout for the lock. If this operation /// succeeds, the lock must be released as soon /// as possible through the <see cref="ReleaseReadLock"/> /// method. /// </summary> /// <returns>A lock that corresponds to the request. If the lock was denied, the /// returned item's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> /// <param name="timeout">The timeout of the lock. /// A null value requests a lock that doesn't time out.</param> /// <returns>True if the lock was granted.</returns> /// <exception cref="ArgumentOutOfRangeException">In case of a negative /// timeout.</exception> public LockItem TryGetReadLock(TimeSpan?timeout) { lock (this) { if (HasWriteLock) { return(LockItem.CreateDenied(ResourceId)); } //clean up inactive locks from time to time Cleanup(); var item = LockItem.CreateRead(ResourceId, timeout); readLocks.Add(item); return(item); } }
/// <summary> /// Releases a previously acquired read lock. /// </summary> /// <param name="lockId">The <see cref="LockItem.LockId"/> /// which identifies the lock to be released.</param> /// <returns>True if a matching lock was found and released. /// If no lock was found, false.</returns> public bool ReleaseReadLock(string lockId) { lock (this) { for (int i = 0; i < readLocks.Count; i++) { LockItem lockItem = readLocks[i]; if (lockItem.LockId == lockId) { readLocks.RemoveAt(i); //don't check whether lock has expired - invoking party is happy return(true); } } return(false); } }
/// <summary> /// Gets a read lock on all parent folders of a given resource, and rolls back the resources /// if the locking fails. /// </summary> /// <param name="repository">The repository that manages locked resources.</param> /// <param name="resourceId">The resource to be locked.</param> /// <param name="isWriteLock">Whether to acquire a read or write lock for the resource itself.</param> /// <param name="parentFolderIds">Ids of the resource's parent folders, which are being locked one /// by one. The list should be ordered, with the first item being the immediate parent of the locked /// resource, and the last one the topmost folder to be locked.</param> /// <returns>A guard which releases the resource and all folders once it is being disposed.</returns> public static ResourceLockGuard GetResourceChainLock(this IResourceLockRepository repository, string resourceId, bool isWriteLock, List <string> parentFolderIds) { LockItem lockItem = isWriteLock ? repository.TryGetWriteLock(resourceId) : repository.TryGetReadLock(resourceId); //we couldn't get a lock on the resource itself if (!lockItem.IsEnabled) { return(new ResourceLockGuard(lockItem, repository)); } //get read locks for all parent folders List <LockItem> folderLocks = new List <LockItem>(); LockItem folderLock = null; foreach (string folderId in parentFolderIds) { //lock parent folders, one by one for read access (prevents deletion) folderLock = repository.TryGetReadLock(folderId); if (!folderLock.IsEnabled) { break; } folderLocks.Add(folderLock); } if (folderLock != null && !folderLock.IsEnabled) { //roll back all locks... repository.ReleaseLock(lockItem); folderLocks.ForEach(li => repository.ReleaseLock(li)); //...and return a dummy lock return(new ResourceLockGuard(LockItem.CreateDenied(resourceId), repository)); } return(new ResourceLockGuard(lockItem, repository) { SecondaryLocks = folderLocks }); }
/// <summary> /// Gets a guard that can be used with a <c>using</c> /// statement, which tries to get a read lock and /// ensures the lock is being released as soon as /// the using block is being exited. Check the /// guard's <see cref="ResourceLockGuard.IsLockEnabled"/> /// property to verify whether the lock was granted /// or not. /// </summary> /// <returns>A guard that handles management of the /// acquired read lock.</returns> public ResourceLockGuard GetReadGuard(string resourceId) { return(new ResourceLockGuard(LockItem.CreateRead(resourceId, null), this)); }
/// <summary> /// Releases a previously acquired lock for a given /// resource. /// </summary> /// <param name="item">The lock to be released.</param> /// <returns>True if the lock was released. False in case /// of an unknown (e.g. automatically removed) lock, or if /// the lock is ignored because it's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> public bool ReleaseLock(LockItem item) { return true; }
/// <summary> /// Tries to acquire an exclusive write lock for the resource /// which expires after a given while in order not to block /// the resource indefinitely. /// If this operation succeeds, the lock must be released as soon /// as possible through the <see cref="IResourceLockRepository.ReleaseWriteLock"/> /// method. /// </summary> /// <param name="resourceId">Identifies the locked resource.</param> /// <param name="timeout">The specified expiration timeout from now. /// Allowed values are null for indefinite locking, or any positive value.</param> /// <returns>True if the lock was granted.</returns> /// <exception cref="ArgumentOutOfRangeException">In case of a negative /// timeout.</exception>sitive value.</param> /// <returns>True if the lock was granted.</returns> public LockItem TryGetWriteLock(string resourceId, TimeSpan?timeout) { return(LockItem.CreateWrite(resourceId, timeout)); }
/// <summary> /// Tries to acquire an exclusive write lock for the resource /// which expires after a given time span in order not to block /// the resource indefinitely. /// If this operation succeeds, the lock must be released as soon /// as possible through the <see cref="IResourceLockRepository.ReleaseReadLock"/> /// method. /// </summary> /// <param name="resourceId">Identifies the locked resource.</param> /// <param name="timeout">The specified expiration timeout from now. /// Allowed values are null for indefinite locking, or any positive value.</param> /// <returns>True if the lock was granted.</returns> /// <exception cref="ArgumentOutOfRangeException">In case of a negative /// timeout.</exception> public LockItem TryGetReadLock(string resourceId, TimeSpan?timeout) { return(LockItem.CreateRead(resourceId, timeout)); }
/// <summary> /// Gets a guard that can be used with a <c>using</c> /// statement, which tries to get a write lock and /// ensures the lock is being released as soon as /// the using block is being exited. Check the /// guard's <see cref="ResourceLockGuard.IsLockEnabled"/> /// property to verify whether the lock was granted /// or not. /// </summary> /// <returns>A guard that handles management of the /// acquired write lock.</returns> public ResourceLockGuard GetWriteGuard(string resourceId) { return(new ResourceLockGuard(LockItem.CreateWrite(resourceId, null), this)); }
/// <summary> /// Tries to acquire a shared read lock for a given resource /// which never expires. /// If this operation succeeds, the lock must be released as soon /// as possible through the <see cref="IResourceLockRepository.ReleaseReadLock"/> /// method. /// </summary> /// <returns>A <see cref="LockItem"/> instance which represents /// the acquired lock, if any. If no lock was granted, the /// returned item's <see cref="LockItem.LockType"/> property /// returns <see cref="ResourceLockType.Denied"/>.</returns> public LockItem TryGetReadLock(string resourceId) { return(LockItem.CreateRead(resourceId, null)); }
/// <summary> /// Releases a previously acquired lock for a given /// resource. /// </summary> /// <param name="item">The lock to be released.</param> /// <returns>True if the lock was released. False in case /// of an unknown (e.g. automatically removed) lock, or if /// the lock is ignored because it's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> public bool ReleaseLock(LockItem item) { switch (item.LockType) { case ResourceLockType.Read: return ReleaseReadLock(item.ResourceId, item.LockId); case ResourceLockType.Write: return ReleaseWriteLock(item.ResourceId, item.LockId); case ResourceLockType.Denied: return false; default: throw new ArgumentOutOfRangeException("Unsupported lock type: " + item.LockType); } }
/// <summary> /// Tries to acquire an exclusive write lock for a given resource /// which never expires. /// If this operation succeeds, the lock must be released as soon /// as possible through the <see cref="IResourceLockRepository.ReleaseWriteLock"/> /// method. /// </summary> /// <returns>A <see cref="LockItem"/> instance which represents /// the acquired lock, if any. If no lock was granted, the /// returned item's <see cref="LockItem.LockType"/> property /// returns <see cref="ResourceLockType.Denied"/>.</returns> public LockItem TryGetWriteLock(string resourceId) { return(LockItem.CreateWrite(resourceId, null)); }
/// <summary> /// Tries to acquire an exclusive write /// for the resource, and specifies a timeout for the lock. If this operation /// succeeds, the lock must be released as soon /// as possible through the <see cref="ReleaseWriteLock"/> /// method. /// </summary> /// <param name="timeout">The timeout of the lock. /// A value of null requests a lock that doesn't time out.</param> /// <returns>A lock that corresponds to the request. If the lock was denied, the /// returned item's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> /// <exception cref="ArgumentOutOfRangeException">In case of a negative /// timeout.</exception> public LockItem TryGetWriteLock(TimeSpan? timeout) { lock (this) { if (HasWriteLock || HasReadLocks) { return LockItem.CreateDenied(ResourceId); } activeWriteLock = LockItem.CreateWrite(ResourceId, timeout); return activeWriteLock; } }
/// <summary> /// Releases a previously acquired lock for a given /// resource. /// </summary> /// <param name="item">The lock to be released.</param> /// <returns>True if the lock was released. False in case /// of an unknown (e.g. automatically removed) lock, or if /// the lock is ignored because it's <see cref="LockItem.LockType"/> /// is <see cref="ResourceLockType.Denied"/>.</returns> public bool ReleaseLock(LockItem item) { return(true); }
/// <summary> /// Releases a previously acquired write lock. /// </summary> /// <param name="lockId">The <see cref="LockItem.LockId"/> /// which identifies the lock to be released.</param> /// <returns>True if a matching lock was found and released. /// If no lock was found, false.</returns> public bool ReleaseWriteLock(string lockId) { lock (this) { if(activeWriteLock != null && activeWriteLock.LockId == lockId) { //don't check whether lock has expired - invoking party is happy activeWriteLock = null; return true; } return false; } }