// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// SchedulingNode.h
//
// Source file containing the SchedulingNode declaration.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
namespace Concurrency
{
namespace details
{
///
/// A scheduling node corresponds to a NUMA node or a processor package; containing one or more virtual processor groups.
///
class SchedulingNode
{
public:
///
/// Constructs a scheduling node.
///
SchedulingNode(const QuickBitSet& resourceSet, DWORD numaNodeNumber, SchedulingRing *pRing);
///
/// Destroys a scheduling node.
///
~SchedulingNode();
///
/// Creates and adds a new virtual processor in the node to associated with the root provided.
/// NOTE: For non-oversubscribed vprocs this API is currently will only work for initial allocation.
///
///
/// The virtual processor root to create the virtual processor with.
///
///
/// True if this is an oversubscribed virtual processor.
///
///
/// The newly created virtual processor.
///
VirtualProcessor* AddVirtualProcessor(IVirtualProcessorRoot *pOwningRoot, bool fOversubscribed = false);
///
/// Returns the scheduler associated with the node.
///
SchedulerBase * GetScheduler() { return m_pScheduler; }
///
/// Returns the scheduling ring associated with the node.
///
SchedulingRing * GetSchedulingRing() { return m_pRing; }
///
/// Find the virtual processor in this node that matches the root provided.
///
///
/// The virtual processor root to match.
///
VirtualProcessor* FindMatchingVirtualProcessor(IVirtualProcessorRoot* pRoot);
///
/// Returns the ID of the scheduling node.
///
int Id() const
{
return m_id;
}
///
/// Returns the first virtual processor in the non-cyclic range [min, max). If such is found
/// the virtual processor is returned and pIdx contains its index within the list array.
/// If not found, NULL is returned and the value in pIdx is unspecified.
///
VirtualProcessor *FindVirtualProcessor(int min, int max, int *pIdx)
{
VirtualProcessor *pVProc = NULL;
int i = min;
for (; i < max && pVProc == NULL; ++i)
{
pVProc = m_virtualProcessors[i];
}
//
// The loop incremented "i" prior to the check. If found, the index is i - 1. If not, we care
// not what pIdx contains.
//
*pIdx = i - 1;
return pVProc;
}
///
/// Returns the first virtual processor.
///
///
/// The iterator position of the returned virtual processor will be placed here. This can only be
/// utilized as the pIdx parameter or the idxStart parameter of a GetNextVirtualProcessor.
///
VirtualProcessor *GetFirstVirtualProcessor(int *pIdx)
{
return FindVirtualProcessor(0, m_virtualProcessors.MaxIndex(), pIdx);
}
///
/// Returns the next virtual processor in an iteration.
///
VirtualProcessor *GetNextVirtualProcessor(int *pIdx, int idxStart = 0)
{
VirtualProcessor *pVProc = NULL;
int min = *pIdx + 1;
if (min > idxStart)
{
pVProc = FindVirtualProcessor(min, m_virtualProcessors.MaxIndex(), pIdx);
min = 0;
}
if (pVProc == NULL)
pVProc = FindVirtualProcessor(min, idxStart, pIdx);
return pVProc;
}
///
/// Returns whether a virtual processor is available.
///
bool HasVirtualProcessorAvailable() const
{
return m_virtualProcessorAvailableCount > 0;
}
///
/// Returns whether a virtual processor is waiting for throttling.
///
bool HasVirtualProcessorPendingThread() const
{
return m_virtualProcessorsPendingThreadCreate > 0;
}
///
/// Returns whether a virtual processor is available to execute new work.
///
bool HasVirtualProcessorAvailableForNewWork() const
{
//
// The observational race (lack of atomicity between the two reads) should not matter. If it does in some obscure
// case, a new atomic counter can be added.
//
return (m_virtualProcessorAvailableCount - m_virtualProcessorsPendingThreadCreate) > 0;
}
///
/// Gets a location object which represents the scheduling node.
///
location GetLocation();
///
/// Returns a virtual processor from the given location. The virtual processor must be within this node.
///
VirtualProcessor* FindVirtualProcessorByLocation(const location* pLoc);
///
/// Determines whether the scheduling node contains an execution resource with ID as specified. Note that this does NOT return
/// whether the said resource is available in the scheduler -- only whether the given resource ID is logically contained in the
/// node. The scheduler may have no virtual processor with that execution resource ID at the moment.
///
bool ContainsResourceId(unsigned int resourceId) /*const*/
{
return m_resourceBitMap.Exists(resourceId);
}
///
/// Notifies the node of a resource that is contained within it and its assigned position in all bitmasks used by all ConcRT
/// schedulers.
///
void NotifyResource(unsigned int resourceId, unsigned int maskId)
{
m_resourceBitMap.Insert(resourceId, maskId);
}
///
/// Returns the bitset for all resources in the node.
///
const QuickBitSet& GetResourceSet()
{
return m_resourceSet;
}
///
/// Gets the NUMA node to which this scheduling node belongs.
///
DWORD GetNumaNodeNumber() const
{
return m_numaNodeNumber;
}
private:
friend class SchedulerBase;
friend class VirtualProcessor;
friend class InternalContextBase;
friend class FairScheduleGroup;
template friend class ListArray;
// Owning scheduler
SchedulerBase *m_pScheduler;
// Owning ring
SchedulingRing * const m_pRing;
// The bit-set identifying execution resources within this node for quick affinity masking.
QuickBitSet m_resourceSet;
// Maps resource IDs contained within the node to a mask identifier
Hash m_resourceBitMap;
volatile LONG m_virtualProcessorAvailableCount;
volatile LONG m_virtualProcessorsPendingThreadCreate;
volatile LONG m_virtualProcessorCount;
volatile LONG m_ramblingCount; // rambling -- searching foreign nodes for work
DWORD m_numaNodeNumber;
int m_id;
// Virtual processors owned by this node.
ListArray m_virtualProcessors;
InternalContextBase *StealLocalRunnableContext(VirtualProcessor* pSkipVirtualProcessor = NULL);
///
/// Find an available virtual processor in the scheduling node.
///
bool FoundAvailableVirtualProcessor(VirtualProcessor::ClaimTicket& ticket,
location bias = location(),
ULONG type = VirtualProcessor::AvailabilityAny);
void Cleanup();
// Prevent warning about generated assignment operator & copy constructors.
SchedulingNode(const SchedulingNode&);
void operator=(const SchedulingNode&);
};
} // namespace details
} // namespace Concurrency