Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update reentrant exception to be of specific type #978

Merged
6 changes: 3 additions & 3 deletions src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs
Expand Up @@ -759,7 +759,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 StackReentrantSemaphoreNestingViolationException();
#pragma warning restore CA2219 // Do not raise exceptions in finally clauses
}
}
Expand Down Expand Up @@ -873,7 +873,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 StackReentrantSemaphoreNestingViolationException();
#pragma warning restore CA2219 // Do not raise exceptions in finally clauses
}
}
Expand Down Expand Up @@ -901,7 +901,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="AsyncSemaphore"/> is in a faulted state.
BradBuhrkuhl-ms marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class SemaphoreFaultedException : InvalidOperationException
{
/// <summary>
/// Initializes a new instance of the <see cref="SemaphoreFaultedException"/> class.
/// </summary>
public SemaphoreFaultedException()
: base(Strings.SemaphoreMisused)
{
}
}
}
@@ -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 which is thrown when the reentrancy contract of a <see cref="ReentrantSemaphore"/> created with <see cref="ReentrancyMode.Stack"/> is violated.
BradBuhrkuhl-ms marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class StackReentrantSemaphoreNestingViolationException : InvalidOperationException
AArnott marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// Initializes a new instance of the <see cref="StackReentrantSemaphoreNestingViolationException"/> class.
/// </summary>
public StackReentrantSemaphoreNestingViolationException()
: base(string.Format(Strings.SemaphoreStackNestingViolated, ReentrantSemaphore.ReentrancyMode.Stack))
BradBuhrkuhl-ms marked this conversation as resolved.
Show resolved Hide resolved
{
}
}
}
@@ -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.StackReentrantSemaphoreNestingViolationException
Microsoft.VisualStudio.Threading.StackReentrantSemaphoreNestingViolationException.StackReentrantSemaphoreNestingViolationException() -> 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.StackReentrantSemaphoreNestingViolationException
Microsoft.VisualStudio.Threading.StackReentrantSemaphoreNestingViolationException.StackReentrantSemaphoreNestingViolationException() -> 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.StackReentrantSemaphoreNestingViolationException
Microsoft.VisualStudio.Threading.StackReentrantSemaphoreNestingViolationException.StackReentrantSemaphoreNestingViolationException() -> 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<StackReentrantSemaphoreNestingViolationException>(() => 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<StackReentrantSemaphoreNestingViolationException>(() => 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<StackReentrantSemaphoreNestingViolationException>(() => 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<StackReentrantSemaphoreNestingViolationException>(() => innerFaulterSemaphoreTask).WithCancellation(this.TimeoutToken);
await Assert.ThrowsAsync<SemaphoreFaultedException>(() => semaphore.ExecuteAsync(() => Task.CompletedTask)).WithCancellation(this.TimeoutToken);
});
}

Expand Down