⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 umsfreevirtualprocessorroot.cpp

📁 C语言库函数的原型,有用的拿去
💻 CPP
📖 第 1 页 / 共 4 页
字号:
            // First compare with 0, then 1.
            //
            ASSERT(m_activationFence == 0 || m_activationFence == 1);
        }

        if (newVal == 0)
        {
            //
            // Reduce the subscription level on the core while the root is suspended. The count is used by dynamic resource management
            // to tell which cores allocated to a scheduler are unused, so that they can be temporarily repurposed.
            //
            if (m_fWokenByScheduler)
            {
                GetSchedulerProxy()->DecrementCoreSubscription(GetExecutionResource());
            }

            if (pProxy != NULL)
            {
                m_fWokenByScheduler = pProxy->Deactivate();
            }
            else
            {
                m_fWokenByScheduler = InternalDeactivate();
            }

            if (m_fWokenByScheduler)
            {
                //
                // This is a 'real' activation by the scheduler.
                //
                GetSchedulerProxy()->IncrementCoreSubscription(GetExecutionResource());
            }
        }
        else
        {
            ASSERT(newVal == 1);
            //
            // The activation for this deactivation came in early, so we return early here without making a kernel transition.
            //
        }

        return m_fWokenByScheduler;
    }

    /// <summary>
    ///     Forces all data in the memory heirarchy of one processor to be visible to all other processors.
    /// </summary>
    /// <param name="pContext">
    ///     The context which is currently being dispatched by this root.
    /// </param>
    void UMSFreeVirtualProcessorRoot::EnsureAllTasksVisible(Concurrency::IExecutionContext *pContext)
    {
        bool fPrimary = OnPrimary();

        if (pContext == NULL || (fPrimary && pContext != m_pSchedulingContext))
        {
            throw std::invalid_argument("pContext");
        }

        if (m_pExecutingProxy == NULL && !fPrimary)
        {
            throw invalid_operation();
        }

        //
        // Note that if pProxy is NULL at the end of this, we cannot touch m_pExecutingContext other than comparisons.  No fields may be
        // touched.  It may already be gone and freed.
        //
        UMSFreeThreadProxy *pProxy = NULL;
        IThreadProxy *pProxyIf = pContext->GetProxy();
        if (pProxyIf != this)
            pProxy = static_cast<UMSFreeThreadProxy *> (pContext->GetProxy());

        if (!fPrimary)
        {
            //
            // We expect this call to be made from within a dispatch loop or the primary.
            //
            if (pProxy != NULL && (m_pExecutingProxy != pProxy || UMSThreadProxy::GetCurrent() != static_cast<UMSThreadProxy *>(pProxy)))
            {
                throw invalid_operation();
            }

            //
            // We must be in a critical region on the **SCHEDULER SIDE** prior to calling this
            //
            CORE_ASSERT(pProxy == NULL || pProxy->GetCriticalRegionType() != OutsideCriticalRegion);
        }

        GetSchedulerProxy()->GetResourceManager()->FlushStoreBuffers();
    }

    /// <summary>
    ///     Performs a deactivation of the virtual processor.  This is always called on the primary.  A user thread which deactivates must defer the
    ///     call to the primary to perform the blocking action.
    /// </summary>
    bool UMSFreeVirtualProcessorRoot::InternalDeactivate()
    {
        CORE_ASSERT(OnPrimary());

        HANDLE hObjects[3];
        int count = 2;

        //
        // Wait for either an activation (m_hBlock) or a completion (the other two events).  Signal back the 
        // context the reason it awoke.
        //
        hObjects[0] = m_hBlock;
        hObjects[1] = SchedulerProxy()->GetCompletionListEvent();
        hObjects[2] = SchedulerProxy()->GetTransferListEvent();

        if (hObjects[2] != NULL) count++;

        CORE_ASSERT(count <= (sizeof(hObjects) / sizeof(HANDLE)));

        DWORD result;
        for(;;)
        {
            result = WaitForMultipleObjects(count, hObjects, FALSE, INFINITE);
            int index = result - WAIT_OBJECT_0;

            if (index == 1)
            {
                CORE_ASSERT(index < count);
                CORE_ASSERT(hObjects[index] == SchedulerProxy()->GetCompletionListEvent());

                // Completion list events should be handled by RM
                SchedulerProxy()->SweepCompletionList();

                // Go back to the loop and wait.
                continue;
            }

            // The scheduler needs to be notified for both activation and transfer list events
            break;
        }
        
        return (result == WAIT_OBJECT_0);
    }

    /// <summary>
    ///     Marks a particular UMS thread proxy as running atop this UMS virtual processor root.
    /// </summary>
    /// <param name="pProxy">
    ///     The proxy which is to run atop this virtual processor root.
    /// </param>
    /// <param name="fCriticalReexecute">
    ///     Is the affinitization due to a critical execution happening on the same vproc.
    /// </param>
    void UMSFreeVirtualProcessorRoot::Affinitize(UMSFreeThreadProxy *pProxy, bool fCriticalReexecute)
    {
        //
        // See FreeVirtualProcessorRoot::Affinitize.  There is an additional detail here: it's entirely possible that a UMS thread blocks
        // and comes back on the completion list before the primary has a chance to do anything (like resetting m_pRoot, etc...).  Another
        // primary might pick this up off the completion list before we even *REALIZE* it's blocked.  We must maintain consistent state.  Until
        // the original primary has had the chance to clear m_pRoot and do everything necessary to maintain state, prevent the switching to
        // pProxy.
        //
        // Critical reexecution need not "spin until blocked" as we've never set the blocked flag.  We must *COMPLETELY MASK* such blocking from
        // above layers.
        //
        if (!fCriticalReexecute)
            pProxy->SpinUntilBlocked();

        RVPMTRACE(MTRACE_EVT_AFFINITIZED, pProxy, this, NULL);

        if (pProxy->m_pLastRoot != this)
        {
#if defined(_DEBUG)
            //
            // If we were last running on a different vproc, we had better not be flagged as critically blocked!
            //
            CORE_ASSERT((pProxy->m_UMSDebugBits & UMS_DEBUGBIT_CRITICALBLOCK) == 0);
#endif // _DEBUG

            //
            // Normally, we will inherit affinity from the primary as UT(u)/KT(p) pair.  If pProxy enters the kernel, however, the directed switch
            // happens and we run atop KT(u).  This does not have the same affinity.  While the original switch elides the scheduler and runs atop the same
            // core, there are two potential problems:
            //
            // - If the thread quantum expires while KT(u) is in the kernel and the NT scheduler reschedules KT(u), it will reschedule with the affinity
            //   of KT(u) not that picked up off KT(p).  
            //
            // - If KT(u) does not block in the kernel, the UMS system will let UT(u) ride out on KT(u) as an optimization for threads making repeated non-blocking
            //   calls into the kernel.  Again, if there is no thread quantum expiration, no problem exists.  If the thread quantum does expire, however, the
            //   same issue exists as above -- the NT scheduler will reschedule UT(u)/KT(u) with the affinity of KT(u) not that picked up off KT(p).  
            //
            // Ideally, we'd like to have the same affinity for KT(u) as KT(p).  Making a call to change the affinity here, however, is a performance hit for
            // user mode context switching -- we will enter the kernel for a user mode switch. SetAffinity avoids the kernel transition if the affinity is the
            // same.
            //
            HardwareAffinity newAffinity = SchedulerProxy()->GetNodeAffinity(GetNodeId());
            pProxy->SetAffinity(newAffinity);

#if defined(_DEBUG)

            //
            // Validate that all the affinities are what we think they are.  If we switch to a UT that doesn't overlap our affinity mask, there's an absolutely
            // enormous performance penalty in Win7.
            //
            HardwareAffinity realPrimaryAffinity(GetCurrentThread());
            HardwareAffinity expectedPrimaryAffinity = SchedulerProxy()->GetNodeAffinity(GetNodeId());
            HardwareAffinity realUTAffinity(pProxy->GetThreadHandle());

            CORE_ASSERT(expectedPrimaryAffinity == realPrimaryAffinity);
            CORE_ASSERT(newAffinity == realPrimaryAffinity);
            CORE_ASSERT(newAffinity == realUTAffinity);

#endif // _DEBUG

        }

        pProxy->PrepareToRun(this);

        m_pExecutingProxy = pProxy;
    }

    /// <summary>
    ///     Executes the specified proxy.  This can only be called from the primary thread!
    /// </summary>
    /// <param name="pProxy">
    ///     The thread proxy to execute.
    /// </param>
    /// <param name="fromSchedulingContext">
    ///     Whether the switch is happening as a result of a SwitchTo from the scheduling context.  On failure, we do not recursively reinvoke
    ///     the scheduling context, we simply return -- indicating failure.
    /// </param>
    /// <param name="fCriticalBlockAndExecute">
    ///     An indication as to whether the execution was due to the result of a critical block and subsequent execute.
    /// </param>
    /// <returns>
    ///     This does *NOT* return if execution is successful.  Any return indicates failure.
    /// </returns>
    void UMSFreeVirtualProcessorRoot::Execute(UMSFreeThreadProxy *pProxy, bool fromSchedulingContext, bool fCriticalBlockAndExecute)
    {
        //
        // *NOTE*:
        // This is the *ONLY* function which should call ExecuteUmsThread!
        //
        RVPMTRACE(MTRACE_EVT_EXECUTE, pProxy, this, NULL);

        CORE_ASSERT(OnPrimary());

        Affinitize(pProxy, fCriticalBlockAndExecute);

        //
        // If we blocked during a critical region and performed a critical block and execute, we must not play with the messaging block.  It's entirely
        // possible that someone page faulted in the critical region during UMSThreadProxy::InternalSwitchTo where the messaging block was being filled in
        // or during UmsThreadYield before we get back to the primary.  Altering the messaging block in that case will corrupt the thread proxy and lead
        // to issues down the line.  We can safely reaffinitize because it will restore exact state, but we cannot touch the messaging block.
        //
        if (!fCriticalBlockAndExecute)
            pProxy->m_yieldAction = UMSThreadProxy::ActionNone;

        bool fCritical = pProxy->GetCriticalRegionType() != OutsideCriticalRegion;

#if defined(_DEBUG)
        bool fSuspended = pProxy->IsSuspended();
        bool fTerminated = pProxy->IsTerminated();

        DWORD oldDebugBits = pProxy->m_UMSDebugBits;
        DWORD oldLastExecuteError = pProxy->m_UMSLastExecuteError;

        pProxy->m_UMSDebugBits = 0;
        pProxy->m_UMSLastExecuteError = 0;
#endif // _DEBUG

        int spinCount = NUMBER_OF_EXECUTE_SPINS;

        for(;;)
        {
            CORE_ASSERT(spinCount > 0);

            UMS::ExecuteUmsThread(pProxy->GetUMSContext());

            //
            // If g_InfiniteSpinOnExecuteFailure is set, we want special debuggability around single stepping this code and we *FOREVER* spin waiting
            // for pProxy to become unsuspended.  This will allow us to single step around this code without triggering context switches 
            //
#if defined(_DEBUG)
            if (g_InfiniteSpinOnExecuteFailure)
            {
                continue;
            }
#else // _DEBUG
            if (--spinCount == 0)
            {
                //
                // There's absolutely no point in handing something to the poller that's not suspended.  It's likely a KAPC is firing and handing to the 
                // poller will just play thread debounce since there's no API to query for kernel locks.  
                //
                if (pProxy->IsSuspended())
                    break;
                else
                    spinCount = NUMBER_OF_EXECUTE_SPINS;
            }
#endif // _DEBUG

            YieldProcessor();
        }

        //
        // If we returned from ExecuteUmsThread, the thread couldn't execute.  It might have been suspended since last going on the completion list or
        // yielding or it might be running an APC due to a GetThreadContext, etc...  The obvious thought here is to unwind and fail the SwitchTo call.
        // The unfortunate reality is that from the scheduler's perspective, pPreviousProxy might be runnable (it might have yielded) and another virtual
        // processor might be trying to SwitchTo that.  In this case, it will spin until NotifyBlocked is called.  The obvious thought here is that one
        // might just cascade failures and set some special signal to that thread in the event of failure.  The reality here is that ExecuteUmsThread 
        // does *NOT RETURN* on success.  The stack of the primary is immediately snapped back to some RTL frame for UMS.  Hence, in the success case,
        //  we need to NotifyBlocked **BEFORE** calling ExecuteUmsThread.  This means that another virtual processor might **ALREADY** be
        // running pPreviousProxy.  We cannot unwind back to return failure from the SwitchTo.  
        //
        // In order to deal with this situation, we take an alternate tact.  If the failure happens, we treat it as if the SwitchTo succeeded and then immediately
        // blocked on the next instruction triggering a return to primary.  We will reinvoke the scheduling context in order to make a decision.
        //
#if defined(_DEBUG)

        pProxy->m_UMSLastExecuteError = GetLastError();
        pProxy->m_UMSDebugBits |= UMS_DEBUGBIT_HANDEDTOPOLLER | 
                                  (fSuspended ? UMS_DEBUGBIT_EXECUTEFAILURESUSPENDED : 0) |
                                  (fTerminated ? UMS_DEBUGBIT_EXECUTEFAILURETERMINATED : 0);

        RVPMTRACE(MTRACE_EVT_EXECUTEFAIL, pProxy, this, pProxy->m_UMSDebugBits);

#endif // _DEBUG

        //
        // The second unfortunate reality is that once ExecuteUmsThread fails for a particular UMS context, it will *NEVER* come back on
        // the completion list.  From UMS's perspective, the USched is now responsible for polling until the end of time whether this particular context
        // is not suspended.
        //
        SchedulerProxy()->PollForCompletion(pProxy);
        if (!fromSchedulingContext || fCritical)
            HandleBlocking(pProxy, true); 
        else
            pProxy->NotifyBlocked(false);
    }

    /// <summary>
    ///     Performs a critical blocking of the primary until a specific UT appears on the completion list.  The specified UT must
    ///     be in a critical region!  This can only be called from the primary thread!
    /// </summary>
    void UMSFreeVirtualProcessorRoot::CriticalBlockAndExecute(UMSFreeThreadProxy *pProxy)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -