Resource Allocation Workflow
Step 1: Get Resources Information From the System
The ResourceManager needs all physical information concerning processors and cores when allocating resources for schedulers. Let's examines the data manipulated by this component.
SELECT FIELDS WHERE IsDirectlyUsedBy "Concurrency.details.ResourceManager"
These structs represent all data needed by ResourceManager. The basic ones are ProcessorCore and ProcessorNode representing information about physical resources. Other structs contains some scheduler data needed for the allocation algorithm.
The good news is that ResourceManager didn't directly access to fields of other logical classes used by it. Let's examine where ResourceManager gets basic information about physical resources. To do so, you can search for methods using GetLogicalProcessorInformation.
SELECT METHODS WHERE IsDirectlyUsing "WindowsAPIGlobalMembers.GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,PDWORD)"
Only ResourceManager::InitializeSystemInformation uses this method to get information from the system. So what method is responsible for populating ProcessorCore and ProcessorNode.
For that, you can search for methods assigning a field of ProcessorCore:
SELECT METHODS WHERE IsDirectlyWritingField "Concurrency.details.ProcessorCore.m_processorNumber"
So ResourceManager invokes the DetermineTopology method to populate data into structs. Here's a dependency graph showing methods concerned by getting nodes and cores information.
Step 2: Scheduler Requests for Resource Allocation
After ResourceManager gets all the data needed for allocation algorithm, a scheduler can ask it to allocate ressources. When the Scheduler is created, it asks for a ResourceManager and invoke CreateResourceManager to get the singleton. After that it invokes IResourceManager::RegisterScheduler to register it self with the resource manager, and get a pointer to ISchedulerProxy, and with this interface the Scheduler will interact with the resource manager. After that the Scheduler is ready to ask for resources allocation by invoking RequestInitialVirtualProcessors.
Again, the Scheduler communicates with resource manager using the IScheduleProxy interface to enforces low coupling, and the concrete implemenatation of this interface invoke ResourceManager::RequestInitialVirtualprocessors.
Step 3: Resource Allocation
The ResourceManager has to give resources to Schedulers when Schedulers are created. It does the allocation depending on the policies and taking into account the other schedulers in the process. Eventually the resources will be provided to the scheduler in need.
The entry point for allocating requests is RequestInitialVirtualProcessors. Here's a dependency graph showing methods used to acheive allocation.
The PerformAllocation method implements the allocation logic, it asks for Scheduler policies from SchedulerProxy, and invokes the AllocateCores method to perform allocation. Here are some characteristics of PerformAllocation and AllocateCores:
Those methods are well documented and the cyclomatic complexity is not high. This a good indicator that the algorithm of initial allocation is not very complicated.
Step 4: Giving Resources to Scheduler
After ResourceManager allocates resources, the scheduler is notified with resources allocated. For that, the ResourceManager invokes the GrantAllocation method.
What resources the ResourceManager have to give to Scheduler?
Well, the resource is a virtual processor associated to a specific thread and runing in a specific core. As shown in the dependency graph, the ResourceManager doesn't give VirtualProcessors to SchedulerProxy, but an array of SchedulerNodes. SchedulerProxy creates VirtualProcessorRoot classes, which represents an abstraction for a hardware thread on which a thread proxy can execute.
Who is creating VirtualProcessor? Before answering this question, let's search for classes that represent a virtual processor, starting with classes inheriting from this one.
So VirtualProcessor could concern a simple thread or an User-Mode Scheduling (UMS) thread -- a lightweight mechanism introduced in Windows7 and Windows Server 2008 R2, that applications can use to schedule their own threads. Let's search for methods creating ThreadVirtualProcessor.
So the Scheduler is the responsible of creating VirtualProcessors and all information needed to create it are given by the manager.
Step 5: Dynamic Resource Management
After the initial allocation of resources, the second main responsability of ResourceManager is constant monitoring of the utilization of resources by schedulers, and dynamically migrating resources between schedulers.
As shown in the following graph, the ResourceManager creates DynamicResourceManage thread. This worker thread wakes up at fixed intervals and load balances resources among schedulers. Here's the dependency graph of DynamicResourceManager:
Is ResourceManager customizable?
ResourceManager is well designed and is decoupled from other Concurrency Runtime framework by using interfaces, but it's possible to customize the allocation behavior of resource manager. For that let's search for ResourceManager virtual methods:
SELECT METHODS FROM TYPES "Concurrency.details.ResourceManager" WHERE IsVirtual
However, these methods are virtual and only implement the IResourceManager methods, so it's not possible to overload allocation resource manager logic and customize it.
Lahlali Issam is the developer of CppDepend code analysis tool for C/C++.
For more on CRT, see Concurrency Runtime (CRT): The Task Scheduler


