// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // UMSThreadVirtualProcessor.cpp // // Source file containing the UMSThreadVirtualProcessor implementation. // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- #include "concrtinternal.h" namespace Concurrency { namespace details { /// /// Construct a UMS virtual processor /// UMSThreadVirtualProcessor::UMSThreadVirtualProcessor(SchedulingNode *pOwningNode, IVirtualProcessorRoot *pOwningRoot) : m_pSchedulingContext(NULL) { // Defer to Initialize Initialize(pOwningNode, pOwningRoot); } /// /// Destroy the UMS virtual processor /// UMSThreadVirtualProcessor::~UMSThreadVirtualProcessor() { delete m_pSchedulingContext; } /// /// Initializes the virtual processor. This API is called by the constructor, and when a virtual processor is to /// be re-initialized, when it is pulled of the free pool in the list array. /// /// /// The owning schedule node for this virtual processor /// /// /// The owning IVirtualProcessorRoot /// void UMSThreadVirtualProcessor::Initialize(SchedulingNode *pOwningNode, IVirtualProcessorRoot *pOwningRoot) { // Initialize the base class VirtualProcessor::Initialize(pOwningNode, pOwningRoot); m_pCriticalContext = NULL; m_fCriticalIsReady = FALSE; // // Much as I'd love to defer these to StartupWorkerContext, it cannot be. That can be called from moving the completion list // to runnables which *MUST* be inside a hyper-critical region to avoid deadlock in the case of blocking between grabbing // the list and putting it on the runnables. // SchedulerBase *pScheduler = m_pOwningNode->GetScheduler(); if (m_pSchedulingContext != NULL) delete m_pSchedulingContext; m_pSchedulingContext = _concrt_new UMSSchedulingContext(static_cast(pScheduler), this); } /// /// Start a worker context executing on this.virtual processor. /// bool UMSThreadVirtualProcessor::StartupWorkerContext(ScheduleGroupSegmentBase* pSegment, InternalContextBase *pContext) { TRACE(TRACE_SCHEDULER, L"UMSThreadVirtualProcessor::StartupWorkerContext"); ASSERT(m_pExecutingContext == NULL); // // There is a subtle difference between UMS utilization of the RM and Win32 thread utilization of the RM. // The first context which we pass to the root to activate with is the scheduling context. It is a special context // which cannot run work. When a UMS thread blocks, the scheduling context is what actively gets switched back to. // m_pStartingSegment = pSegment; m_pPushContext = pContext; VMTRACE(MTRACE_EVT_ACTIVATE, m_pSchedulingContext, this, SchedulerBase::FastCurrentContext()); #if _UMSTRACE ContextBase *pCurrentContext = SchedulerBase::FastCurrentContext(); CMTRACE(MTRACE_EVT_ACTIVATE, (pCurrentContext && !pCurrentContext->IsExternal()) ? static_cast(pCurrentContext) : NULL, this, m_pSchedulingContext); #endif // _UMSTRACE m_pOwningRoot->Activate(m_pSchedulingContext); return true; } /// /// Notification that a critically blocked context has come off the completion list. /// void UMSThreadVirtualProcessor::CriticalNotify() { InterlockedExchange(&m_fCriticalIsReady, TRUE); AttemptWake(); } /// /// Affinitizes an internal context to the virtual processor. /// /// /// The internal context to affinitize. /// void UMSThreadVirtualProcessor::Affinitize(InternalContextBase *pContext) { if (pContext != NULL) { VirtualProcessor::Affinitize(pContext); } else { // // We want to switch to the scheduling Context. Setting // executingContext to NULL will accomplish that. // m_pExecutingContext = NULL; } } /// /// Attempts to wake the virtual processor due to a notification coming in that is critical to progress on /// the virtual processor (e.g.: a critically blocked context coming back on the completion list from /// another virtual processor). /// void UMSThreadVirtualProcessor::AttemptWake() { VirtualProcessor::ClaimTicket ticket; if (ClaimExclusiveOwnership(ticket)) { CONCRT_COREASSERT(ticket.ExerciseWakesExisting()); ticket.Exercise(); } } /// /// Makes a virtual processor available for scheduling work. This can only be called from the scheduling /// context. /// void UMSThreadVirtualProcessor::MakeAvailableFromSchedulingContext() { // // This makes the virtual processor available for wake/sleep *FROM* the scheduling context. // m_pExecutingContext = m_pSchedulingContext; MakeAvailable(AvailabilityIdlePendingThread); } /// /// Returns a type-cast of pContext to an InternalContextBase or NULL if it is not. /// InternalContextBase *UMSThreadVirtualProcessor::ToInternalContext(IExecutionContext *pContext) { if (pContext == m_pSchedulingContext) return NULL; else return static_cast(pContext); } /// /// Stub called in SFW before we search for runnable contexts. /// /// /// A context which should be run. /// InternalContextBase *UMSThreadVirtualProcessor::PreRunnableSearch() { UMSThreadScheduler *pScheduler = static_cast(m_pOwningNode->GetScheduler()); pScheduler->MoveCompletionListToRunnables(GetLocation()); return NULL; } } // namespace details } // namespace Concurrency