// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Concurrent; using System.Threading; using System.Threading.Tasks; namespace MQTTnet.Internal { public sealed class AsyncQueue : IDisposable { readonly AsyncSignal _signal = new AsyncSignal(); readonly object _syncRoot = new object(); ConcurrentQueue _queue = new ConcurrentQueue(); bool _isDisposed; public int Count => _queue.Count; public void Clear() { Interlocked.Exchange(ref _queue, new ConcurrentQueue()); } public void Dispose() { lock (_syncRoot) { _signal.Dispose(); _isDisposed = true; } } public void Enqueue(TItem item) { lock (_syncRoot) { _queue.Enqueue(item); _signal.Set(); } } public AsyncQueueDequeueResult TryDequeue() { if (_queue.TryDequeue(out var item)) { return AsyncQueueDequeueResult.Success(item); } return AsyncQueueDequeueResult.NonSuccess; } public async Task> TryDequeueAsync(CancellationToken cancellationToken) { while (!cancellationToken.IsCancellationRequested) { try { Task task = null; lock (_syncRoot) { if (_isDisposed) { return AsyncQueueDequeueResult.NonSuccess; } if (_queue.IsEmpty) { task = _signal.WaitAsync(cancellationToken); } } if (task != null) { await task.ConfigureAwait(false); } if (cancellationToken.IsCancellationRequested) { return AsyncQueueDequeueResult.NonSuccess; } if (_queue.TryDequeue(out var item)) { return AsyncQueueDequeueResult.Success(item); } } catch (OperationCanceledException) { return AsyncQueueDequeueResult.NonSuccess; } } return AsyncQueueDequeueResult.NonSuccess; } } }