Skip to content

Commit

Permalink
Update reentrant exception to be of specific type (#978)
Browse files Browse the repository at this point in the history
  • Loading branch information
BradBuhrkuhl-ms committed Jan 21, 2022
1 parent 70507b1 commit 1160dbb
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 12 deletions.
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.Threading
{
using System;
using System.Globalization;

/// <summary>
/// Exception which is thrown when the contract of a <see cref="ReentrantSemaphore"/> is violated.
/// </summary>
public class IllegalSemaphoreUsageException : InvalidOperationException
{
/// <summary>
/// Initializes a new instance of the <see cref="IllegalSemaphoreUsageException"/> class.
/// </summary>
public IllegalSemaphoreUsageException(string message)
: base(message)
{
}
}
}
7 changes: 4 additions & 3 deletions src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs
Expand Up @@ -7,6 +7,7 @@ namespace Microsoft.VisualStudio.Threading
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
Expand Down Expand Up @@ -759,7 +760,7 @@ public override async Task ExecuteAsync(Func<Task> operation, CancellationToken
// When the semaphore faults, we will drain and throw for awaiting tasks one by one.
this.faulted = true;
#pragma warning disable CA2219 // Do not raise exceptions in finally clauses
throw Verify.FailOperation(Strings.SemaphoreStackNestingViolated, ReentrancyMode.Stack);
throw new IllegalSemaphoreUsageException(string.Format(CultureInfo.CurrentCulture, Strings.SemaphoreStackNestingViolated, ReentrantSemaphore.ReentrancyMode.Stack));
#pragma warning restore CA2219 // Do not raise exceptions in finally clauses
}
}
Expand Down Expand Up @@ -873,7 +874,7 @@ public override async ValueTask<T> ExecuteAsync<T>(Func<ValueTask<T>> operation,
// When the semaphore faults, we will drain and throw for awaiting tasks one by one.
this.faulted = true;
#pragma warning disable CA2219 // Do not raise exceptions in finally clauses
throw Verify.FailOperation(Strings.SemaphoreStackNestingViolated, ReentrancyMode.Stack);
throw new IllegalSemaphoreUsageException(string.Format(CultureInfo.CurrentCulture, Strings.SemaphoreStackNestingViolated, ReentrantSemaphore.ReentrancyMode.Stack));
#pragma warning restore CA2219 // Do not raise exceptions in finally clauses
}
}
Expand Down Expand Up @@ -901,7 +902,7 @@ protected override void ThrowIfFaulted()
{
if (this.faulted)
{
throw Verify.FailOperation(Strings.SemaphoreMisused);
throw new SemaphoreFaultedException();
}
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/Microsoft.VisualStudio.Threading/SemaphoreFaultedException.cs
@@ -0,0 +1,21 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.Threading
{
using System;

/// <summary>
/// Exception thrown when a <see cref="ReentrantSemaphore"/> is in a faulted state.
/// </summary>
public class SemaphoreFaultedException : InvalidOperationException
{
/// <summary>
/// Initializes a new instance of the <see cref="SemaphoreFaultedException"/> class.
/// </summary>
public SemaphoreFaultedException()
: base(Strings.SemaphoreMisused)
{
}
}
}
@@ -1,4 +1,8 @@
Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.AsyncReaderWriterLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics = false) -> void
Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.AsyncReaderWriterResourceLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics) -> void
Microsoft.VisualStudio.Threading.JoinableTaskContext.IsMainThreadMaybeBlocked() -> bool
Microsoft.VisualStudio.Threading.SemaphoreFaultedException
Microsoft.VisualStudio.Threading.SemaphoreFaultedException.SemaphoreFaultedException() -> void
Microsoft.VisualStudio.Threading.IllegalSemaphoreUsageException
Microsoft.VisualStudio.Threading.IllegalSemaphoreUsageException.IllegalSemaphoreUsageException(string! message) -> void
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.DeadlockCheckTimeout.get -> System.TimeSpan
@@ -1,4 +1,8 @@
Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.AsyncReaderWriterLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics = false) -> void
Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.AsyncReaderWriterResourceLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics) -> void
Microsoft.VisualStudio.Threading.JoinableTaskContext.IsMainThreadMaybeBlocked() -> bool
Microsoft.VisualStudio.Threading.SemaphoreFaultedException
Microsoft.VisualStudio.Threading.SemaphoreFaultedException.SemaphoreFaultedException() -> void
Microsoft.VisualStudio.Threading.IllegalSemaphoreUsageException
Microsoft.VisualStudio.Threading.IllegalSemaphoreUsageException.IllegalSemaphoreUsageException(string! message) -> void
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.DeadlockCheckTimeout.get -> System.TimeSpan
@@ -1,4 +1,8 @@
Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.AsyncReaderWriterLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics = false) -> void
Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock<TMoniker, TResource>.AsyncReaderWriterResourceLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics) -> void
Microsoft.VisualStudio.Threading.JoinableTaskContext.IsMainThreadMaybeBlocked() -> bool
Microsoft.VisualStudio.Threading.SemaphoreFaultedException
Microsoft.VisualStudio.Threading.SemaphoreFaultedException.SemaphoreFaultedException() -> void
Microsoft.VisualStudio.Threading.IllegalSemaphoreUsageException
Microsoft.VisualStudio.Threading.IllegalSemaphoreUsageException.IllegalSemaphoreUsageException(string! message) -> void
virtual Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.DeadlockCheckTimeout.get -> System.TimeSpan
Expand Up @@ -533,18 +533,18 @@ public void Stack_ViolationCaughtAtBothSites()

