diff --git a/dotnet/src/webdriver/BiDi/EventDispatcher.cs b/dotnet/src/webdriver/BiDi/EventDispatcher.cs index 39c9d82c7c0e7..16753b106ff18 100644 --- a/dotnet/src/webdriver/BiDi/EventDispatcher.cs +++ b/dotnet/src/webdriver/BiDi/EventDispatcher.cs @@ -34,6 +34,8 @@ internal sealed class EventDispatcher : IAsyncDisposable private readonly ConcurrentDictionary _eventRegistrations = new(); + private readonly ConcurrentDictionary _runningHandlers = new(); + private readonly Channel _pendingEvents = Channel.CreateUnbounded(new() { SingleReader = true, @@ -81,23 +83,34 @@ private async Task ProcessEventsAwaiterAsync() { while (reader.TryRead(out var result)) { - try + if (_eventRegistrations.TryGetValue(result.Method, out var registration)) { - if (_eventRegistrations.TryGetValue(result.Method, out var registration)) + foreach (var handler in registration.GetHandlers()) // copy-on-write array, safe to iterate { - foreach (var handler in registration.GetHandlers()) // copy-on-write array, safe to iterate + var runningHandlerTask = InvokeHandlerAsync(handler, result.EventArgs); + if (!runningHandlerTask.IsCompleted) { - await handler.InvokeAsync(result.EventArgs).ConfigureAwait(false); + _runningHandlers.TryAdd(runningHandlerTask, 0); + _ = runningHandlerTask.ContinueWith(static (t, state) => ((ConcurrentDictionary)state!).TryRemove(t, out _), + _runningHandlers, TaskContinuationOptions.ExecuteSynchronously); } } } - catch (Exception ex) - { - if (_logger.IsEnabled(LogEventLevel.Error)) - { - _logger.Error($"Unhandled error processing BiDi event handler: {ex}"); - } - } + } + } + } + + private async Task InvokeHandlerAsync(EventHandler handler, EventArgs eventArgs) + { + try + { + await handler.InvokeAsync(eventArgs).ConfigureAwait(false); + } + catch (Exception ex) + { + if (_logger.IsEnabled(LogEventLevel.Error)) + { + _logger.Error($"Unhandled error processing BiDi event handler: {ex}"); } } } @@ -108,6 +121,8 @@ public async ValueTask DisposeAsync() await _eventEmitterTask.ConfigureAwait(false); + await Task.WhenAll(_runningHandlers.Keys).ConfigureAwait(false); + GC.SuppressFinalize(this); }