summaryrefslogtreecommitdiff
path: root/src/gc/sample/gcenv.windows.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gc/sample/gcenv.windows.cpp')
-rw-r--r--src/gc/sample/gcenv.windows.cpp453
1 files changed, 453 insertions, 0 deletions
diff --git a/src/gc/sample/gcenv.windows.cpp b/src/gc/sample/gcenv.windows.cpp
new file mode 100644
index 0000000000..76187f2185
--- /dev/null
+++ b/src/gc/sample/gcenv.windows.cpp
@@ -0,0 +1,453 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+//
+// Implementation of the GC environment
+//
+
+#include "common.h"
+
+#include "windows.h"
+
+#include "gcenv.h"
+#include "gc.h"
+
+MethodTable * g_pFreeObjectMethodTable;
+
+int32_t g_TrapReturningThreads;
+
+bool g_fFinalizerRunOnShutDown;
+
+GCSystemInfo g_SystemInfo;
+
+static LARGE_INTEGER g_performanceFrequency;
+
+// Initialize the interface implementation
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::Initialize()
+{
+ if (!::QueryPerformanceFrequency(&g_performanceFrequency))
+ {
+ return false;
+ }
+
+ SYSTEM_INFO systemInfo;
+ GetSystemInfo(&systemInfo);
+
+ g_SystemInfo.dwNumberOfProcessors = systemInfo.dwNumberOfProcessors;
+ g_SystemInfo.dwPageSize = systemInfo.dwPageSize;
+ g_SystemInfo.dwAllocationGranularity = systemInfo.dwAllocationGranularity;
+
+ return true;
+}
+
+// Shutdown the interface implementation
+void GCToOSInterface::Shutdown()
+{
+}
+
+// Get numeric id of the current thread if possible on the
+// current platform. It is indended for logging purposes only.
+// Return:
+// Numeric id of the current thread or 0 if the
+uint64_t GCToOSInterface::GetCurrentThreadIdForLogging()
+{
+ return ::GetCurrentThreadId();
+}
+
+// Get id of the process
+// Return:
+// Id of the current process
+uint32_t GCToOSInterface::GetCurrentProcessId()
+{
+ return ::GetCurrentProcessId();
+}
+
+// Set ideal affinity for the current thread
+// Parameters:
+// affinity - ideal processor affinity for the thread
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::SetCurrentThreadIdealAffinity(GCThreadAffinity* affinity)
+{
+ bool success = true;
+
+#if !defined(FEATURE_CORESYSTEM)
+ SetThreadIdealProcessor(GetCurrentThread(), (DWORD)affinity->Processor);
+#else
+ PROCESSOR_NUMBER proc;
+
+ if (affinity->Group != -1)
+ {
+ proc.Group = (WORD)affinity->Group;
+ proc.Number = (BYTE)affinity->Processor;
+ proc.Reserved = 0;
+
+ success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
+ }
+ else
+ {
+ if (GetThreadIdealProcessorEx(GetCurrentThread(), &proc))
+ {
+ proc.Number = affinity->Processor;
+ success = !!SetThreadIdealProcessorEx(GetCurrentThread(), &proc, NULL);
+ }
+ }
+#endif
+
+ return success;
+}
+
+// Get the number of the current processor
+uint32_t GCToOSInterface::GetCurrentProcessorNumber()
+{
+ _ASSERTE(GCToOSInterface::CanGetCurrentProcessorNumber());
+ return ::GetCurrentProcessorNumber();
+}
+
+// Check if the OS supports getting current processor number
+bool GCToOSInterface::CanGetCurrentProcessorNumber()
+{
+ return true;
+}
+
+// Flush write buffers of processors that are executing threads of the current process
+void GCToOSInterface::FlushProcessWriteBuffers()
+{
+ ::FlushProcessWriteBuffers();
+}
+
+// Break into a debugger
+void GCToOSInterface::DebugBreak()
+{
+ ::DebugBreak();
+}
+
+// Get number of logical processors
+uint32_t GCToOSInterface::GetLogicalCpuCount()
+{
+ return g_SystemInfo.dwNumberOfProcessors;
+}
+
+// Causes the calling thread to sleep for the specified number of milliseconds
+// Parameters:
+// sleepMSec - time to sleep before switching to another thread
+void GCToOSInterface::Sleep(uint32_t sleepMSec)
+{
+ ::Sleep(sleepMSec);
+}
+
+// Causes the calling thread to yield execution to another thread that is ready to run on the current processor.
+// Parameters:
+// switchCount - number of times the YieldThread was called in a loop
+void GCToOSInterface::YieldThread(uint32_t switchCount)
+{
+ SwitchToThread();
+}
+
+// Reserve virtual memory range.
+// Parameters:
+// address - starting virtual address, it can be NULL to let the function choose the starting address
+// size - size of the virtual memory range
+// alignment - requested memory alignment
+// flags - flags to control special settings like write watching
+// Return:
+// Starting virtual address of the reserved range
+void* GCToOSInterface::VirtualReserve(void* address, size_t size, size_t alignment, uint32_t flags)
+{
+ DWORD memFlags = (flags & VirtualReserveFlags::WriteWatch) ? (MEM_RESERVE | MEM_WRITE_WATCH) : MEM_RESERVE;
+ return ::VirtualAlloc(0, size, memFlags, PAGE_READWRITE);
+}
+
+// Release virtual memory range previously reserved using VirtualReserve
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualRelease(void* address, size_t size)
+{
+ UNREFERENCED_PARAMETER(size);
+ return !!::VirtualFree(address, 0, MEM_RELEASE);
+}
+
+// Commit virtual memory range. It must be part of a range reserved using VirtualReserve.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualCommit(void* address, size_t size)
+{
+ return ::VirtualAlloc(address, size, MEM_COMMIT, PAGE_READWRITE) != NULL;
+}
+
+// Decomit virtual memory range.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualDecommit(void* address, size_t size)
+{
+ return !!::VirtualFree(address, size, MEM_DECOMMIT);
+}
+
+// Reset virtual memory range. Indicates that data in the memory range specified by address and size is no
+// longer of interest, but it should not be decommitted.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+// unlock - true if the memory range should also be unlocked
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::VirtualReset(void * address, size_t size, bool unlock)
+{
+ bool success = ::VirtualAlloc(address, size, MEM_RESET, PAGE_READWRITE) != NULL;
+ if (success && unlock)
+ {
+ // Remove the page range from the working set
+ ::VirtualUnlock(address, size);
+ }
+
+ return success;
+}
+
+// Check if the OS supports write watching
+bool GCToOSInterface::SupportsWriteWatch()
+{
+ return false;
+}
+
+// Reset the write tracking state for the specified virtual memory range.
+// Parameters:
+// address - starting virtual address
+// size - size of the virtual memory range
+void GCToOSInterface::ResetWriteWatch(void* address, size_t size)
+{
+}
+
+// Retrieve addresses of the pages that are written to in a region of virtual memory
+// Parameters:
+// resetState - true indicates to reset the write tracking state
+// address - starting virtual address
+// size - size of the virtual memory range
+// pageAddresses - buffer that receives an array of page addresses in the memory region
+// pageAddressesCount - on input, size of the lpAddresses array, in array elements
+// on output, the number of page addresses that are returned in the array.
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::GetWriteWatch(bool resetState, void* address, size_t size, void** pageAddresses, uintptr_t* pageAddressesCount)
+{
+ return false;
+}
+
+// Get size of the largest cache on the processor die
+// Parameters:
+// trueSize - true to return true cache size, false to return scaled up size based on
+// the processor architecture
+// Return:
+// Size of the cache
+size_t GCToOSInterface::GetLargestOnDieCacheSize(bool trueSize)
+{
+ // TODO: implement
+ return 0;
+}
+
+// Get affinity mask of the current process
+// Parameters:
+// processMask - affinity mask for the specified process
+// systemMask - affinity mask for the system
+// Return:
+// true if it has succeeded, false if it has failed
+// Remarks:
+// A process affinity mask is a bit vector in which each bit represents the processors that
+// a process is allowed to run on. A system affinity mask is a bit vector in which each bit
+// represents the processors that are configured into a system.
+// A process affinity mask is a subset of the system affinity mask. A process is only allowed
+// to run on the processors configured into a system. Therefore, the process affinity mask cannot
+// specify a 1 bit for a processor when the system affinity mask specifies a 0 bit for that processor.
+bool GCToOSInterface::GetCurrentProcessAffinityMask(uintptr_t* processMask, uintptr_t* systemMask)
+{
+ return false;
+}
+
+// Get number of processors assigned to the current process
+// Return:
+// The number of processors
+uint32_t GCToOSInterface::GetCurrentProcessCpuCount()
+{
+ return g_SystemInfo.dwNumberOfProcessors;
+}
+
+// Return the size of the user-mode portion of the virtual address space of this process.
+// Return:
+// non zero if it has succeeded, 0 if it has failed
+size_t GCToOSInterface::GetVirtualMemoryLimit()
+{
+ MEMORYSTATUSEX memStatus;
+
+ memStatus.dwLength = sizeof(MEMORYSTATUSEX);
+ BOOL fRet = GlobalMemoryStatusEx(&memStatus);
+ _ASSERTE(fRet);
+
+ return (size_t)memStatus.ullTotalVirtual;
+}
+
+// Get the physical memory that this process can use.
+// Return:
+// non zero if it has succeeded, 0 if it has failed
+uint64_t GCToOSInterface::GetPhysicalMemoryLimit()
+{
+ MEMORYSTATUSEX memStatus;
+
+ memStatus.dwLength = sizeof(MEMORYSTATUSEX);
+ BOOL fRet = GlobalMemoryStatusEx(&memStatus);
+ _ASSERTE(fRet);
+
+ return memStatus.ullTotalPhys;
+}
+
+// Get memory status
+// Parameters:
+// memory_load - A number between 0 and 100 that specifies the approximate percentage of physical memory
+// that is in use (0 indicates no memory use and 100 indicates full memory use).
+// available_physical - The amount of physical memory currently available, in bytes.
+// available_page_file - The maximum amount of memory the current process can commit, in bytes.
+void GCToOSInterface::GetMemoryStatus(uint32_t* memory_load, uint64_t* available_physical, uint64_t* available_page_file)
+{
+ MEMORYSTATUSEX memStatus;
+
+ memStatus.dwLength = sizeof(MEMORYSTATUSEX);
+ BOOL fRet = GlobalMemoryStatusEx(&memStatus);
+ _ASSERTE (fRet);
+
+ // If the machine has more RAM than virtual address limit, let us cap it.
+ // The GC can never use more than virtual address limit.
+ if (memStatus.ullAvailPhys > memStatus.ullTotalVirtual)
+ {
+ memStatus.ullAvailPhys = memStatus.ullAvailVirtual;
+ }
+
+ if (memory_load != NULL)
+ *memory_load = memStatus.dwMemoryLoad;
+ if (available_physical != NULL)
+ *available_physical = memStatus.ullAvailPhys;
+ if (available_page_file != NULL)
+ *available_page_file = memStatus.ullAvailPageFile;
+}
+
+// Get a high precision performance counter
+// Return:
+// The counter value
+int64_t GCToOSInterface::QueryPerformanceCounter()
+{
+ LARGE_INTEGER ts;
+ if (!::QueryPerformanceCounter(&ts))
+ {
+ _ASSERTE(!"Fatal Error - cannot query performance counter.");
+ abort();
+ }
+
+ return ts.QuadPart;
+}
+
+// Get a frequency of the high precision performance counter
+// Return:
+// The counter frequency
+int64_t GCToOSInterface::QueryPerformanceFrequency()
+{
+ return g_performanceFrequency.QuadPart;
+}
+
+// Get a time stamp with a low precision
+// Return:
+// Time stamp in milliseconds
+uint32_t GCToOSInterface::GetLowPrecisionTimeStamp()
+{
+ return ::GetTickCount();
+}
+
+// Parameters of the GC thread stub
+struct GCThreadStubParam
+{
+ GCThreadFunction GCThreadFunction;
+ void* GCThreadParam;
+};
+
+// GC thread stub to convert GC thread function to an OS specific thread function
+static DWORD __stdcall GCThreadStub(void* param)
+{
+ GCThreadStubParam *stubParam = (GCThreadStubParam*)param;
+ GCThreadFunction function = stubParam->GCThreadFunction;
+ void* threadParam = stubParam->GCThreadParam;
+
+ delete stubParam;
+
+ function(threadParam);
+
+ return 0;
+}
+
+// Create a new thread
+// Parameters:
+// function - the function to be executed by the thread
+// param - parameters of the thread
+// affinity - processor affinity of the thread
+// Return:
+// true if it has succeeded, false if it has failed
+bool GCToOSInterface::CreateThread(GCThreadFunction function, void* param, GCThreadAffinity* affinity)
+{
+ DWORD thread_id;
+
+ GCThreadStubParam* stubParam = new (nothrow) GCThreadStubParam();
+ if (stubParam == NULL)
+ {
+ return false;
+ }
+
+ stubParam->GCThreadFunction = function;
+ stubParam->GCThreadParam = param;
+
+ HANDLE gc_thread = ::CreateThread(NULL, 0, GCThreadStub, stubParam, CREATE_SUSPENDED, &thread_id);
+
+ if (!gc_thread)
+ {
+ delete stubParam;
+ return false;
+ }
+
+ SetThreadPriority(gc_thread, /* THREAD_PRIORITY_ABOVE_NORMAL );*/ THREAD_PRIORITY_HIGHEST );
+
+ ResumeThread(gc_thread);
+
+ CloseHandle(gc_thread);
+
+ return true;
+}
+
+// Initialize the critical section
+void CLRCriticalSection::Initialize()
+{
+ ::InitializeCriticalSection(&m_cs);
+}
+
+// Destroy the critical section
+void CLRCriticalSection::Destroy()
+{
+ ::DeleteCriticalSection(&m_cs);
+}
+
+// Enter the critical section. Blocks until the section can be entered.
+void CLRCriticalSection::Enter()
+{
+ ::EnterCriticalSection(&m_cs);
+}
+
+// Leave the critical section
+void CLRCriticalSection::Leave()
+{
+ ::LeaveCriticalSection(&m_cs);
+}