//////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Windows.cs // // Note: P/Invoke definitions for manipulating threads, processes, and jobs. // using System; using System.Diagnostics; using System.ComponentModel; using System.Runtime.InteropServices; // This only works on kernels later than Vista, XP64, and W2003S. // Notably, it does not work on XP 32-bit. So, we can't learn which // processors are virtual processors (HyperThreading), so we can't // make sure that we schedule jobs only on the main virtual processor // of each physical core yet. The reason to do this is because // HyperThreading really doesn't work for CPU-bound tasks; in nearly // all tests, running CPU-bound tasks on both virtual processors of // a single hyper-threaded core results in *worse* performance, largely // due to the increased cache pressure. // // #define ENABLE_LOGICAL_PROCESSOR_INFORMATION namespace Windows { public class Kernel32 { const string DllName = "KERNEL32.DLL"; [DllImport(DllName, CharSet = CharSet.Unicode)] static extern IntPtr CreateJobObject( IntPtr SecurityAttributes, string Name); public static IntPtr CreateJobObject() { IntPtr handle = CreateJobObject(IntPtr.Zero, null); if (handle == IntPtr.Zero || handle == ((IntPtr)~0)) { int error = Marshal.GetLastWin32Error(); throw new Exception("The job object could not be created.", new Win32Exception(error)); } return handle; } [DllImport(DllName, SetLastError = true)] public static extern bool CloseHandle(IntPtr ObjectHandle); [DllImport(DllName, SetLastError = true)] public static extern bool AssignProcessToJobObject( IntPtr JobHandle, IntPtr ProcessHandle); [DllImport(DllName, SetLastError=true)] static unsafe extern bool QueryInformationJobObject( IntPtr JobHandle, JobObjectInformationClass InformationClass, void* Buffer, int BufferLength, out int ReturnLength); public static JOBOBJECT_BASIC_ACCOUNTING_INFORMATION QueryJobObjectBasicAccountingInformation(IntPtr JobHandle) { unsafe { JOBOBJECT_BASIC_ACCOUNTING_INFORMATION buffer; int length; if (!QueryInformationJobObject( JobHandle, JobObjectInformationClass.Accounting, &buffer, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), out length)) { int error = Marshal.GetLastWin32Error(); throw new Exception("Failed to query information from job object.", new Win32Exception(error)); } return buffer; } } public static bool IsCurrentProcessInJob() { unsafe { JOBOBJECT_BASIC_ACCOUNTING_INFORMATION information = new JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(); int returnLength = 0; if (QueryInformationJobObject(IntPtr.Zero, JobObjectInformationClass.Accounting, &information, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), out returnLength)) { return true; } else { return false; } } } [DllImport(DllName, SetLastError = true, CharSet=CharSet.Unicode)] static unsafe extern int FormatMessage( FormatMessageFlag flags, void* Source, int MessageId, int LanguageId, void* Buffer, int BufferLength, void* Arguments); public static unsafe string FormatMessageFromSystem(int messageId, int languageId) { char* buffer = null; int length = FormatMessage( FormatMessageFlag.AllocateBuffer | FormatMessageFlag.FromSystem, null, messageId, languageId, (void*)&buffer, 0, null); if (length == 0) return null; string result = new String(buffer, 0, length); Marshal.FreeHGlobal((IntPtr)(void*)buffer); return result; } public unsafe static string FormatMessageFromSystem(int messageId) { return FormatMessageFromSystem(messageId, 0); } public static string GetErrorText(int error) { string text = FormatMessageFromSystem(error); if (text == null) { if (error < 0x10000 && error >= 0) return String.Format("Unknown error: {0]", error); else return String.Format("Unknown error: 0x{0:x}", error); } else { return text; } } [DllImport(DllName, SetLastError = true)] public static extern uint SetThreadIdealProcessor( IntPtr ThreadHandle, int IdealProcessor); [DllImport(DllName, SetLastError = true)] public static extern bool GetProcessAffinityMask( IntPtr ProcessHandle, out UIntPtr ProcessAffinityMask, out UIntPtr SystemAffinityMask); [DllImport(DllName)] public static extern IntPtr GetCurrentThread(); [DllImport(DllName)] public static extern IntPtr GetCurrentProcess(); [DllImport(DllName, SetLastError = true)] public static extern bool GetThreadTimes( IntPtr hThread, out /* FILETIME */ long lpCreationTime, out /* FILETIME */ long lpExitTime, out /* FILETIME */ long lpKernelTime, out /* FILETIME */ long lpUserTime ); public void GetThreadTimes( IntPtr ThreadHandle, out DateTime creationTime, out DateTime exitTime, out TimeSpan kernelTime, out TimeSpan userTime) { long ticks_creationTime; long ticks_exitTime; long ticks_kernelTime; long ticks_userTime; if (GetThreadTimes(ThreadHandle, out ticks_creationTime, out ticks_exitTime, out ticks_kernelTime, out ticks_userTime)) { creationTime = new DateTime(ticks_creationTime); exitTime = new DateTime(ticks_exitTime); kernelTime = new TimeSpan(ticks_kernelTime); userTime = new TimeSpan(ticks_userTime); } else { throw new Exception("The thread times could not be retrieved.", new Win32Exception(Marshal.GetLastWin32Error())); } } public static int GetProcessorCount() { UIntPtr processAffinityMask; UIntPtr systemAffinityMask; if (!GetProcessAffinityMask(GetCurrentProcess(), out processAffinityMask, out systemAffinityMask)) { Console.WriteLine("nib: Failed to get process affinity mask! Assuming 1 processor."); return 1; } ulong mask = (ulong)processAffinityMask; int count = 0; while (mask != 0) { if ((mask & 1) != 0) count++; mask >>= 1; } if (count == 0) return 1; Console.WriteLine("nib: Found {0} processors.", count); return count; } public static string GetLastErrorText() { int error = Marshal.GetLastWin32Error(); return GetErrorText(error); } #if ENABLE_LOGICAL_PROCESSOR_INFORMATION [DllImport(DllName, SetLastError=true)] static unsafe extern bool GetLogicalProcessorInformation( SYSTEM_LOGICAL_PROCESSOR_INFORMATION* buffer, ref int length); public static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] GetLogicalProcessorInformation() { unsafe { int length = 0; GetLogicalProcessorInformation(null, ref length); if (length == 0) return new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[0]; int count = length / sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION); SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] array = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[count]; bool result; fixed (SYSTEM_LOGICAL_PROCESSOR_INFORMATION* array_pinned = &array[0]) { int length2 = sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION) * count; result = GetLogicalProcessorInformation(array_pinned, ref length2); } if (result) return array; else throw new Exception("Failed to query logical processor information.", new Win32Exception(Marshal.GetLastWin32Error())); } } public static int[] GetPhysicalProcessorList() { SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] logical_info = GetLogicalProcessorInformation(); for (int i = 0; i < logical_info.Length; i++) { Debug.WriteLine(logical_info.ToString()); } return null; } #endif } [Flags] enum FormatMessageFlag { AllocateBuffer = 0x100, FromSystem = 0x1000, FromModule = 0x800, } public enum JobObjectInformationClass { Accounting = 1, // JOBOBJECT_BASIC_ACCOUNTING_INFORMATION } [StructLayout(LayoutKind.Sequential)] public struct JOBOBJECT_BASIC_ACCOUNTING_INFORMATION { public TimeSpan TotalUserTime; public TimeSpan TotalKernelTime; public TimeSpan ThisPeriodTotalUserTime; public TimeSpan ThisPeriodTotalKernelTime; public uint TotalPageFaultCount; public uint TotalProcesses; public uint ActiveProcesses; public uint TotalTerminatedProcesses; } #if ENABLE_LOGICAL_PROCESSOR_INFORMATION [StructLayout(LayoutKind.Sequential)] struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION { public UIntPtr ProcessorMask; public LOGICAL_PROCESSOR_RELATIONSHIP Relationship; // //union { // struct // { // BYTE Flags; // } ProcessorCore; // struct // { // DWORD NodeNumber; // } NumaNode; // CACHE_DESCRIPTOR Cache; // ULONGLONG Reserved[2]; //} // public ProcessorRelationUnion RelationUnion; } [StructLayout(LayoutKind.Explicit)] struct ProcessorRelationUnion { [FieldOffset(0)] public CACHE_DESCRIPTOR Cache; [FieldOffset(0)] public uint NumaNodeNumber; [FieldOffset(0)] public byte ProcessorCoreFlags; } [StructLayout(LayoutKind.Sequential)] struct CACHE_DESCRIPTOR { public byte Level; public byte Associativity; public ushort LineSize; public uint Size; public PROCESSOR_CACHE_TYPE Type; } enum PROCESSOR_CACHE_TYPE { Unified = 0, Instruction = 1, Data = 2, Trace = 3, } enum LOGICAL_PROCESSOR_RELATIONSHIP : uint { ProcessorCore = 0, NumaNode = 1, RelationCache = 2, } #endif }