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); } } } }