using System.Threading.Tasks;
using System.Collections.Concurrent;
public static async Task Main()
var service = new Service();
var blocker1 = new Blocker();
service.AddBlocker(blocker1);
using var semaphore1 = new SemaphoreSlim(0, 1);
await Task.Delay(100).ConfigureAwait(false);
await semaphore1.WaitAsync().ConfigureAwait(false);
Console.WriteLine("Blocker 1 released.");
var serviceTask1 = Task.Run(async () =>
await service.WaitBlockersAsync().ConfigureAwait(false);
await serviceTask1.ConfigureAwait(false);
var blocker2 = new Blocker();
service.AddBlocker(blocker2);
using var semaphore2 = new SemaphoreSlim(0);
await Task.Delay(100).ConfigureAwait(false);
await semaphore2.WaitAsync().ConfigureAwait(false);
Console.WriteLine("Blocker 2 released.");
var serviceTask2 = Task.Run(async () =>
await service.WaitBlockersAsync().ConfigureAwait(false);
await serviceTask2.ConfigureAwait(false);
Console.WriteLine("ASSERT");
public event EventHandler Released;
Released?.Invoke(this, EventArgs.Empty);
private object _lockObject = new object();
private TaskCompletionSource<bool> _tcs = new TaskCompletionSource<bool>();
private readonly ConcurrentDictionary<Blocker, byte> _commandBlockers = new ConcurrentDictionary<Blocker, byte>();
public Task WaitBlockersAsync()
return Task.CompletedTask;
public void AddBlocker(Blocker commandBlocker)
commandBlocker.Released += CommandBlocker_Release;
_commandBlockers.TryAdd(commandBlocker, 0);
_tcs = new TaskCompletionSource<bool>();
private void CommandBlocker_Release(object? sender, EventArgs e)
var blocker = (Blocker)sender;
blocker.Released -= CommandBlocker_Release;
_commandBlockers.TryRemove(blocker, out var _);
if (_commandBlockers.IsEmpty && _tcs != null)