private static int GetRemainingTimeout(int millisecondsTimeout, long initialTicks) { return(millisecondsTimeout == Timeout.Infinite ? Timeout.Infinite : (int)Math.Max(0, millisecondsTimeout - (TimeUtils.GetTimestampTicks(true) - initialTicks) / 10000)); }
/// <summary> /// Tries to asynchronously enter the lock in write mode, with an optional integer time-out. /// </summary> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or -1 /// (<see cref="Timeout.Infinite"/>) to wait indefinitely.</param> /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param> /// <returns>A task that will complete with a result of <c>true</c> if the lock has been entered, /// otherwise with a result of <c>false</c>.</returns> /// <exception cref="ArgumentOutOfRangeException"><paramref name="millisecondsTimeout"/> is a negative number /// other than -1, which represents an infinite time-out.</exception> /// <exception cref="OperationCanceledException"><paramref name="cancellationToken"/> was canceled.</exception> /// <exception cref="ObjectDisposedException">The current instance has already been disposed.</exception> public async Task <bool> TryEnterWriteLockAsync( int millisecondsTimeout = 0, CancellationToken cancellationToken = default) { DenyIfDisposed(); if (millisecondsTimeout < Timeout.Infinite) { throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout)); } cancellationToken.ThrowIfCancellationRequested(); long initialTicks = millisecondsTimeout == Timeout.Infinite ? 0 : TimeUtils.GetTimestampTicks(true); // Enter the write lock semaphore before doing anything else. if (!EnterWriteLockPreface(out bool waitForReadLocks)) { bool writeLockWaitResult = false; try { writeLockWaitResult = await this.writeLockSemaphore.WaitAsync( millisecondsTimeout, cancellationToken); } finally { EnterWriteLockPostface(writeLockWaitResult, out waitForReadLocks); } if (!writeLockWaitResult) { return(false); } } // After we set the write lock state, we might need to wait for existing read // locks to be released. // In this state, no new read locks can be entered until we release the write // lock state. // We only wait one time since only the last active read lock will release // the semaphore. if (waitForReadLocks) { bool waitResult = false; try { // This may throw an OperationCanceledException. waitResult = await this.readLockReleaseSemaphore.WaitAsync( GetRemainingTimeout(millisecondsTimeout, initialTicks), cancellationToken); } finally { if (!waitResult) { // Timeout has been exceeded or a OperationCancelledException has // been thrown. HandleEnterWriteLockWaitFailure(); } } if (!waitResult) { return(false); // Timeout exceeded } } return(true); }