mirror of
https://github.com/Facepunch/sbox-public.git
synced 2026-01-06 21:38:32 -05:00
This commit imports the C# engine code and game files, excluding C++ source code. [Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
87 lines
2.3 KiB
C#
87 lines
2.3 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Sandbox.Tasks;
|
|
|
|
internal static class WorkerThread
|
|
{
|
|
private static Task[] _sWorkerTasks;
|
|
private static CancellationTokenSource _sCts;
|
|
|
|
public static bool HasStarted => _sWorkerTasks != null;
|
|
|
|
/// <summary>
|
|
/// Starts a bunch of long-running tasks in the worker thread pool that
|
|
/// keep calling <see cref="ExpirableSynchronizationContext.ProcessQueue"/> on
|
|
/// <see cref="SyncContext.WorkerThread"/>.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">Thrown if tasks are already running.</exception>
|
|
public static void Start()
|
|
{
|
|
if ( HasStarted )
|
|
{
|
|
throw new InvalidOperationException( "Attempted to start new worker threads while some are still running." );
|
|
}
|
|
|
|
ThreadPool.GetMinThreads( out var workerThreads, out _ );
|
|
|
|
// Don't starve main thread
|
|
|
|
workerThreads = Math.Max( Environment.ProcessorCount - 2, 2 );
|
|
|
|
_sWorkerTasks = new Task[workerThreads];
|
|
_sCts = new CancellationTokenSource();
|
|
|
|
for ( var i = 0; i < workerThreads; i++ )
|
|
{
|
|
_sWorkerTasks[i] = Task.Run( WorkerTask );
|
|
}
|
|
}
|
|
|
|
private static async Task WorkerTask()
|
|
{
|
|
var ct = _sCts.Token;
|
|
|
|
while ( !ct.IsCancellationRequested )
|
|
{
|
|
SyncContext.WorkerThread.ProcessQueue();
|
|
|
|
try
|
|
{
|
|
await Task.Delay( 1, ct );
|
|
}
|
|
catch ( TaskCanceledException )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forces the tasks created by <see cref="Start"/> to cancel, to be restarted later.
|
|
/// This doesn't cancel tasks created with Sandbox.TaskSource.RunInThreadAsync, they
|
|
/// just get suspended until <see cref="Start"/> is called again.
|
|
/// </summary>
|
|
/// <param name="millisecondsTimeout">
|
|
/// Log an error if any tasks take longer than this to return.
|
|
/// </param>
|
|
public static void Stop( int millisecondsTimeout )
|
|
{
|
|
if ( _sWorkerTasks == null ) return;
|
|
|
|
_sCts.Cancel();
|
|
|
|
if ( !Task.WhenAll( _sWorkerTasks ).Wait( millisecondsTimeout ) )
|
|
{
|
|
Log.Error( "Background task(s) failed to pause within the allowed time.\n" +
|
|
"Make sure any long-running backround tasks occasionally await TaskSource.Yield() or TaskSource.Delay().\n" +
|
|
$"Tasks: {string.Join( "", _sWorkerTasks.Where( x => !x.IsCompleted ).Select( x => $"\n {x}" ) )}" );
|
|
}
|
|
|
|
_sWorkerTasks = null;
|
|
_sCts = null;
|
|
}
|
|
}
|