/// <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;
        }
    }