// Release the outer one first. This should throw because the inner one hasn't been released yet.
release1.Set();
await Assert.ThrowsAsync<InvalidOperationException>(() => operation1);
await Assert.ThrowsAsync<IllegalSemaphoreUsageException>(() => operation1);

// Verify that the semaphore is in a faulted state.
Assert.Throws<InvalidOperationException>(() => this.semaphore.CurrentCount);
Assert.Throws<SemaphoreFaultedException>(() => this.semaphore.CurrentCount);

// Release the nested one last, which should similarly throw because its parent is already released.
release2.Set();
await Assert.ThrowsAsync<InvalidOperationException>(() => operation2);
await Assert.ThrowsAsync<IllegalSemaphoreUsageException>(() => operation2);

// Verify that the semaphore is still in a faulted state, and will reject new calls.
Assert.Throws<InvalidOperationException>(() => this.semaphore.CurrentCount);
await Assert.ThrowsAsync<InvalidOperationException>(() => this.semaphore.ExecuteAsync(() => Task.CompletedTask));
Assert.Throws<SemaphoreFaultedException>(() => this.semaphore.CurrentCount);
await Assert.ThrowsAsync<SemaphoreFaultedException>(() => this.semaphore.ExecuteAsync(() => Task.CompletedTask));
});
}

Expand Down Expand Up @@ -691,12 +691,12 @@ public void Stack_FaultedSemaphoreDrains()
Task? pendingSemaphoreTask = semaphore.ExecuteAsync(() => Task.CompletedTask);
releaser1.Set();
await Assert.ThrowsAsync<InvalidOperationException>(() => outerFaultySemaphoreTask).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<InvalidOperationException>(() => pendingSemaphoreTask).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<IllegalSemaphoreUsageException>(() => outerFaultySemaphoreTask).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<SemaphoreFaultedException>(() => pendingSemaphoreTask).WithCancellation(this.TimeoutToken);
releaser1.Set();
await Assert.ThrowsAsync<InvalidOperationException>(() => innerFaulterSemaphoreTask).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<InvalidOperationException>(() => semaphore.ExecuteAsync(() => Task.CompletedTask)).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<IllegalSemaphoreUsageException>(() => innerFaulterSemaphoreTask).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<SemaphoreFaultedException>(() => semaphore.ExecuteAsync(() => Task.CompletedTask)).WithCancellation(this.TimeoutToken);
});
}

Expand Down

0 comments on commit 1160dbb

Please sign in to comment.