// Copyright © WireMock.Net
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading;
namespace WireMock.Models;
///
internal class BlockingQueue(TimeSpan? readTimeout = null) : IBlockingQueue
{
private readonly TimeSpan _readTimeout = readTimeout ?? TimeSpan.FromHours(1);
private readonly Queue _queue = new();
private readonly object _lockObject = new();
private bool _isClosed;
///
/// Writes an item to the queue and signals that an item is available.
///
/// The item to be added to the queue.
public void Write(T item)
{
lock (_lockObject)
{
if (_isClosed)
{
throw new InvalidOperationException("Cannot write to a closed queue.");
}
_queue.Enqueue(item);
// Signal that an item is available
Monitor.Pulse(_lockObject);
}
}
///
/// Tries to read an item from the queue.
/// - waits until an item is available
/// - or the timeout occurs
/// - or queue is closed
///
/// The item read from the queue, or default if the timeout occurs.
/// True if an item was successfully read; otherwise, false.
public bool TryRead([NotNullWhen(true)] out T? item)
{
lock (_lockObject)
{
// Wait until an item is available or timeout occurs
while (_queue.Count == 0 && !_isClosed)
{
// Wait with timeout
if (!Monitor.Wait(_lockObject, _readTimeout))
{
item = default;
return false;
}
}
// After waiting, check if we have items
if (_queue.Count == 0)
{
item = default;
return false;
}
item = _queue.Dequeue();
return item != null;
}
}
///
/// Closes the queue and signals all waiting threads.
///
public void Close()
{
lock (_lockObject)
{
_isClosed = true;
Monitor.PulseAll(_lockObject);
}
}
}