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
@@ -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