public virtual async Task ReconfigureAsync(HubConfiguration <T> configuration, CancellationToken cancellationToken) { await this.sync.WaitAsync(cancellationToken); try { // Cancel task. this.forwardingTaskCancellationTokenSource?.Cancel(); if (cancellationToken.IsCancellationRequested) { return; } // Stop routers and wait until all have finished routing. if (this.Configuration != null) { Task.WaitAll(this.Configuration.Routers.Select(r => r.StopAsync(cancellationToken)).ToArray(), cancellationToken); } if (cancellationToken.IsCancellationRequested) { return; } // Apply new configuration. this.Configuration = configuration; if (configuration == null) { // Keep queue to reuse its contents when reconfiguring again. this.forwardingTaskCancellationTokenSource = null; this.forwardingTask = null; } else { Task.WaitAll(this.Configuration.Routers.Select(r => r.StartAsync(cancellationToken)).ToArray(), cancellationToken); // Create new queue with configured capacity while reusing left-over // queue contents from previous configuration or from the // not-configured state. this.queue = new BlockingCollection <IQueueable>( new ConcurrentQueue <IQueueable>(this.queue.ToArray() ?? Array.Empty <IQueueable>()), configuration.MaximumRoutablesQueueLength); var cts = new CancellationTokenSource(); this.forwardingTaskCancellationTokenSource = cts; this.forwardingTask = new Task(() => { try { this.processQueue(configuration, queue, cts.Token); } catch (OperationCanceledException) { throw; } catch { } }, cts.Token, TaskCreationOptions.LongRunning); this.forwardingTask.Start(); } } catch { throw; } finally { this.sync.Release(); } }
void processQueue(HubConfiguration <T> configuration, BlockingCollection <IQueueable> queue, CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { var list = new List <IQueueable>(); // Get next routable to forward. var queueable = queue.Take(cancellationToken); list.Add(queueable); // Get even more routables to forward within the configured time frame. var tryTakeStarted = DateTime.UtcNow; while ((!cancellationToken.IsCancellationRequested) && (list.Count < configuration.MaximumRoutablesForwardingCount) && (queue.TryTake(out queueable, TimeSpan.FromTicks(Math.Max(0, (tryTakeStarted.Add(configuration.WaitForMoreRoutablesForwardingDelay) - DateTime.UtcNow).Ticks))))) { list.Add(queueable); } if (cancellationToken.IsCancellationRequested) { return; } // Apply after-enqueueing preprocessors now. // TODO Test. foreach (var preprocessor in configuration.Preprocessors.Where(p => !p.OnEnqueueing)) { int i = 0; while (i < list.Count) { var preprocessedRoutable = list[i]; if (preprocessedRoutable is QueueableEvent <T> queueableEvent) { var replacementRoutables = preprocessor.Process(queueableEvent.Event); if (replacementRoutables != null) { // Remove original item first. list.RemoveAt(i); if (replacementRoutables.Any()) { // Insert replacement items. list.InsertRange(i, replacementRoutables.Select(r => new QueueableEvent <T>(evt: r)).ToArray()); i += replacementRoutables.Count(); } } else { // Keep item. ++i; } } else { // Not a routable. Keep item. ++i; } } } if (cancellationToken.IsCancellationRequested) { return; } if (list.Any()) { foreach (var router in configuration.Routers) { try { router.ForwardAsync(list.OfType <QueueableEvent <T> >().Select(q => q.Event), default).GetAwaiter().GetResult(); } catch { // TODO Log. } } foreach (var r in list.OfType <QueueableWaitHandleSignal>()) { try { r.Signal(); } catch { // TODO Log. } } } } catch (OperationCanceledException) { return; } catch { // TODO Log. } } }