diff --git a/src/Microsoft.VisualStudio.Threading/IPendingExecutionRequestState.cs b/src/Microsoft.VisualStudio.Threading/IPendingExecutionRequestState.cs
new file mode 100644
index 000000000..9d73c4e8c
--- /dev/null
+++ b/src/Microsoft.VisualStudio.Threading/IPendingExecutionRequestState.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Microsoft Corporation. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.ComponentModel;
+using System.Diagnostics.CodeAnalysis;
+
+namespace Microsoft.VisualStudio.Threading
+{
+ ///
+ /// An optional interface implemented by pending request state posted to the underline synchronization context. It allows synchronization context to remove completed requests.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ [Experimental("VSOnly")]
+ public interface IPendingExecutionRequestState
+ {
+ ///
+ /// Gets a value indicating whether the current request has been completed, and can be skipped.
+ ///
+ bool IsCompleted { get; }
+ }
+}
diff --git a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs
index 018f4c4bc..8395363b9 100644
--- a/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs
+++ b/src/Microsoft.VisualStudio.Threading/JoinableTaskFactory.cs
@@ -1058,7 +1058,9 @@ public void Dispose()
/// A delegate wrapper that ensures the delegate is only invoked at most once.
///
[DebuggerDisplay("{DelegateLabel}")]
- internal class SingleExecuteProtector
+#pragma warning disable VSOnly // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
+ internal class SingleExecuteProtector : IPendingExecutionRequestState
+#pragma warning restore VSOnly // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
{
///
/// Executes the delegate if it has not already executed.
@@ -1112,6 +1114,11 @@ private SingleExecuteProtector(JoinableTask job)
this.job = job;
}
+ ///
+ /// Gets a value indicating whether the current request has been completed, and can be skipped.
+ ///
+ bool IPendingExecutionRequestState.IsCompleted => this.HasBeenExecuted;
+
///
/// Gets a value indicating whether this instance has already executed.
///
diff --git a/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
index 0e00ec898..82ffde8aa 100644
--- a/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net472/PublicAPI.Unshipped.txt
@@ -1,2 +1,4 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt
index 0e00ec898..82ffde8aa 100644
--- a/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net8.0-windows/PublicAPI.Unshipped.txt
@@ -1,2 +1,4 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt
index 0e00ec898..82ffde8aa 100644
--- a/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/net8.0/PublicAPI.Unshipped.txt
@@ -1,2 +1,4 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file
diff --git a/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt b/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
index 0e00ec898..82ffde8aa 100644
--- a/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.VisualStudio.Threading/netstandard2.0/PublicAPI.Unshipped.txt
@@ -1,2 +1,4 @@
Microsoft.VisualStudio.Threading.AsyncBarrier.SignalAndWait(System.Threading.CancellationToken cancellationToken) -> System.Threading.Tasks.ValueTask
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState
+Microsoft.VisualStudio.Threading.IPendingExecutionRequestState.IsCompleted.get -> bool
static Microsoft.VisualStudio.Threading.JoinableTaskContext.CreateNoOpContext() -> Microsoft.VisualStudio.Threading.JoinableTaskContext!
\ No newline at end of file