diff --git a/src/Microsoft.VisualStudio.Threading/IllegalSemaphoreUsageException.cs b/src/Microsoft.VisualStudio.Threading/IllegalSemaphoreUsageException.cs
new file mode 100644
index 000000000..1660c1905
--- /dev/null
+++ b/src/Microsoft.VisualStudio.Threading/IllegalSemaphoreUsageException.cs
@@ -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;
+
+ ///
+ /// Exception which is thrown when the contract of a is violated.
+ ///
+ public class IllegalSemaphoreUsageException : InvalidOperationException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public IllegalSemaphoreUsageException(string message)
+ : base(message)
+ {
+ }
+ }
+}
diff --git a/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs b/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs
index a8e4c8954..d6b97eb20 100644
--- a/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs
+++ b/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs
@@ -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;
@@ -759,7 +760,7 @@ public override async Task ExecuteAsync(Func 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
}
}
@@ -873,7 +874,7 @@ public override async ValueTask ExecuteAsync(Func> 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
}
}
@@ -901,7 +902,7 @@ protected override void ThrowIfFaulted()
{
if (this.faulted)
{
- throw Verify.FailOperation(Strings.SemaphoreMisused);
+ throw new SemaphoreFaultedException();
}
}
}
diff --git a/src/Microsoft.VisualStudio.Threading/SemaphoreFaultedException.cs b/src/Microsoft.VisualStudio.Threading/SemaphoreFaultedException.cs
new file mode 100644
index 000000000..d9412344c
--- /dev/null
+++ b/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;
+
+ ///
+ /// Exception thrown when a is in a faulted state.
+ ///
+ public class SemaphoreFaultedException : InvalidOperationException
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SemaphoreFaultedException()
+ : base(Strings.SemaphoreMisused)
+ {
+ }
+ }
+}
diff --git a/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
index 860bd7636..e85d3c718 100644
--- a/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
@@ -1,4 +1,8 @@
Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.AsyncReaderWriterLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics = false) -> void
Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock.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
diff --git a/src/Microsoft.VisualStudio.Threading/netcoreapp3.1/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/netcoreapp3.1/PublicAPI.Unshipped.txt
index 860bd7636..e85d3c718 100644
--- a/src/Microsoft.VisualStudio.Threading/netcoreapp3.1/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/netcoreapp3.1/PublicAPI.Unshipped.txt
@@ -1,4 +1,8 @@
Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.AsyncReaderWriterLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics = false) -> void
Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock.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
diff --git a/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
index 860bd7636..e85d3c718 100644
--- a/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
@@ -1,4 +1,8 @@
Microsoft.VisualStudio.Threading.AsyncReaderWriterLock.AsyncReaderWriterLock(Microsoft.VisualStudio.Threading.JoinableTaskContext? joinableTaskContext, bool captureDiagnostics = false) -> void
Microsoft.VisualStudio.Threading.AsyncReaderWriterResourceLock.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
diff --git a/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs
index 7b05eced7..d5a9482b8 100644
--- a/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs
+++ b/test/Microsoft.VisualStudio.Threading.Tests/ReentrantSemaphoreTestBase.cs
@@ -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(() => operation1);
+ await Assert.ThrowsAsync(() => operation1);
// Verify that the semaphore is in a faulted state.
- Assert.Throws(() => this.semaphore.CurrentCount);
+ Assert.Throws(() => this.semaphore.CurrentCount);
// Release the nested one last, which should similarly throw because its parent is already released.
release2.Set();
- await Assert.ThrowsAsync(() => operation2);
+ await Assert.ThrowsAsync(() => operation2);
// Verify that the semaphore is still in a faulted state, and will reject new calls.
- Assert.Throws(() => this.semaphore.CurrentCount);
- await Assert.ThrowsAsync(() => this.semaphore.ExecuteAsync(() => Task.CompletedTask));
+ Assert.Throws(() => this.semaphore.CurrentCount);
+ await Assert.ThrowsAsync(() => this.semaphore.ExecuteAsync(() => Task.CompletedTask));
});
}
@@ -691,12 +691,12 @@ public void Stack_FaultedSemaphoreDrains()
Task? pendingSemaphoreTask = semaphore.ExecuteAsync(() => Task.CompletedTask);
releaser1.Set();
- await Assert.ThrowsAsync(() => outerFaultySemaphoreTask).WithCancellation(this.TimeoutToken);
- await Assert.ThrowsAsync(() => pendingSemaphoreTask).WithCancellation(this.TimeoutToken);
+ await Assert.ThrowsAsync(() => outerFaultySemaphoreTask).WithCancellation(this.TimeoutToken);
+ await Assert.ThrowsAsync(() => pendingSemaphoreTask).WithCancellation(this.TimeoutToken);
releaser1.Set();
- await Assert.ThrowsAsync(() => innerFaulterSemaphoreTask).WithCancellation(this.TimeoutToken);
- await Assert.ThrowsAsync(() => semaphore.ExecuteAsync(() => Task.CompletedTask)).WithCancellation(this.TimeoutToken);
+ await Assert.ThrowsAsync(() => innerFaulterSemaphoreTask).WithCancellation(this.TimeoutToken);
+ await Assert.ThrowsAsync(() => semaphore.ExecuteAsync(() => Task.CompletedTask)).WithCancellation(this.TimeoutToken);
});
}