/// <summary> /// Initialize the transmogrified primary. /// </summary> void TransmogrifiedPrimary::Initialize() { if (!UMS::CreateUmsCompletionList(&m_pCompletionList)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); if (!UMS::GetUmsCompletionListEvent(m_pCompletionList, &m_hCompletionListEvent)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); m_hRetire = CreateEventW(NULL, FALSE, FALSE, NULL); if (m_hRetire == NULL) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); m_hBlock = CreateEventW(NULL, FALSE, FALSE, NULL); if (m_hBlock == NULL) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); m_hPrimary = LoadLibraryAndCreateThread(NULL, 0, PrimaryMain, this, 0, &m_primaryId); if (m_hPrimary == NULL) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); }
/// <summary> /// Sweeps the completion list looking for critically blocked items or the sought item before moving everything to /// the second stage transfer list. If the sought item is found, true is returned and it is NOT enqueued to the /// transfer list. Any critically blocked item signals a critical notification event of the appropriate primary /// and is NOT enqueued to the transfer list. /// </summary> /// <param name="pSought"> /// The thread proxy to sweep for. If NULL, everything but critically blocked items are moved to the transfer list. /// </param> /// <param name="fWait"> /// An indication as to whether or not to wait for something to come onto the completion list. /// </param> /// <returns> /// An indication as to whether the swept item was found. The caller owns it if true was returned. It is NOT moved /// to the transfer list. /// </returns> bool UMSSchedulerProxy::SweepCompletionList(UMSThreadProxy *pSought, bool fWait) { PUMS_CONTEXT pFirstContext; PUMS_CONTEXT pContext; bool fFound = false; if (!UMS::DequeueUmsCompletionListItems(m_pCompletionList, fWait ? INFINITE : 0, &pFirstContext)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); pContext = pFirstContext; while (pContext != NULL) { UMSThreadProxy *pProxy = UMSThreadProxy::FromUMSContext(pContext); PUMS_CONTEXT pNext = UMS::GetNextUmsListItem(pContext); #if defined(_DEBUG) CONCRT_COREASSERT((pProxy->m_UMSDebugBits & (UMS_DEBUGBIT_HANDEDTOPOLLER | UMS_DEBUGBIT_POLLERFOUNDCOMPLETION)) != UMS_DEBUGBIT_HANDEDTOPOLLER); pProxy->m_UMSDebugBits |= UMS_DEBUGBIT_COMPLETION; #endif // _DEBUG if (pProxy == pSought) fFound = true; else HandleCompletion(pProxy); pContext = pNext; } return fFound; }
/// <summary> /// Constructs a new scheduler proxy for a UMS scheduler. /// </summary> /// <param name="pScheduler"> /// The scheduler in question. /// </param> /// <param name="pResourceManager"> /// The resource manager instance. /// </param> /// <param name="policy"> /// A copy of the scheduler's policy /// </param> UMSSchedulerProxy::UMSSchedulerProxy(IScheduler *pScheduler, ResourceManager *pResourceManager, const SchedulerPolicy &policy) : SchedulerProxy(pScheduler, pResourceManager, policy), m_pCompletionList(NULL), m_hCompletionListEvent(NULL), m_hTransferListEvent(NULL), m_pushedBackCount(0), m_refCount(1) { m_hTransferListEvent = CreateEventW(NULL, FALSE, FALSE, NULL); if (m_hTransferListEvent == NULL) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); if (!UMS::CreateUmsCompletionList(&m_pCompletionList)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); if (!UMS::GetUmsCompletionListEvent(m_pCompletionList, &m_hCompletionListEvent)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); InitializeSListHead(&m_transferList); (static_cast<IUMSScheduler *>(pScheduler))->SetCompletionList(this); }
/// <summary> /// Wait for a proxy to appear on the completion list /// </summary> UMSThreadProxy * TransmogrifiedPrimary::WaitForBlockedThread(UMSThreadProxy * pProxy) { // // While waiting on the completion list we need to poll proxies for execution, if any. // This is required because the current proxy could be blocked for a resource that is // held by a UT that is suspended (and needs to be polled for subsequent execution). // const int maxCount = 2; HANDLE hObjects[maxCount]; int count = 0; hObjects[count++] = m_poller.GetEvent(); hObjects[count++] = m_hCompletionListEvent; CONCRT_COREASSERT(count == maxCount); DWORD timeout = INFINITE; for(;;) { DWORD result = WaitForMultipleObjectsEx(count, hObjects, FALSE, timeout, FALSE); DWORD index = (result == WAIT_TIMEOUT) ? 0 : (result - WAIT_OBJECT_0); if (index == 0) { bool done = m_poller.DoPolling(); // // Poll every interval // timeout = done ? INFINITE : UMSBackgroundPoller::PollInterval(); } else { CONCRT_COREASSERT(index == 1); // Proxy came back on the completion list PUMS_CONTEXT pUMSContext = NULL; if (!UMS::DequeueUmsCompletionListItems(m_pCompletionList, 0, &pUMSContext)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); // // The completed thread should be the one we are running // UMSThreadProxy *pCompletedProxy = UMSThreadProxy::FromUMSContext(pUMSContext); CONCRT_COREASSERT(pCompletedProxy == pProxy && UMS::GetNextUmsListItem(pUMSContext) == NULL); return pCompletedProxy; } } }
/// <summary> /// The primary thread for this transmogrified primary. /// </summary> /// <param name="pContext"> /// The TransmogrifiedPrimary that this thread manages. /// </param> DWORD CALLBACK TransmogrifiedPrimary::PrimaryMain(LPVOID pContext) { TransmogrifiedPrimary *pPrimary = reinterpret_cast<TransmogrifiedPrimary *>(pContext); UMS_SCHEDULER_STARTUP_INFO si; si.UmsVersion = UMS_VERSION; si.CompletionList = pPrimary->m_pCompletionList; si.SchedulerProc = (PUMS_SCHEDULER_ENTRY_POINT) &PrimaryInvocation; si.SchedulerParam = pContext; if (!UMS::EnterUmsSchedulingMode(&si)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); delete pPrimary; FreeLibraryAndDestroyThread(0); return 0; }
/// <summary> /// The UMS primary function. This is invoked when the primary switches into UMS scheduling mode or whenever a given /// context blocks or yields. /// </summary> /// <param name="reason"> /// The reason for the UMS invocation. /// </param> /// <param name="activationPayload"> /// The activation payload (depends on reason) /// </param> /// <param name="pData"> /// The context (the primary pointer) /// </param> void NTAPI TransmogrifiedPrimary::PrimaryInvocation(UMS_SCHEDULER_REASON reason, ULONG_PTR activationPayload, PVOID pData) { TransmogrifiedPrimary *pRoot = NULL; PUMS_CONTEXT pPrimaryContext = UMS::GetCurrentUmsThread(); if (reason != UmsSchedulerStartup) { // // activationPayload and pData might be NULL (blocking), so we're left with storing the TransmogrifiedPrimary in either // TLS or the UMS context (the primary does have one). At present, it's in the UMS context. // if (!UMS::QueryUmsThreadInformation(pPrimaryContext, UmsThreadUserContext, &pRoot, sizeof(pRoot), NULL)) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); } else { pRoot = reinterpret_cast<TransmogrifiedPrimary *>(pData); if (!UMS::SetUmsThreadInformation(pPrimaryContext, UmsThreadUserContext, &pRoot, sizeof(pRoot))) throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError())); } UMSThreadProxy *pProxy = NULL; switch(reason) { case UmsSchedulerStartup: { pProxy = pRoot->WaitForWork(); if (pProxy == NULL) { // // No work was found. We are done // return; } pRoot->Execute(pProxy); CONCRT_COREASSERT(false); break; } case UmsSchedulerThreadBlocked: { pProxy = pRoot->HandleBlocking(); if (pProxy == NULL) { // // No work was found. We are done // return; } pRoot->Execute(pProxy); CONCRT_COREASSERT(false); break; } case UmsSchedulerThreadYield: { pProxy = pRoot->HandleYielding(); if (pProxy == NULL) { // // No work was found. We are done. // return; } pRoot->Execute(pProxy); CONCRT_COREASSERT(false); break; } default: CONCRT_COREASSERT(false); break; } }