using System.Threading; using XFESystem.Desktop; namespace XFESystem.Kernel; /// One verified worker and sixteen pending callbacks; never holds a lock across application work. internal sealed class NativeManagedWorkScheduler : IEditorBackgroundWorkScheduler, IEditorPreparationCanvasFactory { // Both real UI and interpreter work retain the previous generous boot-stack // budget. Smaller managed stacks are not claimed safe without native proof. internal const int StackBytes = 8 * 1024 * 1024; private const int Capacity = 16; private readonly Action?[] pending = new Action?[Capacity]; private uint published, consumed; private readonly Action trace; private readonly Thread thread; private readonly int layoutScale; internal NativeManagedWorkScheduler(Action trace, int layoutScale) { if (!NativeThreadRegression.Passed) throw new InvalidOperationException("The native thread/EH/GC regression has not passed."); this.trace = trace; this.layoutScale = layoutScale; thread = new Thread(WorkLoop, StackBytes); thread.Start(); } public bool TryQueue(Action work) { ArgumentNullException.ThrowIfNull(work); // Single producer (the desktop) / single consumer. Publication is the // only shared operation; the worker never executes under a queue lock. uint writer = Volatile.Read(ref published), reader = Volatile.Read(ref consumed); if (unchecked(writer - reader) >= Capacity) return false; pending[writer & (Capacity - 1)] = work; Volatile.Write(ref published, unchecked(writer + 1)); return true; } private void WorkLoop() { trace("XFE_EDITOR_WORKER_READY|Thread|" + NativeThreadRegression.CurrentThreadId() + "|StackBytes|" + StackBytes + "|SingleCpu|1|GcStopTheWorld|1|QueueCapacity|" + Capacity + "\n"); while (true) { uint reader = Volatile.Read(ref consumed); if (reader == Volatile.Read(ref published)) { Thread.Sleep(2); continue; } var action = pending[reader & (Capacity - 1)]!; pending[reader & (Capacity - 1)] = null; Volatile.Write(ref consumed, unchecked(reader + 1)); try { action(); } catch (Exception error) when (error is not OutOfMemoryException and not StackOverflowException) { trace("XFE_EDITOR_WORKER_UNHANDLED|" + error.GetType().Name + "\n"); } } } public IDesktopCanvas CreateEditorPreparationCanvas() => CanvasAdapter.CreateEditorPreparation(layoutScale); }