ryujinx/Ryujinx.Common/Logging/Targets/AsyncLogTargetWrapper.cs
jduncanator d306115750 Logger and Configuration Refactoring (#573)
* Logging: Refactor log targets into Ryujinx.Common

* Logger: Implement JSON Log Target

* Logger: Optimize Console/File logging targets

Implement a simple ObjectPool to pool up StringBuilders to avoid causing excessive GCing of gen1/2 items when large amounts of log entries are being generated.

We can also pre-determine the async overflow action at initialization time, allowing for an easy optimization in the message enqueue function, avoiding a number of comparisons.

* Logger: Implement LogFormatters

* Config: Refactor configuration file and loading

* Config: Rename to .jsonc to avoid highlighting issues in VSC and GitHub

* Resolve style nits

* Config: Resolve incorrect default key binding

* Config: Also update key binding default in schema

* Tidy up namespace imports

* Config: Update CONFIG.md to reflect new Config file
2019-02-11 09:00:32 -03:00

76 lines
2.3 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Threading;
namespace Ryujinx.Common.Logging
{
public enum AsyncLogTargetOverflowAction
{
/// <summary>
/// Block until there's more room in the queue
/// </summary>
Block = 0,
/// <summary>
/// Discard the overflowing item
/// </summary>
Discard = 1
}
public class AsyncLogTargetWrapper : ILogTarget
{
private ILogTarget _target;
private Thread _messageThread;
private BlockingCollection<LogEventArgs> _messageQueue;
private readonly int _overflowTimeout;
public AsyncLogTargetWrapper(ILogTarget target)
: this(target, -1, AsyncLogTargetOverflowAction.Block)
{ }
public AsyncLogTargetWrapper(ILogTarget target, int queueLimit, AsyncLogTargetOverflowAction overflowAction)
{
_target = target;
_messageQueue = new BlockingCollection<LogEventArgs>(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.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();
}
}
}