diff --git a/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs b/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs index 3e7c581a5..792cac1ce 100644 --- a/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs +++ b/src/Microsoft.VisualStudio.Threading/ReentrantSemaphore.cs @@ -153,7 +153,8 @@ public static ReentrantSemaphore Create(int initialCount = 1, JoinableTaskContex /// /// A cancellation token. /// A task that completes with the result of , after the semaphore has been exited. - public abstract Task ExecuteAsync(Func operation, CancellationToken cancellationToken = default); + public Task ExecuteAsync(Func operation, CancellationToken cancellationToken = default) + => this.ExecuteAsync(operation, null, cancellationToken); /// /// Executes a given operation within the semaphore. @@ -167,7 +168,37 @@ public static ReentrantSemaphore Create(int initialCount = 1, JoinableTaskContex /// /// A cancellation token. /// A task that completes with the result of , after the semaphore has been exited. - public abstract ValueTask ExecuteAsync(Func> operation, CancellationToken cancellationToken = default); + public ValueTask ExecuteAsync(Func> operation, CancellationToken cancellationToken = default) + => this.ExecuteValueTaskAsync(operation, null, cancellationToken); + + /// + /// Executes a given operation within the semaphore. + /// + /// + /// The delegate to invoke once the semaphore is entered. If a was supplied to the constructor, + /// this delegate will execute on the main thread if this is invoked on the main thread, otherwise it will be invoked on the + /// threadpool. When no is supplied to the constructor, this delegate will execute on the + /// caller's context. + /// + /// A cancellation token. + /// A task that completes with the result of , after the semaphore has been exited. + public Task ExecuteAsync(Func operation, object? state, CancellationToken cancellationToken = default) + => this.ExecuteAsync((object)operation, state, cancellationToken); + + /// + /// Executes a given operation within the semaphore. + /// + /// The type of value returned by the operation. + /// + /// The delegate to invoke once the semaphore is entered. If a was supplied to the constructor, + /// this delegate will execute on the main thread if this is invoked on the main thread, otherwise it will be invoked on the + /// threadpool. When no is supplied to the constructor, this delegate will execute on the + /// caller's context. + /// + /// A cancellation token. + /// A task that completes with the result of , after the semaphore has been exited. + public ValueTask ExecuteAsync(Func> operation, object? state, CancellationToken cancellationToken = default) + => this.ExecuteValueTaskAsync(operation, state, cancellationToken); /// /// Conceals evidence that the caller has entered this till its result is disposed. @@ -191,6 +222,33 @@ public void Dispose() GC.SuppressFinalize(this); } + /// + /// Executes a given operation within the semaphore. + /// + /// + /// The delegate to invoke once the semaphore is entered. If a was supplied to the constructor, + /// this delegate will execute on the main thread if this is invoked on the main thread, otherwise it will be invoked on the + /// threadpool. When no is supplied to the constructor, this delegate will execute on the + /// caller's context. + /// + /// A cancellation token. + /// A task that completes with the result of , after the semaphore has been exited. + private protected abstract Task ExecuteAsync(object operation, object? state, CancellationToken cancellationToken = default); + + /// + /// Executes a given operation within the semaphore. + /// + /// The type of value returned by the operation. + /// + /// The delegate to invoke once the semaphore is entered. If a was supplied to the constructor, + /// this delegate will execute on the main thread if this is invoked on the main thread, otherwise it will be invoked on the + /// threadpool. When no is supplied to the constructor, this delegate will execute on the + /// caller's context. + /// + /// A cancellation token. + /// A task that completes with the result of , after the semaphore has been exited. + private protected abstract ValueTask ExecuteValueTaskAsync(object operation, object? state, CancellationToken cancellationToken = default); + /// /// Disposes managed and unmanaged resources held by this instance. /// @@ -317,7 +375,7 @@ internal NotRecognizedSemaphore(int initialCount, JoinableTaskContext? joinableT } /// - public override async Task ExecuteAsync(Func operation, CancellationToken cancellationToken = default) + private protected override async Task ExecuteAsync(object operation, object? state, CancellationToken cancellationToken = default) { Requires.NotNull(operation, nameof(operation)); @@ -371,7 +429,14 @@ await this.ExecuteCoreAsync(async delegate } } - await operation().ConfigureAwaitRunInline(); + if (operation is Func func) + { + await func().ConfigureAwaitRunInline(); + } + else + { + await ((Func)operation)(state).ConfigureAwaitRunInline(); + } }); } finally @@ -381,7 +446,7 @@ await this.ExecuteCoreAsync(async delegate } /// - public override async ValueTask ExecuteAsync(Func> operation, CancellationToken cancellationToken = default) + private protected override async ValueTask ExecuteValueTaskAsync(object operation, object? state, CancellationToken cancellationToken = default) { Requires.NotNull(operation, nameof(operation)); @@ -432,7 +497,14 @@ public override async ValueTask ExecuteAsync(Func> operation, } } - return await operation().ConfigureAwait(true); + if (operation is Func> func) + { + return await func().ConfigureAwait(true); + } + else + { + return await ((Func>)operation)(state).ConfigureAwait(true); + } }); } finally @@ -470,7 +542,7 @@ internal NotAllowedSemaphore(int initialCount, JoinableTaskContext? joinableTask } /// - public override async Task ExecuteAsync(Func operation, CancellationToken cancellationToken = default) + private protected override async Task ExecuteAsync(object operation, object? state, CancellationToken cancellationToken = default) { Requires.NotNull(operation, nameof(operation)); this.ThrowIfFaulted(); @@ -529,7 +601,15 @@ await this.ExecuteCoreAsync(async delegate } this.reentrancyDetection.Value = ownedBox = new StrongBox(true); - await operation().ConfigureAwaitRunInline(); + + if (operation is Func func) + { + await func().ConfigureAwaitRunInline(); + } + else + { + await ((Func)operation)(state).ConfigureAwaitRunInline(); + } }); } finally @@ -546,7 +626,7 @@ await this.ExecuteCoreAsync(async delegate } /// - public override async ValueTask ExecuteAsync(Func> operation, CancellationToken cancellationToken = default) + private protected override async ValueTask ExecuteValueTaskAsync(object operation, object? state, CancellationToken cancellationToken = default) { Requires.NotNull(operation, nameof(operation)); this.ThrowIfFaulted(); @@ -604,7 +684,15 @@ public override async ValueTask ExecuteAsync(Func> operation, } this.reentrancyDetection.Value = ownedBox = new StrongBox(true); - return await operation().ConfigureAwait(true); + + if (operation is Func> func) + { + return await func().ConfigureAwait(true); + } + else + { + return await ((Func>)operation)(state).ConfigureAwait(true); + } }); } finally