public override IFetchedJob FetchNextJob(string[] queueNames, CancellationToken cancellationToken) { using (var ready = new SemaphoreSlim(0, queueNames.Length)) { var waitNode = new InMemoryQueueWaitNode(ready); var waitAdded = false; while (!cancellationToken.IsCancellationRequested) { // TODO: Ensure duplicate queue names do not fail everything var entries = _dispatcher.GetOrAddQueues(queueNames); foreach (var entry in entries) { if (entry.Value.Queue.TryDequeue(out var jobId)) { _dispatcher.SignalOneQueueWaitNode(entry.Value); return(new InMemoryFetchedJob(_dispatcher, entry.Key, jobId)); } } if (!waitAdded && ready.CurrentCount == 0) { foreach (var entry in entries) { _dispatcher.AddQueueWaitNode(entry.Value, waitNode); } waitAdded = true; continue; } ready.Wait(cancellationToken); waitAdded = false; } } cancellationToken.ThrowIfCancellationRequested(); return(null); }
public override IFetchedJob FetchNextJob([NotNull] string[] queues, CancellationToken cancellationToken) { if (queues == null) { throw new ArgumentNullException(nameof(queues)); } if (queues.Length == 0) { throw new ArgumentException("Queue array must be non-empty.", nameof(queues)); } using (var cancellationEvent = cancellationToken.GetCancellationEvent()) { var entries = _dispatcher.GetOrAddQueues(queues).ToArray(); var readyEvents = new WaitHandle[entries.Length + 1]; var waitAdded = new bool[entries.Length]; try { for (var i = 0; i < entries.Length; i++) { readyEvents[i] = new AutoResetEvent(false); } readyEvents[entries.Length] = cancellationEvent.WaitHandle; while (!cancellationToken.IsCancellationRequested) { foreach (var entry in entries) { if (entry.Value.Queue.TryDequeue(out var jobId)) { _dispatcher.SignalOneQueueWaitNode(entry.Value); return(new InMemoryFetchedJob(_dispatcher, entry.Key, jobId)); } } for (var i = 0; i < entries.Length; i++) { if (!waitAdded[i]) { _dispatcher.AddQueueWaitNode(entries[i].Value, new InMemoryQueueWaitNode((AutoResetEvent)readyEvents[i])); waitAdded[i] = true; } } var ready = WaitHandle.WaitAny(readyEvents, TimeSpan.FromSeconds(1)); if (ready != WaitHandle.WaitTimeout && ready < entries.Length) { waitAdded[ready] = false; } } cancellationToken.ThrowIfCancellationRequested(); return(null); } finally { for (var i = 0; i < entries.Length; i++) { readyEvents[i]?.Dispose(); } } } }