📄 umsfreevirtualprocessorroot.cpp
字号:
{
CORE_ASSERT(OnPrimary());
CORE_ASSERT(pProxy->GetCriticalRegionType() != OutsideCriticalRegion);
HANDLE hWaitObjects[2];
int count = 2;
//
// NOTE: It is *ABSOLUTELY* imperative that the critical notification event come FIRST in the wait order!
// Critically blocked proxies cannot be on the transfer list. So do not wait on transfer list events.
//
hWaitObjects[0] = m_hCriticalNotificationEvent;
hWaitObjects[1] = SchedulerProxy()->GetCompletionListEvent();
//
// Continue blocking until we either pull something off the completion list (in which case we simply sleep again) or the
// critical notification event is signaled (indicating that someone pulled the critical item off the list). When the critical
// notification event is signaled, whatever we are critically blocking upon *SHOULD* be runnable (assuming it wasn't asynchronously
// suspended in the interim or some such silly thing).
//
for(;;)
{
SchedulerProxy()->SweepCompletionList();
//
// Even if we were the one to sweep pProxy off the list, we must still perform the wait -- the event must be reset for the next
// critical blocking.
//
DWORD result = WaitForMultipleObjects(count, hWaitObjects, FALSE, INFINITE);
if (result == WAIT_OBJECT_0)
{
#if defined(_DEBUG)
pProxy->m_UMSDebugBits |= UMS_DEBUGBIT_CRITICALAWAKENING;
#endif // _DEBUG
//
// For threads we burn, they come back through the critical notification path here and simply do not get executed. Any return
// from CriticalBlockAndExecute indicates thread termination.
//
if (pProxy->IsTerminated())
return;
Execute(pProxy, false, true);
//
// If we get here, it means that ExecuteUmsThread failed (because of asynchronous suspension such as APC).
// In that case, we really just need to loop up and wait for this object to appear on the completion list again.
// Note that if we succeed, they just reset the stack frame of the primary and this magically disappears off the stack (e.g.:
// ExecuteUmsThread does *NOT* return ever on success).
//
}
//
// In either case -- failure or something appearing on the completion list, we need to sweep the list and wait again until the critical
// notification event is signaled.
//
}
}
/// <summary>
/// Called in order to handle a UMS thread blocking.
/// </summary>
/// <param name="pBlockedProxy">
/// The thread that is blocking.
/// </param>
/// <param name="fAsynchronous">
/// An indication of whether the blocking was due to an asynchronous event (e.g.: page fault) or a synchronous one (e.g.: calling an API
/// which explicitly blocked.
/// </param>
void UMSFreeVirtualProcessorRoot::HandleBlocking(UMSFreeThreadProxy *pBlockedProxy, bool fAsynchronous)
{
RVPMTRACE(MTRACE_EVT_RETURNTOPRIMARY_BLOCKED, pBlockedProxy, this, fAsynchronous);
CriticalRegionType type = pBlockedProxy->GetCriticalRegionType();
//
// We control thread lifetimes within the RM. Any thread which we are burning on the way out must be in a hyper-critical region.
//
CORE_ASSERT(!pBlockedProxy->IsTerminated() || type == InsideHyperCriticalRegion);
for (;;)
{
if (type == InsideHyperCriticalRegion || (type == InsideCriticalRegion && fAsynchronous))
{
RVPMTRACE(MTRACE_EVT_CRITICALBLOCK, pBlockedProxy, this, fAsynchronous);
#if defined(_DEBUG)
pBlockedProxy->m_UMSDebugBits |= UMS_DEBUGBIT_CRITICALBLOCK;
#endif // _DEBUG
pBlockedProxy->NotifyBlocked(true);
CriticalBlockAndExecute(pBlockedProxy);
//
// CriticalBlockAndExecute always performs a UMS::ExecuteUMSThread until it succeeds or the thread terminates. If it succeeds,
// the stack is snapped back and we never get here. Getting here implies that the thread terminated.
//
CORE_ASSERT(pBlockedProxy->IsTerminated());
delete pBlockedProxy;
return;
}
RVPMTRACE(MTRACE_EVT_UMSBLOCKED, pBlockedProxy, this, fAsynchronous);
#if defined(_DEBUG)
pBlockedProxy->m_UMSDebugBits |= UMS_DEBUGBIT_BLOCKED;
#endif // _DEBUG
pBlockedProxy->NotifyBlocked(false);
InvokeSchedulingContext(fAsynchronous);
}
}
/// <summary>
/// Called in order to handle a UMS thread cooperative yielding.
/// </summary>
/// <param name="pProxy">
/// The thread that is yielding.
/// </param>
void UMSFreeVirtualProcessorRoot::HandleYielding(UMSFreeThreadProxy *pProxy)
{
RVPMTRACE(MTRACE_EVT_RETURNTOPRIMARY_YIELD, pProxy, this, NULL);
CORE_ASSERT(pProxy->m_yieldAction != UMSThreadProxy::ActionNone);
#if defined(_DEBUG)
pProxy->m_UMSDebugBits |= UMS_DEBUGBIT_YIELDED;
#endif // _DEBUG
switch(pProxy->m_yieldAction)
{
//
// Someone performed a SwitchTo(..., Nesting). The UT which performed this call already created a transmogrified primary
// to transmogrify itself into a "virtual"-thread. We simply need to message that primary that it's now safe to execute the
// thread.
//
case UMSThreadProxy::ActionTransmogrify:
//
// Intentional Fall Through:
//
//
// Someone performed a SwitchTo. We do not block the thread (per-kernel-block), the thread simply put data in its
// messaging block and explicitly yielded to the primary.
//
case UMSThreadProxy::ActionSwitchTo:
{
UMSFreeThreadProxy *pNextProxy = NULL;
if (pProxy->m_pNextProxy != NULL)
{
pNextProxy = static_cast<UMSFreeThreadProxy *>(pProxy->m_pNextProxy);
#if defined(_DEBUG)
CORE_ASSERT((pNextProxy->m_UMSDebugBits & UMS_DEBUGBIT_DEACTIVATEDPROXY) == 0);
#endif
}
//
// We need to make sure that the transmogrified primary does this when the transmogrification is complete. Otherwise, someone
// may wind up trying to run pProxy prematurely on the exit of the nesting.
//
if (pProxy->m_yieldAction == UMSThreadProxy::ActionTransmogrify)
{
//
// The transmogrification might not even be created yet! Notify the transmogrificator. It will defer unblock it if necessary.
//
GetResourceManager()->GetTransmogrificator()->UnblockTransmogrification(pProxy);
}
else
{
pProxy->NotifyBlocked(false);
}
if (pNextProxy != NULL)
{
Execute(pNextProxy, false, false);
}
else
{
//
// Someone wants to *DIRECTLY* switch to the primary. Note that this is *NOT* an infinite loop as the execution
// will snap out the stack.
//
for (;;)
{
InvokeSchedulingContext(false);
}
}
CORE_ASSERT(false);
break;
}
//
// Someone performed a SwitchTo(..., idle). We do not block the thread (per-kernel-block), the thread simply put data
// in its messaging block and explicitly yielded to the primary. We do, however, put the thread back on the idle pool.
//
case UMSThreadProxy::ActionSwitchToAndRetire:
{
UMSFreeThreadProxy *pNextProxy = NULL;
if (pProxy->m_pNextProxy != NULL)
{
pNextProxy = static_cast<UMSFreeThreadProxy *>(pProxy->m_pNextProxy);
RVPMTRACE(MTRACE_EVT_CONTEXT_RELEASED, pProxy, this, pNextProxy);
}
else
{
RVPMTRACE(MTRACE_EVT_CONTEXT_RELEASED, pProxy, this, NULL);
}
#if defined(_DEBUG)
pProxy->m_UMSDebugBits |= UMS_DEBUGBIT_FREELIST;
#endif // _DEBUG
pProxy->NotifyBlocked(false);
pProxy->ReturnIdleProxy();
if (pNextProxy != NULL)
{
Execute(pNextProxy, false, false);
}
else
{
//
// Someone wants to *DIRECTLY* switch to the primary. Note that this is *NOT* an infinite loop as the execution
// will snap out the stack.
//
for (;;)
{
InvokeSchedulingContext(false);
}
}
CORE_ASSERT(false);
break;
}
//
// Someone called YieldToSystem. Yield the time quantum to the operating system using SwitchToThread
// and re-execute the UT.
//
case UMSThreadProxy::ActionYieldToSystem:
{
pProxy->NotifyBlocked(false);
YieldToSystem();
Execute(pProxy, false, false);
CORE_ASSERT(false);
break;
}
//
// Someone performed a Deactivate on the virtual processor root that was running this context.
//
case UMSThreadProxy::ActionDeactivate:
{
CORE_ASSERT(pProxy->m_pLastRoot == this);
//
// The semantics around Deactivate make it perfectly safe to mark it blocked now. Normally, we cannot touch pProxy
// afterward, but Deactivate is special.
//
pProxy->NotifyBlocked(false);
#if defined(_DEBUG)
pProxy->m_UMSDebugBits |= UMS_DEBUGBIT_DEACTIVATEDPROXY;
#endif // _DEBUG
if (InternalDeactivate())
pProxy->m_activationCause = UMSThreadProxy::ActivationCauseActivate;
else
pProxy->m_activationCause = UMSThreadProxy::ActivationCauseCompletionNotification;
CORE_ASSERT(pProxy->m_pLastRoot == this);
Execute(static_cast<UMSFreeThreadProxy *>(m_pExecutingProxy), false, false);
break;
}
//
// The thread is being freed (because it exited the dispatch loop). It may or may not be pooled.
//
case UMSThreadProxy::ActionFree:
case UMSThreadProxy::ActionResetForSwitchOut:
{
bool fFreeThread = (pProxy->m_yieldAction == UMSThreadProxy::ActionFree);
if (fFreeThread)
{
RVPMTRACE(MTRACE_EVT_CONTEXT_RELEASED, pProxy, this, NULL);
pProxy->NotifyBlocked(false);
pProxy->ReturnIdleProxy();
}
else
{
//
// If the proxy is switching out blocking, then we need to exit hypercritical
// region since it could be resumed on another vproc.
//
CORE_ASSERT(pProxy->GetCriticalRegionType() == InsideHyperCriticalRegion);
pProxy->ExitHyperCriticalRegion();
pProxy->NotifyBlocked(false);
}
if (!m_fDelete)
{
m_pExecutingProxy = NULL;
m_fActivated = false;
WaitForSingleObject(m_hBlock, INFINITE);
//
// Right now, we semantically leave undefined the activate call after a context exits the dispatch loop.
//
CORE_ASSERT(m_pExecutingProxy == NULL);
CORE_ASSERT(m_fDelete);
}
break;
}
case UMSThreadProxy::ActionStartup:
{
//
// UT startup (Thread engine should take care of running proxies on startup.
// Virtual processor root shall not see any proxy prior to startup).
//
CORE_ASSERT(false);
break;
}
default:
CORE_ASSERT(false);
}
}
/// <summary>
/// Called in order to invoke the scheduler's scheduling context.
/// </summary>
/// <param name="fAsynchronous">
/// If invocation of this context is due to previous context blocking, then was it due to an asynchronous event (e.g.: page fault).
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -