// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // UMSThreadScheduler.cpp // // Source file containing the implementation for a UMS thread based concrt scheduler // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- #pragma once #include "concrtinternal.h" namespace Concurrency { namespace details { DWORD UMSThreadScheduler::t_dwSchedulingContextIndex; /// /// Creates a UMS thread based scheduler /// UMSThreadScheduler::UMSThreadScheduler(_In_ const ::Concurrency::SchedulerPolicy& policy) : SchedulerBase(policy), m_pCompletionList(NULL) { } /// /// Creates a UMS thread based scheduler /// UMSThreadScheduler* UMSThreadScheduler::Create(_In_ const ::Concurrency::SchedulerPolicy& policy) { return _concrt_new UMSThreadScheduler(policy); } /// /// Creates a UMS thread based virtual processor. /// VirtualProcessor* UMSThreadScheduler::CreateVirtualProcessor(SchedulingNode *pOwningNode, IVirtualProcessorRoot* pOwningRoot) { return _concrt_new UMSThreadVirtualProcessor(pOwningNode, pOwningRoot); } /// /// Creates a new thread internal context and returns it to the base scheduler. /// InternalContextBase *UMSThreadScheduler::CreateInternalContext() { return _concrt_new UMSThreadInternalContext(this); } /// /// Destroys a UMS thread based scheduler /// UMSThreadScheduler::~UMSThreadScheduler() { } /// /// Notification after a virtual processor goes from INACTIVE to ACTIVE or ACTIVE to INACTIVE /// For UMS we need to ensure that there is at least 1 active vproc. /// /// /// True if a virtual processor is going from INACTIVE to ACTIVE, and false if it is going from ACTIVE to INACTIVE. /// /// /// Active virtual processor count after the transition /// void UMSThreadScheduler::VirtualProcessorActiveNotification(bool fActive, LONG activeCount) { // We need to maintain at least 1 active virtual processor as long // as there is work. Since we cannot easily determine if there are blocked UMS context, // do not allow active vproc count to go from 1 to 0. if (activeCount == 0) { // If we are the last active virtual processor, we should be in a hyper cricital region CONCRT_COREASSERT(!fActive); StartupIdleVirtualProcessor(GetNextSchedulingRing()->GetAnonymousScheduleGroupSegment()); } } /// /// Called in order to move the completion list to the runnables lists. /// /// /// Bias any awakening of virtual processors to this location. /// /// /// Whether there was anything on the completion list when queried. /// bool UMSThreadScheduler::MoveCompletionListToRunnables(location bias) { bool fHadItems = false; // // This *ABSOLUTELY* must be in a hyper-critical region. Deadlock can ensue if not. Anything outward from this // must follow the set of rules governing a hyper-critical region. // ContextBase *pCurrentContext = SchedulerBase::FastCurrentContext(); if (pCurrentContext != NULL) pCurrentContext->EnterHyperCriticalRegion(); IUMSUnblockNotification *pUnblock = m_pCompletionList->GetUnblockNotifications(); while (pUnblock != NULL) { fHadItems = true; IUMSUnblockNotification *pNext = pUnblock->GetNextUnblockNotification(); UMSThreadInternalContext *pContext = static_cast (pUnblock->GetContext()); VCMTRACE(MTRACE_EVT_PULLEDFROMCOMPLETION, pContext, (pCurrentContext && !pCurrentContext->IsExternal()) ? static_cast(pCurrentContext)->m_pVirtualProcessor : NULL, pCurrentContext); #if defined(_DEBUG) pContext->SetDebugBits(CTX_DEBUGBIT_PULLEDFROMCOMPLETIONLIST); #endif // _DEBUG // // In order to know what to do with this particular item, we need to know *why* it blocked. If the primary hasn't gotten to telling us that, // we must spin. // UMSThreadInternalContext::BlockingType blockingType = pContext->SpinOnAndReturnBlockingType(); CONCRT_COREASSERT(blockingType != UMSThreadInternalContext::BlockingNone); // // Make a determination of where this item goes. There are several cases here: // // - It might have UMS blocked during a normal critical region (e.g.: the main dispatch loop blocked on the heap lock or some // other similar object). If the context was inside a critical region, we have special determinations to make. // // - It might just be a runnable. // switch(blockingType) { case UMSThreadInternalContext::BlockingCritical: // // This is the single special context allowed to be "inside a critical region" on the virtual processor. Signal the virtual // processor specially. // VCMTRACE(MTRACE_EVT_CRITICALNOTIFY, pContext, (pCurrentContext && !pCurrentContext->IsExternal()) ? static_cast(pCurrentContext)->m_pVirtualProcessor : NULL, pCurrentContext); #if defined(_DEBUG) pContext->SetDebugBits(CTX_DEBUGBIT_CRITICALNOTIFY); #endif // _DEBUG pContext->m_pLastVirtualProcessor->CriticalNotify(); break; case UMSThreadInternalContext::BlockingNormal: // // If it's a normal runnable, it just goes on the runnables list. We pass along the bias to indicate which virtual processor // (or owning node) we prefer to awaken due to the addition of runnables. // pContext->AddToRunnables(bias); break; } pUnblock = pNext; } if (pCurrentContext != NULL) pCurrentContext->ExitHyperCriticalRegion(); return fHadItems; } /// /// Static initialization common to UMS schedulers. /// void UMSThreadScheduler::OneShotStaticConstruction() { t_dwSchedulingContextIndex = TlsAlloc(); if (t_dwSchedulingContextIndex == TLS_OUT_OF_INDEXES) { throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); } } /// /// Static destruction common to UMS schedulers. /// void UMSThreadScheduler::OneShotStaticDestruction() { TlsFree(t_dwSchedulingContextIndex); t_dwSchedulingContextIndex = 0; } } // namespace details } // namespace Concurrency