📄 resourcemanager.cpp
字号:
}
else if (::Concurrency::GetOSVersion() == ::Concurrency::IResourceManager::XP)
{
// For XP, we assume everything is on one node, so affinity plays no role.
if (pCore != NULL)
{
*pCore = 0;
}
return 0;
}
else
{
throw ::Concurrency::unsupported_os();
}
}
/// <summary>
/// Creates the static singleton instance of the Resource Manager.
/// </summary>
ResourceManager* ResourceManager::CreateSingleton()
{
ResourceManager *pRM = NULL;
{ // begin locked region
_StaticLock::_Scoped_lock lock(s_lock);
if (s_pResourceManager == NULL)
{
pRM = new ResourceManager();
pRM->Reference();
s_pResourceManager = (ResourceManager*) Security::EncodePointer(pRM);
}
else
{
pRM = (ResourceManager*) Security::DecodePointer(s_pResourceManager);
if ( !pRM->SafeReference())
{
// pRM has refcnt=0 and will be deleted as soon as the lock is released
pRM = new ResourceManager();
pRM->Reference();
s_pResourceManager = (ResourceManager*) Security::EncodePointer(pRM);
}
}
} // end locked region
return pRM;
}
/// <summary>
/// Constructs the resource manager.
/// </summary>
ResourceManager::ResourceManager() :
m_referenceCount(0L),
m_hDynamicRMThreadHandle(NULL),
m_hDynamicRMEvent(NULL),
m_dynamicRMWorkerState(Standby),
m_maxSchedulers(16),
m_numSchedulersNeedingNotifications(0),
m_allocationRound(0),
m_ppProxyData(NULL),
m_ppGivingProxies(NULL),
m_ppReceivingProxies(NULL),
m_numSchedulers(0),
m_pGlobalNodes(NULL),
m_pSortedNodeOrder(NULL),
m_pUMSPoller(NULL),
m_pTransmogrificator(NULL)
{
PFnFlushProcessWriteBuffers pfnFunction = (PFnFlushProcessWriteBuffers)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
"FlushProcessWriteBuffers");
if (pfnFunction != NULL)
{
m_pfnFlushProcessWriteBuffers = (PFnFlushProcessWriteBuffers) Security::EncodePointer(pfnFunction);
m_pPageVirtualProtect = NULL;
}
else
{
m_pfnFlushProcessWriteBuffers = NULL;
// If we're unable to find the FlushProcessorWriteBuffers entry point in kernel32.dll, we're likely on an OS
// with version < 6000. We need to use a different mechanism to enable write buffer flushing.
// Start by allocating a commited region of memory the size of a page.
m_pPageVirtualProtect = (char *) VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (m_pPageVirtualProtect == NULL)
{
throw std::bad_alloc();
}
// We expect the OS to give us an allocation starting at a page boundary.
ASSERT(((ULONG_PTR)m_pPageVirtualProtect & 0xFFF) == 0);
*m_pPageVirtualProtect = 1;
}
// Initialize static information about the system asking for the the topology information to be saved
// so we can use it right after.
InitializeSystemInformation(true);
DetermineTopology();
// The dynamic RM thread is not created up front. It is created when the number of schedulers go from 1 to 2
// In addition, once it is created, it is placed on standby if the number of schedulers goes down to 1 again.
// The event is created here, since it could be signaled even before the thread is created.
m_hDynamicRMEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
// Allocate common memory used for static and dynamic allocation. Buffers specific to dynamic core migration
// are only allocated when the DRM thread is started up.
m_ppProxyData = new AllocationData * [m_maxSchedulers];
#if defined(CONCRT_TRACING)
// Assumes a m x n allocation.
m_numTotalCores = m_nodeCount * m_pGlobalNodes[0].m_coreCount;
m_drmInitialState = new GlobalCoreData[m_numTotalCores];
memset(m_drmInitialState, 0, sizeof(GlobalCoreData) * m_numTotalCores);
// Maintains a trace for every core removed during preprocessing.
memset(m_preProcessTraces, 0, sizeof(PreProcessingTraceData) * 100);
m_preProcessTraceIndex = 0;
// Maintains a trace for each core allocation or assignment.
memset(m_dynAllocationTraces, 0, sizeof(DynamicAllocationTraceData) * 100);
m_dynAllocationTraceIndex = 0;
#endif
}
/// <summary>
/// This API is called by the dynamic RM worker thread when it starts up, and right after its state changed to
/// LoadBalance after being on Standby for a while. We need to find the existing schedulers, and discard the
/// statistics they have collected so far if any. Either we've never collected statistics for this scheduler before,
/// or too much/too little time has passed since we last collected statistics, and this information cannot be trusted.
/// </summary>
void ResourceManager::DiscardExistingSchedulerStatistics()
{
// NOTE: This routine must be called while holding m_lock.
ASSERT(m_numSchedulers > 1);
ASSERT(m_dynamicRMWorkerState == LoadBalance);
SchedulerProxy * pSchedulerProxy = NULL;
for (pSchedulerProxy = m_schedulers.First(); pSchedulerProxy != NULL; pSchedulerProxy = m_schedulers.Next(pSchedulerProxy))
{
// Initialize variables needed for statistics.
unsigned int taskCompletionRate = 0, taskArrivalRate = 0;
// Get the stored scheduler queue length.
unsigned int numberOfTasksEnqueued = pSchedulerProxy->GetQueueLength();
// Collect statistical information about this scheduler.
pSchedulerProxy->Scheduler()->Statistics(&taskCompletionRate, &taskArrivalRate, &numberOfTasksEnqueued);
// Update the queue length using the number computed by the statistics.
pSchedulerProxy->SetQueueLength(numberOfTasksEnqueued);
}
}
/// <summary>
/// Creates the dynamic RM worker thread and allocates memory for its use. The worker thread wakes up at
/// fixed intervals and load balances resources among schedulers, until it it put on standby.
/// </summary>
void ResourceManager::CreateDynamicRMWorker()
{
// NOTE: This routine is called *without* holding m_lock.
// Set up a background thread for dynamic RM.
m_hDynamicRMThreadHandle = LoadLibraryAndCreateThread(NULL,
DEFAULTCONTEXTSTACKSIZE,
DynamicRMThreadProc,
this,
0,
NULL);
if (m_hDynamicRMThreadHandle == NULL)
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
// Make sure the background thread is running at the highest priority.
if (SetThreadPriority(m_hDynamicRMThreadHandle, THREAD_PRIORITY_TIME_CRITICAL) == 0)
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
}
/// <summary>
/// Initialize function pointers that are only present on specific versions of the OS (Win7 or higher)
/// <summary>
void ResourceManager::InitializeSystemFunctionPointers()
{
ASSERT(s_version == ::Concurrency::IResourceManager::Win7OrLater || s_version == ::Concurrency::IResourceManager::UmsThreadAwareOS);
HardwareAffinity::InitializeSetThreadGroupAffinityFn();
PFnGetCurrentProcessorNumberEx pfnFunction = (PFnGetCurrentProcessorNumberEx)GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
"GetCurrentProcessorNumberEx");
if (pfnFunction == NULL)
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
s_pfnGetCurrentProcessorNumberEx = (PFnGetCurrentProcessorNumberEx) Security::EncodePointer(pfnFunction);
}
/// <summary>
/// Initializes static information such as os version, number of nodes and cores.
/// </summary>
void ResourceManager::InitializeSystemInformation(bool fSaveTopologyInfo)
{
SYSTEM_INFO info;
GetSystemInfo(&info);
s_fNativeX64 = (info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
s_fRequireUMSWorkaround = false;
OSVERSIONINFOW osvi;
memset(&osvi, 0, sizeof(OSVERSIONINFOW));
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOW);
if( !GetVersionExW(&osvi))
{
s_version = ::Concurrency::IResourceManager::UnsupportedOS;
throw ::Concurrency::unsupported_os();
}
else
{
switch (osvi.dwMajorVersion)
{
case 5:
switch (osvi.dwMinorVersion)
{
case 2: // Win2k3 or XP-64
s_version = ::Concurrency::IResourceManager::Win2k3;
break;
case 1: // XP
s_version = ::Concurrency::IResourceManager::XP;
break;
case 0: // Win2k -- unsupported
s_version = ::Concurrency::IResourceManager::UnsupportedOS;
throw ::Concurrency::unsupported_os();
break;
}
break;
case 6: // Vista
switch (osvi.dwMinorVersion)
{
case 0:
s_version = ::Concurrency::IResourceManager::Vista;
break;
default: // OS with > 64 core support. Win7 is showing up as version 6.1
if (s_fNativeX64)
{
s_version = ::Concurrency::IResourceManager::UmsThreadAwareOS; // OS with UMS support
//
// There is a critical bug in Win7 RTM that we must work around. This will be addressed in the next kernel update.
// Key off 7600 as the RTM build to detect whether we need the workaround.
//
#if !_NO_WIN7_WORKAROUND
s_fRequireUMSWorkaround = (osvi.dwBuildNumber <= 7600);
#endif // !_NO_WIN7_WORKAROUND
}
else
{
s_version = ::Concurrency::IResourceManager::Win7OrLater;
}
}
break;
default:
if (osvi.dwMajorVersion >= 7)
{
s_version = ::Concurrency::IResourceManager::Win7OrLater;
if (s_fNativeX64)
{
s_version = ::Concurrency::IResourceManager::UmsThreadAwareOS; // OS with UMS support
}
}
else
{
s_version = ::Concurrency::IResourceManager::UnsupportedOS;
throw ::Concurrency::unsupported_os();
}
break;
}
}
ASSERT(s_pSysInfo == NULL);
// There is some ambiguity around the behavior of this API for OSs < Vista. Set node count to
// 1 for these OSs, as other areas of the implementation make assumptions around this.
if (s_version == ::Concurrency::IResourceManager::XP || s_version == ::Concurrency::IResourceManager::Win2k3)
{
s_packageCount = 1;
s_nodeCount = 1;
s_physicalProcessorCount = info.dwNumberOfProcessors;
}
else if (s_version == ::Concurrency::IResourceManager::Win7OrLater ||
s_version == ::Concurrency::IResourceManager::UmsThreadAwareOS)
{
typedef BOOL (WINAPI *PFnGetLogicalProcessorInformationEx)(LOGICAL_PROCESSOR_RELATIONSHIP, PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PDWORD);
PFnGetLogicalProcessorInformationEx pfnGetLogicalProcessorInformationEx
= (PFnGetLogicalProcessorInformationEx) GetProcAddress(GetModuleHandleW(L"kernel32.dll"),
"GetLogicalProcessorInformationEx");
if (pfnGetLogicalProcessorInformationEx == NULL)
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -