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
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
SELECT METHODS WHERE IsDirectlyUsing "WindowsAPIGlobalMembers.GetLogicalProcessorInformation(PSYSTEM_LOGICAL_PROCESSOR_INFORMATION,PDWORD)"
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
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
Again, the Scheduler communicates with resource manager using the
IScheduleProxy interface to enforces low coupling, and the concrete implemenatation of this interface invoke
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.
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
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
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
SchedulerProxy, but an array of
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.
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
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
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