using System; using System.Collections.Concurrent; using System.Threading; namespace Ryujinx.Common.Logging { public enum AsyncLogTargetOverflowAction { /// /// Block until there's more room in the queue /// Block = 0, /// /// Discard the overflowing item /// Discard = 1 } public class AsyncLogTargetWrapper : ILogTarget { private ILogTarget _target; private Thread _messageThread; private BlockingCollection _messageQueue; private readonly int _overflowTimeout; string ILogTarget.Name { get => _target.Name; } public AsyncLogTargetWrapper(ILogTarget target) : this(target, -1, AsyncLogTargetOverflowAction.Block) { } public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction) { _target = target; _messageQueue = new BlockingCollection(queueLimit); _overflowTimeout = overflowAction == AsyncLogTargetOverflowAction.Block ? -1 : 0; _messageThread = new Thread(() => { while (!_messageQueue.IsCompleted) { try { _target.Log(this, _messageQueue.Take()); } catch (InvalidOperationException) { // IOE means that Take() was called on a completed collection. // Some other thread can call CompleteAdding after we pass the // IsCompleted check but before we call Take. // We can simply catch the exception since the loop will break // on the next iteration. } } }); _messageThread.Name = "Logger.MessageThread"; _messageThread.IsBackground = true; _messageThread.Start(); } public void Log(object sender, LogEventArgs e) { if (!_messageQueue.IsAddingCompleted) { _messageQueue.TryAdd(e, _overflowTimeout); } } public void Dispose() { _messageQueue.CompleteAdding(); _messageThread.Join(); } } }