/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. Copyright (C) 2010-2020 Michael Möller */ using System; using System.Runtime.InteropServices; namespace OpenHardwareMonitor.Hardware { internal static class ThreadAffinity { static ThreadAffinity() { ProcessorGroupCount = GetProcessorGroupCount(); } private static int GetProcessorGroupCount() { if (OperatingSystem.IsUnix) return 1; try { return NativeMethods.GetActiveProcessorGroupCount(); } catch { return 1; } } public static int ProcessorGroupCount { get; } public static bool IsValid(GroupAffinity affinity) { if (OperatingSystem.IsUnix) { if (affinity.Group > 0) return false; } try { var previous = Set(affinity); if (previous == GroupAffinity.Undefined) return false; Set(previous); return true; } catch { return false; } } /// /// Sets the processor group affinity for the current thread. /// /// The processor group affinity. /// The previous processor group affinity. public static GroupAffinity Set(GroupAffinity affinity) { if (affinity == GroupAffinity.Undefined) return GroupAffinity.Undefined; if (OperatingSystem.IsUnix) { if (affinity.Group > 0) throw new ArgumentOutOfRangeException("affinity.Group"); ulong result = 0; if (NativeMethods.sched_getaffinity(0, (IntPtr)8, ref result) != 0) return GroupAffinity.Undefined; ulong mask = affinity.Mask; if (NativeMethods.sched_setaffinity(0, (IntPtr)8, ref mask) != 0) return GroupAffinity.Undefined; return new GroupAffinity(0, result); } else { UIntPtr uIntPtrMask; try { uIntPtrMask = (UIntPtr)affinity.Mask; } catch (OverflowException) { throw new ArgumentOutOfRangeException("affinity.Mask"); } var groupAffinity = new NativeMethods.GROUP_AFFINITY { Group = affinity.Group, Mask = uIntPtrMask }; var currentThread = NativeMethods.GetCurrentThread(); try { if (NativeMethods.SetThreadGroupAffinity(currentThread, ref groupAffinity, out var previousGroupAffinity)) { return new GroupAffinity(previousGroupAffinity.Group, (ulong)previousGroupAffinity.Mask); } else { return GroupAffinity.Undefined; } } catch (EntryPointNotFoundException) { if (affinity.Group > 0) throw new ArgumentOutOfRangeException("affinity.Group"); var previous = (ulong)NativeMethods.SetThreadAffinityMask( currentThread, uIntPtrMask); return new GroupAffinity(0, previous); } } } private static class NativeMethods { private const string KERNEL = "kernel32.dll"; [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] public static extern UIntPtr SetThreadAffinityMask(IntPtr handle, UIntPtr mask); [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] public static extern IntPtr GetCurrentThread(); [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] public static extern ushort GetActiveProcessorGroupCount(); [StructLayout(LayoutKind.Sequential, Pack = 4)] public struct GROUP_AFFINITY { public UIntPtr Mask; [MarshalAs(UnmanagedType.U2)] public ushort Group; [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3, ArraySubType = UnmanagedType.U2)] public ushort[] Reserved; } [DllImport(KERNEL, CallingConvention = CallingConvention.Winapi)] public static extern bool SetThreadGroupAffinity( IntPtr thread, ref GROUP_AFFINITY groupAffinity, out GROUP_AFFINITY previousGroupAffinity); private const string LIBC = "libc"; [DllImport(LIBC)] public static extern int sched_getaffinity(int pid, IntPtr maskSize, ref ulong mask); [DllImport(LIBC)] public static extern int sched_setaffinity(int pid, IntPtr maskSize, ref ulong mask); } } }