GBA.Unity/Assets/Iris/Iris.Common/Scheduler.cs
2024-08-14 16:02:39 +08:00

139 lines
4.5 KiB
C#

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Iris.Common
{
public sealed class Scheduler(int taskListSize, int scheduledTaskListSize)
{
public delegate void Task_Delegate(UInt64 cycleCountDelay);
private readonly Task_Delegate[] _taskList = new Task_Delegate[taskListSize];
private struct ScheduledTaskListEntry
{
internal int _id;
internal UInt64 _cycleCount;
}
private readonly ScheduledTaskListEntry[] _scheduledTaskList = new ScheduledTaskListEntry[scheduledTaskListSize]; // sorted by _cycleCount from smallest to largest
private int _scheduledTaskCount;
private UInt64 _cycleCounter;
public void ResetState()
{
_scheduledTaskCount = 0;
_cycleCounter = 0;
}
public void LoadState(BinaryReader reader)
{
foreach (ref ScheduledTaskListEntry entry in _scheduledTaskList.AsSpan())
{
entry._id = reader.ReadInt32();
entry._cycleCount = reader.ReadUInt64();
}
_scheduledTaskCount = reader.ReadInt32();
_cycleCounter = reader.ReadUInt64();
}
public void SaveState(BinaryWriter writer)
{
foreach (ScheduledTaskListEntry entry in _scheduledTaskList)
{
writer.Write(entry._id);
writer.Write(entry._cycleCount);
}
writer.Write(_scheduledTaskCount);
writer.Write(_cycleCounter);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public UInt64 GetCycleCounter()
{
return _cycleCounter;
}
public void AdvanceCycleCounter(UInt64 cycleCount)
{
_cycleCounter += cycleCount;
// process tasks
ref readonly ScheduledTaskListEntry firstEntry = ref MemoryMarshal.GetArrayDataReference(_scheduledTaskList);
ref Task_Delegate taskListDataRef = ref MemoryMarshal.GetArrayDataReference(_taskList);
while ((_scheduledTaskCount > 0) && (firstEntry._cycleCount <= _cycleCounter))
{
// save the task
ScheduledTaskListEntry entry = firstEntry;
// remove it from the list
--_scheduledTaskCount;
if (_scheduledTaskCount > 0)
Array.Copy(_scheduledTaskList, 1, _scheduledTaskList, 0, _scheduledTaskCount);
// execute it
Unsafe.Add(ref taskListDataRef, entry._id)(_cycleCounter - entry._cycleCount);
}
}
public void RegisterTask(int id, Task_Delegate task)
{
_taskList[id] = task;
}
public void ScheduleTask(int id, UInt64 cycleCount)
{
// convert cycleCount from relative to absolute
cycleCount += _cycleCounter;
// get the position and reference of the new task
// (searching is done backward because a new task is more likely to be inserted towards the end)
int index = _scheduledTaskCount;
ref ScheduledTaskListEntry entry = ref Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(_scheduledTaskList), _scheduledTaskCount - 1);
while ((index > 0) && (entry._cycleCount > cycleCount))
{
--index;
entry = ref Unsafe.Subtract(ref entry, 1);
}
entry = ref Unsafe.Add(ref entry, 1);
// insert the new task
if (index < _scheduledTaskCount)
Array.Copy(_scheduledTaskList, index, _scheduledTaskList, index + 1, _scheduledTaskCount - index);
entry._id = id;
entry._cycleCount = cycleCount;
++_scheduledTaskCount;
}
public void CancelTask(int id)
{
int index = 0;
ref ScheduledTaskListEntry entry = ref MemoryMarshal.GetArrayDataReference(_scheduledTaskList);
while (index < _scheduledTaskCount)
{
if (entry._id == id)
{
--_scheduledTaskCount;
if (index < _scheduledTaskCount)
Array.Copy(_scheduledTaskList, index + 1, _scheduledTaskList, index, _scheduledTaskCount - index);
return;
}
++index;
entry = ref Unsafe.Add(ref entry, 1);
}
}
}
}