using System.Threading;
using XFESystem.Desktop;
namespace XFESystem.Kernel;
/// <summary>One verified worker and sixteen pending callbacks; never holds a lock across application work.</summary>
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<string> trace;
private readonly Thread thread;
private readonly int layoutScale;
internal NativeManagedWorkScheduler(Action<string> 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);
}
using System.Threading;
using XFESystem.Desktop;
namespace XFESystem.Kernel;
/// <summary>One verified worker and sixteen pending callbacks; never holds a lock across application work.</summary>
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<string> trace;
private readonly Thread thread;
private readonly int layoutScale;
internal NativeManagedWorkScheduler(Action<string> 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);
}