DragonECS-ClassicThreads/src/ThreadRunner.cs

184 lines
5.7 KiB
C#
Raw Normal View History

2023-06-26 02:58:59 +08:00
using DCFApixels.DragonECS.ClassicThreadsInternal;
2023-06-11 23:24:46 +08:00
using System;
using System.Collections.Concurrent;
2023-06-11 23:24:46 +08:00
using System.Threading;
2023-06-11 23:18:56 +08:00
namespace DCFApixels.DragonECS
2023-06-11 23:09:46 +08:00
{
2024-05-01 14:47:52 +08:00
#if ENABLE_IL2CPP
using Unity.IL2CPP.CompilerServices;
[Il2CppSetOption (Option.NullChecks, false)]
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
#endif
2023-06-11 23:18:56 +08:00
internal static class ThreadRunner
{
private readonly static int _maxThreadsCount;
private static ThreadReacord[] _threads;
2023-06-11 23:09:46 +08:00
2023-06-12 14:40:53 +08:00
private static EcsThreadHandler _worker;
private static EcsThreadHandler _nullWorker = delegate { };
2023-06-11 23:18:56 +08:00
private static int[] _entities = new int[64];
private static ConcurrentQueue<Exception> _catchedExceptions = new ConcurrentQueue<Exception>();
2023-06-11 23:09:46 +08:00
2023-06-12 12:52:45 +08:00
private static bool _isRunning = false;
2024-01-04 22:52:12 +08:00
private static object _lock = new object();
2023-06-11 23:18:56 +08:00
private static void ThreadProc(object obj)
2023-06-11 23:09:46 +08:00
{
2023-06-12 01:33:52 +08:00
int i = (int)obj;
ref ThreadReacord record = ref _threads[i];
2023-06-12 01:16:00 +08:00
while (Thread.CurrentThread.IsAlive)
{
try
2023-06-11 23:18:56 +08:00
{
record.runWork.WaitOne();
record.runWork.Reset();
_worker.Invoke(new ReadOnlySpan<int>(_entities, record.start, record.size));
record.doneWork.Set();
}
2023-06-12 01:33:52 +08:00
catch (Exception e)
2023-06-12 01:16:00 +08:00
{
_catchedExceptions.Enqueue(e);
2023-06-12 01:16:00 +08:00
record.doneWork.Set();
}
}
2023-06-11 23:09:46 +08:00
}
2023-06-11 23:18:56 +08:00
static ThreadRunner()
2023-06-11 23:09:46 +08:00
{
2023-06-11 23:18:56 +08:00
_maxThreadsCount = Environment.ProcessorCount;
_threads = new ThreadReacord[_maxThreadsCount];
for (int i = 0; i < _maxThreadsCount; i++)
2023-06-11 23:09:46 +08:00
{
2023-06-11 23:18:56 +08:00
_threads[i] = new ThreadReacord()
{
thread = new Thread(ThreadProc) { IsBackground = true },
runWork = new ManualResetEvent(false),
doneWork = new ManualResetEvent(true),
};
_threads[i].thread.Start(i);
}
2023-06-12 00:59:01 +08:00
_worker = _nullWorker;
2023-06-11 23:09:46 +08:00
}
public static void Run(EcsThreadHandler worker, EcsSpan entities, int minSpanSize)
2023-06-11 23:18:56 +08:00
{
2023-06-12 12:52:45 +08:00
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
2024-03-02 21:49:04 +08:00
if (_isRunning) { Throw.DoubleParallelIteration(); }
2023-06-12 12:52:45 +08:00
#endif
_isRunning = true;
2023-06-11 23:18:56 +08:00
_worker = worker;
2024-03-02 21:49:04 +08:00
if (_entities.Length < entities.Count)
{
Array.Resize(ref _entities, entities.Count);
}
for (int i = 0; i < entities.Count; i++)
{
_entities[i] = entities[i];
}
int entitiesCount = entities.Count;
2023-06-11 23:09:46 +08:00
2023-06-11 23:18:56 +08:00
int threadsCount = entitiesCount / minSpanSize;
2023-06-12 01:05:19 +08:00
if (entitiesCount % minSpanSize > 0)
{
2023-06-12 01:05:19 +08:00
threadsCount++;
}
2023-06-11 23:18:56 +08:00
if (threadsCount > _maxThreadsCount)
{
2023-06-11 23:18:56 +08:00
threadsCount = _maxThreadsCount;
}
2023-06-11 23:18:56 +08:00
if (threadsCount > 1)
{
2023-06-12 13:35:54 +08:00
int remainder = entitiesCount % threadsCount;
int quotient = entitiesCount / threadsCount;
for (int i = 0, start = 0; i < threadsCount; i++)
2023-06-11 23:18:56 +08:00
{
ref var thread = ref _threads[i];
2023-06-12 13:35:54 +08:00
thread.start = start;
thread.size = quotient;
if (remainder > 0)
{
thread.size++;
remainder--;
}
start += thread.size;
2023-06-11 23:18:56 +08:00
}
}
else
{
threadsCount = 1;
ref var thread = ref _threads[0];
thread.start = 0;
thread.size = entitiesCount;
}
2023-06-11 23:09:46 +08:00
for (int i = 0; i < threadsCount; i++)
{
ref var thread = ref _threads[i];
2023-06-11 23:56:35 +08:00
thread.doneWork.Reset();
2023-06-11 23:18:56 +08:00
thread.runWork.Set();
}
for (int i = 0; i < threadsCount; i++)
{
_threads[i].doneWork.WaitOne();
2023-06-11 23:09:46 +08:00
}
2023-06-12 12:52:45 +08:00
_isRunning = false;
2023-06-12 00:59:01 +08:00
_worker = _nullWorker;
if (_catchedExceptions.Count > 0)
2023-06-12 01:33:52 +08:00
{
2024-01-04 22:52:12 +08:00
lock (_lock)
{
Exception[] exceptions = _catchedExceptions.ToArray();
_catchedExceptions = new ConcurrentQueue<Exception>();
throw new AggregateException(exceptions);
}
2023-06-12 01:33:52 +08:00
}
2023-06-11 23:09:46 +08:00
}
2023-06-11 23:18:56 +08:00
private struct ThreadReacord
2023-06-11 23:09:46 +08:00
{
2023-06-11 23:18:56 +08:00
public Thread thread;
public ManualResetEvent runWork;
public ManualResetEvent doneWork;
public int start;
public int size;
}
2023-06-11 23:09:46 +08:00
}
2023-06-12 14:40:53 +08:00
public delegate void EcsThreadHandler(ReadOnlySpan<int> entities);
2024-05-01 14:47:52 +08:00
}
#if ENABLE_IL2CPP
// Unity IL2CPP performance optimization attribute.
namespace Unity.IL2CPP.CompilerServices
{
using System;
internal enum Option
{
NullChecks = 1,
ArrayBoundsChecks = 2,
DivideByZeroChecks = 3,
}
[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Delegate, Inherited = false, AllowMultiple = true)]
internal class Il2CppSetOptionAttribute : Attribute
{
public Option Option { get; private set; }
public object Value { get; private set; }
public Il2CppSetOptionAttribute(Option option, object value)
{
Option = option;
Value = value;
}
}
}
#endif