using System.Runtime.InteropServices;
namespace XFESystem.NativeKernel;
[StructLayout(LayoutKind.Sequential)]
public struct ThreadProbe { public ulong Tls, Canary, Steps, Errors, PeerSteps, Done, Deadline, TickAddress, StartTicks; }
public static unsafe class ThreadSelfTest
{
static ulong _event;
static ulong _eventStep;
[UnmanagedCallersOnly(EntryPoint = "xfe_kernel_event_probe")]
public static uint EventProbe(void* argument)
{
Threads.Sleep(20);
_eventStep = 1;
if (!KernelEvents.Set(_event)) return 0;
return 1;
}
public static bool Run()
{
if (!Threads.Ready || !KernelEvents.Initialize() || !LocalApicTimer.Initialize()) return false;
ulong page = PhysicalMemory.Allocate();
if (page == 0) return false;
byte* storage = (byte*)PhysicalMemory.ToVirtual(page);
ThreadProbe* a = (ThreadProbe*)storage;
ThreadProbe* b = (ThreadProbe*)(storage + 128);
ulong* tlsA = (ulong*)(storage + 256), tlsB = (ulong*)(storage + 384);
*tlsA = 0x5846455448524431; *tlsB = 0x5846455448524432;
a->Tls = (ulong)tlsA; b->Tls = (ulong)tlsB;
a->Canary = *tlsA; b->Canary = *tlsB;
a->PeerSteps = (ulong)&b->Steps; b->PeerSteps = (ulong)&a->Steps;
a->TickAddress = b->TickAddress = (ulong)Native.Storage(14);
a->StartTicks = b->StartTicks = LocalApicTimer.Ticks;
a->Deadline = b->Deadline = Native.ReadTsc() + MonotonicClock.Frequency * 3;
ulong oldPreemptions = Threads.Preemptions;
delegate* unmanaged<void*, uint> probe = (delegate* unmanaged<void*, uint>)Native.ThreadProbeAddress();
if (Threads.Create(probe, a, 65536, a->Tls, false) == 0 || Threads.Create(probe, b, 65536, b->Tls, false) == 0) return false;
ulong timeout = MonotonicClock.Nanoseconds() + 4000000000;
while ((a->Done == 0 || b->Done == 0) && MonotonicClock.Nanoseconds() < timeout) Threads.Yield();
bool result = a->Done != 0 && b->Done != 0 && a->Errors == 0 && b->Errors == 0 &&
a->Steps >= 100000 && b->Steps >= 100000 && Threads.Preemptions >= oldPreemptions + 3 && Native.ReadMsr(0xc0000100) == 0;
Threads.Yield(); // reap finished guarded stacks only from another stack
if (!PhysicalMemory.Release(page)) return false;
_event = KernelEvents.Create(false, false);
if (_event == 0 || Threads.Create(&EventProbe, null, 65536, 0, false) == 0) return false;
ulong eventHandle = _event;
uint wait = KernelEvents.Wait(&eventHandle, 1, 1000);
if (wait != 0 || _eventStep != 1 || KernelEvents.Wait(&eventHandle, 1, 0) != KernelEvents.Timeout) return false;
if (!KernelEvents.Close(_event) || KernelEvents.Set(_event)) return false;
ulong manual = KernelEvents.Create(true, true);
if (manual == 0 || KernelEvents.Wait(&manual, 1, 0) != 0 || KernelEvents.Wait(&manual, 1, 0) != 0 ||
!KernelEvents.Reset(manual) || KernelEvents.Wait(&manual, 1, 2) != KernelEvents.Timeout || !KernelEvents.Close(manual)) return false;
Threads.Yield();
return result;
}
}
using System.Runtime.InteropServices;
namespace XFESystem.NativeKernel;
[StructLayout(LayoutKind.Sequential)]
public struct ThreadProbe { public ulong Tls, Canary, Steps, Errors, PeerSteps, Done, Deadline, TickAddress, StartTicks; }
public static unsafe class ThreadSelfTest
{
static ulong _event;
static ulong _eventStep;
[UnmanagedCallersOnly(EntryPoint = "xfe_kernel_event_probe")]
public static uint EventProbe(void* argument)
{
Threads.Sleep(20);
_eventStep = 1;
if (!KernelEvents.Set(_event)) return 0;
return 1;
}
public static bool Run()
{
if (!Threads.Ready || !KernelEvents.Initialize() || !LocalApicTimer.Initialize()) return false;
ulong page = PhysicalMemory.Allocate();
if (page == 0) return false;
byte* storage = (byte*)PhysicalMemory.ToVirtual(page);
ThreadProbe* a = (ThreadProbe*)storage;
ThreadProbe* b = (ThreadProbe*)(storage + 128);
ulong* tlsA = (ulong*)(storage + 256), tlsB = (ulong*)(storage + 384);
*tlsA = 0x5846455448524431; *tlsB = 0x5846455448524432;
a->Tls = (ulong)tlsA; b->Tls = (ulong)tlsB;
a->Canary = *tlsA; b->Canary = *tlsB;
a->PeerSteps = (ulong)&b->Steps; b->PeerSteps = (ulong)&a->Steps;
a->TickAddress = b->TickAddress = (ulong)Native.Storage(14);
a->StartTicks = b->StartTicks = LocalApicTimer.Ticks;
a->Deadline = b->Deadline = Native.ReadTsc() + MonotonicClock.Frequency * 3;
ulong oldPreemptions = Threads.Preemptions;
delegate* unmanaged<void*, uint> probe = (delegate* unmanaged<void*, uint>)Native.ThreadProbeAddress();
if (Threads.Create(probe, a, 65536, a->Tls, false) == 0 || Threads.Create(probe, b, 65536, b->Tls, false) == 0) return false;
ulong timeout = MonotonicClock.Nanoseconds() + 4000000000;
while ((a->Done == 0 || b->Done == 0) && MonotonicClock.Nanoseconds() < timeout) Threads.Yield();
bool result = a->Done != 0 && b->Done != 0 && a->Errors == 0 && b->Errors == 0 &&
a->Steps >= 100000 && b->Steps >= 100000 && Threads.Preemptions >= oldPreemptions + 3 && Native.ReadMsr(0xc0000100) == 0;
Threads.Yield(); // reap finished guarded stacks only from another stack
if (!PhysicalMemory.Release(page)) return false;
_event = KernelEvents.Create(false, false);
if (_event == 0 || Threads.Create(&EventProbe, null, 65536, 0, false) == 0) return false;
ulong eventHandle = _event;
uint wait = KernelEvents.Wait(&eventHandle, 1, 1000);
if (wait != 0 || _eventStep != 1 || KernelEvents.Wait(&eventHandle, 1, 0) != KernelEvents.Timeout) return false;
if (!KernelEvents.Close(_event) || KernelEvents.Set(_event)) return false;
ulong manual = KernelEvents.Create(true, true);
if (manual == 0 || KernelEvents.Wait(&manual, 1, 0) != 0 || KernelEvents.Wait(&manual, 1, 0) != 0 ||
!KernelEvents.Reset(manual) || KernelEvents.Wait(&manual, 1, 2) != KernelEvents.Timeout || !KernelEvents.Close(manual)) return false;
Threads.Yield();
return result;
}
}