using System; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; namespace RobotNet10.Realtime; /// /// Native Linux system calls and constants for realtime operations. /// internal static partial class LinuxNative { // Scheduling policies public const int SCHED_NORMAL = 0; public const int SCHED_FIFO = 1; public const int SCHED_RR = 2; public const int SCHED_BATCH = 3; public const int SCHED_IDLE = 5; public const int SCHED_DEADLINE = 6; // Clock IDs public const int CLOCK_REALTIME = 0; public const int CLOCK_MONOTONIC = 1; public const int CLOCK_PROCESS_CPUTIME_ID = 2; public const int CLOCK_THREAD_CPUTIME_ID = 3; public const int CLOCK_MONOTONIC_RAW = 4; public const int CLOCK_REALTIME_COARSE = 5; public const int CLOCK_MONOTONIC_COARSE = 6; public const int CLOCK_BOOTTIME = 7; public const int CLOCK_REALTIME_ALARM = 8; public const int CLOCK_BOOTTIME_ALARM = 9; // Timerfd flags public const int TFD_NONBLOCK = 0x800; public const int TFD_CLOEXEC = 0x80000; // Timerfd timer types public const int TFD_TIMER_ABSTIME = 0x1; // CPU set size (for CPU affinity) public const int CPU_SETSIZE = 1024; public const int __NCPUBITS = 64; // Memory lock flags public const int MCL_CURRENT = 1; public const int MCL_FUTURE = 2; public const int MCL_ONFAULT = 4; // Signal numbers for real-time signals (SIGRTMIN to SIGRTMAX) public const int SIGRTMIN = 34; public const int SIGRTMAX = 64; // Priority ranges public const int MIN_PRIORITY = 1; public const int MAX_PRIORITY = 99; [StructLayout(LayoutKind.Sequential)] public struct sched_param { public int sched_priority; } [StructLayout(LayoutKind.Sequential)] public struct timespec { public long tv_sec; // seconds public long tv_nsec; // nanoseconds } [StructLayout(LayoutKind.Sequential)] public struct itimerspec { public timespec it_interval; // timer interval public timespec it_value; // timer expiration } [StructLayout(LayoutKind.Sequential)] public unsafe struct cpu_set_t { // Using fixed buffer instead of array with MarshalAs for LibraryImport compatibility // 16 * 64 = 1024 bits public fixed ulong __bits[16]; } // Scheduling functions [LibraryImport("libc", SetLastError = true)] public static partial int sched_setscheduler(int pid, int policy, ref sched_param param); [LibraryImport("libc", SetLastError = true)] public static partial int sched_getscheduler(int pid); [LibraryImport("libc", SetLastError = true)] public static partial int sched_getparam(int pid, ref sched_param param); [LibraryImport("libc", SetLastError = true)] public static partial int sched_setparam(int pid, ref sched_param param); [LibraryImport("libc", SetLastError = true)] public static partial int sched_get_priority_min(int policy); [LibraryImport("libc", SetLastError = true)] public static partial int sched_get_priority_max(int policy); [LibraryImport("libc", SetLastError = true)] public static partial int sched_yield(); // CPU affinity functions [LibraryImport("libc", SetLastError = true)] public static partial int sched_setaffinity(int pid, IntPtr cpusetsize, ref cpu_set_t mask); [LibraryImport("libc", SetLastError = true)] public static partial int sched_getaffinity(int pid, IntPtr cpusetsize, ref cpu_set_t mask); // CPU set manipulation macros (implemented as functions) public static unsafe void CPU_ZERO(ref cpu_set_t set) { for (int i = 0; i < 16; i++) { set.__bits[i] = 0; } } public static unsafe void CPU_SET(int cpu, ref cpu_set_t set) { int index = cpu / __NCPUBITS; int bit = cpu % __NCPUBITS; if (index < 16) { set.__bits[index] |= (1UL << bit); } } public static unsafe void CPU_CLR(int cpu, ref cpu_set_t set) { int index = cpu / __NCPUBITS; int bit = cpu % __NCPUBITS; if (index < 16) { set.__bits[index] &= ~(1UL << bit); } } public static unsafe int CPU_ISSET(int cpu, ref cpu_set_t set) { int index = cpu / __NCPUBITS; int bit = cpu % __NCPUBITS; if (index >= 16) { return 0; } return (set.__bits[index] & (1UL << bit)) != 0 ? 1 : 0; } public static unsafe int CPU_COUNT(ref cpu_set_t set) { int count = 0; for (int i = 0; i < 16; i++) { count += System.Numerics.BitOperations.PopCount(set.__bits[i]); } return count; } // Memory locking functions [LibraryImport("libc", SetLastError = true)] public static partial int mlock(IntPtr addr, IntPtr len); [LibraryImport("libc", SetLastError = true)] public static partial int munlock(IntPtr addr, IntPtr len); [LibraryImport("libc", SetLastError = true)] public static partial int mlockall(int flags); [LibraryImport("libc", SetLastError = true)] public static partial int munlockall(); // Clock functions [LibraryImport("libc", SetLastError = true)] public static partial int clock_gettime(int clockid, ref timespec tp); [LibraryImport("libc", SetLastError = true)] public static partial int clock_settime(int clockid, ref timespec tp); [LibraryImport("libc", SetLastError = true)] public static partial int clock_getres(int clockid, ref timespec res); // Timerfd functions [LibraryImport("libc", SetLastError = true)] public static partial int timerfd_create(int clockid, int flags); [LibraryImport("libc", SetLastError = true)] public static partial int timerfd_settime(int fd, int flags, ref itimerspec new_value, ref itimerspec old_value); [LibraryImport("libc", SetLastError = true)] public static partial int timerfd_gettime(int fd, ref itimerspec curr_value); // Process/Thread functions [LibraryImport("libc", SetLastError = true)] public static partial int getpid(); [LibraryImport("libc", SetLastError = true)] public static partial uint gettid(); // Error handling [LibraryImport("libc")] public static partial IntPtr __errno_location(); public static int GetLastError() { IntPtr errnoPtr = __errno_location(); if (errnoPtr == IntPtr.Zero) { return 0; } return Marshal.ReadInt32(errnoPtr); } public static void ThrowLastError(string operation) { int errno = GetLastError(); IntPtr errorMsgPtr = strerror(errno); string errorMsg = errorMsgPtr != IntPtr.Zero ? Marshal.PtrToStringAnsi(errorMsgPtr) ?? "Unknown error" : "Unknown error"; throw new RealtimeException($"{operation} failed with errno {errno}: {errorMsg}", errno); } [LibraryImport("libc")] private static partial IntPtr strerror(int errno); }