Files
LuaCsForBarotraumaEP/Barotrauma/BarotraumaShared/SharedSource/SingleThreadWorker.cs

117 lines
3.7 KiB
C#

using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;
namespace Barotrauma
{
public class SingleThreadWorker
{
private ConcurrentQueue<Action> ActionQueue;
public static SingleThreadWorker Instance = new SingleThreadWorker();
private readonly CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
private readonly SemaphoreSlim actionSignal = new SemaphoreSlim(0);
private readonly Task workerTask;
private bool disposed;
public static readonly SemaphoreSlim SingleThreadActionStandbySignal = new SemaphoreSlim(1);
/// <summary>
/// Initilize a SingleThreadWorker
/// SingleThreadWorker or STW for short is a FIFO queue ensure single-thread execution of a series of actions.
/// </summary>
public SingleThreadWorker()
{
ActionQueue = new ConcurrentQueue<Action>();
workerTask = CreateProcessTask(cancellationTokenSource.Token);
}
public void Dispose()
{
if (disposed) { return; }
disposed = true;
cancellationTokenSource.Cancel();
try
{
actionSignal.Release();
workerTask.Wait(2);
}
catch (AggregateException) { }
catch (ObjectDisposedException) { }
cancellationTokenSource.Dispose();
actionSignal.Dispose();
}
private async Task CreateProcessTask(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
bool lockTaken = false;
try
{
await actionSignal.WaitAsync(100, token);
SingleThreadActionStandbySignal.Wait(CancellationToken.None);
lockTaken = true;
RunActions();
}
catch (OperationCanceledException)
{
break;
}
finally
{
if (lockTaken)
{
SingleThreadActionStandbySignal.Release();
}
}
}
}
/// <summary>
/// Add a pending action in a STW queue.
/// DO NOT ABUSE IT OR IT WILL SLOW DOWN MAIN THREAD!!!!
/// </summary>
/// <param name="action"></param>
public void AddAction(Action action)
{
if (disposed || action == null) { return; }
// enqueue and let background task handle the rest
ActionQueue.Enqueue(action);
if (actionSignal.CurrentCount == 0)
{
actionSignal.Release();
}
}
/// <summary>
/// Run all pending actions in the STW queue
/// </summary>
[STAThread]
private void RunActions()
{
while (ActionQueue.TryDequeue(out Action action))
{
try
{
action?.Invoke();
}
catch (Exception e)
{
// Just try-catch and do nothing but print errorlogs. We cannot afford crashing the game.
ConsoleColor originalForeground = Console.ForegroundColor;
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"WARNING: Error occurred when running Single Thread Actions." +
$"If the server didn't crash or stop responding then this should be fine \n{e}");
Console.ForegroundColor = originalForeground;
}
}
}
}
}