internalcontextbase.cpp

来自「C语言库函数的原型,有用的拿去」· C++ 代码 · 共 1,283 行 · 第 1/5 页

CPP
1,283
字号
    /// </summary>
    void InternalContextBase::RejoinScheduler()
    {
        EnterCriticalRegion();

        ASSERT(SchedulerBase::FastCurrentContext() == this);
        ASSERT(m_pVirtualProcessor == NULL);
        ASSERT(m_pGroup != NULL);
        ASSERT(IsBlocked());

        // Switch out - this will take care of putting this context on a runnables queue and waking up a virtual processor
        // if one is available.
        SwitchOut(Nesting);
        ExitCriticalRegion();
    }

    /// <summary>
    ///     Wait for work algorithm:
    ///
    ///     We search numSearches times through the loop looking for work and then we allow other threads to claim this virtual processor
    ///     while we deactivate the virtual processor root, asking to be woken up when all processor write buffers are flushed
    ///     (signified by the m_fIsVisibleVirtualProcessor flag). After that we do *one* more full search for work.
    ///
    ///     Scenario #1: If the final sweep (after the flush) does not yield any work, we set the virtual processor to Idle
    ///     deactivate the virtual processor root. We will be woken up when work comes in, when we are canceled
    ///     due to the scheduler shutting down, or when the virtual processor we're running on was marked for retirement.
    ///     If we are not canceled, we simply reset and start over. If we are, we cleanup and exit the dispatch loop.
    ///
    ///     Scenario #2: If the final sweep does find work and this virtual processor is still available, we claim it and
    ///     do work.
    ///
    ///     Scenario #3: If the final sweep does find work and this virtual processor is not available, then either someone has activated
    ///     the underlying root while adding more work (via StartupIdleVirtualProcessor), which takes it from available list
    ///     (pVProc->IsAvailable()), or the virtual processor we're running on was retired due to core migration (via RemoveVirtualProcessors).
    ///     In this we execute a Deactivate to consume the activation. Note that even if we were marked for retirement, we will execute 
    ///     the chore we just picked up, which will delay retirement a bit.
    /// </summary>
    void InternalContextBase::WaitForWork()
    {
        const unsigned int numSearches = 256;

        ++m_searchCount;

        CMTRACE(MTRACE_EVT_SFW_NEXTLOOP, this, m_pVirtualProcessor, m_searchCount);

        if (m_searchCount < numSearches)
        {
            // Yield thread helps perf for oversubscribed vprocs
            m_pThreadProxy->YieldToSystem();

            CORE_ASSERT(!m_fIsVisibleVirtualProcessor);
            // Do another search for the work within the loop.
        }
        else if (m_searchCount == numSearches)
        {
            // At this point virtual processor has to be un-available for everyone but this
            // internal context.
            CORE_ASSERT(!m_pVirtualProcessor->IsAvailable());

            // At this point, we've made the virtual processor 'visible' to the rest of the scheduler. This means that anyone
            // adding work to the scheduler is able to grab this virtual processor and assume that it will find the work that
            // was added.
            m_fIsVisibleVirtualProcessor = true;

            // Make this virtual processor available and force all tasks to be visible. The idea is that any work queued *before*
            // the point at which we made the virtual processor available is visible after the API call, and we should be able to
            // make one single pass through the scheduler to find any such work. Work queued *after* the point at which we made
            // the virtual processor available should be able to wake up the virtual processor.
            m_pVirtualProcessor->MakeAvailable();

            m_pVirtualProcessor->EnsureAllTasksVisible(this);
            // If we find work during our final search, we will reclaim the virtual processor and reset the search count.

            // Context could not have been canceled since this vproc is not 'idle'.
            CORE_ASSERT(m_fCanceled == 0);
        }
        else
        {
            CORE_ASSERT(m_searchCount == numSearches + 1);
            CORE_ASSERT(m_fIsVisibleVirtualProcessor);

            if (m_fWorkSkipped)
            {
                m_searchCount--;

                //
                // Account for the cases where we fail to check some of the work-stealing queues due to task pool cancellation in progress.
                // If we skip work and deactivate the vproc, the work left in the queue could never be picked up. An example would be a
                // chore that blocks on a win32 event that is to be signaled by a queued chore which essentially requires stealing. If the
                // only other vproc goes idle incorrectly (because it wasn't able to steal from this queue), then the application would hang.
                //

                // 
                // Go back and search again.  Hopefully the owning context of the wsq that was skipped has finished canceling.
                //
                m_pThreadProxy->YieldToSystem();
            }
            else
            {
                // Notify the scheduler that this virtual processor is idle, right before going into a sleep state.
                m_pScheduler->VirtualProcessorIdle(true);

                // Note that the previous call to VirtualProcessorIdle could well be the one that takes the scheduler into PhaseTwoShutdown,
                // if this happens to be the last active vproc in the scheduler to go idle, AND no external references to the scheduler exist,
                // AND no work remains. In this case we expect that this context is canceled.
                CORE_ASSERT( !m_fCanceled || m_pScheduler->InFinalizationSweep() || m_pScheduler->HasCompletedShutdown());

                // Deactivate the virtual processor for real this time. We will return out of deactivate for one of the following reasons:
                //      1] Someone adds work and wakes us up.
                //      2] The scheduler hass shutdown and this context was canceled (and woken up)
                //      3] The RM is in the process of removing virtual processors dut do core migration, and marks the underlying vproc for retirement.
                // Even if we were canceled during the call to virtual processor idle, we must call deactivate to consume the
                // signal sent to the thread proxy (via Activate, when the virtual processor was canceled).
#if defined(_DEBUG)
                bool fRMAwaken = false;
#endif // _DEBUG

                CORE_ASSERT(!IsBlocked());
                while ( !m_pVirtualProcessor->Deactivate(this))
                {
                    //
                    // The resource manager has woken us up because of a completion notification from the completion list.  It's entirely possible
                    // that another thread was running, pulled it, and went idle.  Since we are in idle the VirtualProcessorIdle call above, that other virtual
                    // processor could have triggered finalization and we could be *IN* phase two shutdown right now.  Racing with phase two shutdown
                    // and VirtualProcessorIdle() / ClaimExclusiveOwnership() is *NOT* healthy for correct finalization.  Instead, we simply pretend that this
                    // virtual processor is still idle and simply *borrow* the *thread* to move completion list items.  The movement will translate into
                    // AddToRunnables calls which will activate virtual processors if there was something blocked.  If that was the case, we couldn't be in phase
                    // two finalization.
                    //
#if defined(_DEBUG)
                    fRMAwaken = true;
#endif // _DEBUG
                    RMAwaken();
                    //
                    // At this point, if things have moved from the completion list to runnables, the virtual processor might have been activated.  The RM will handle
                    // the activate/deactivate race and the looping around will swallow the activate and let us continue running in *PROPER FORM*.
                    //
                }

                CMTRACE(MTRACE_EVT_WOKEAFTERDEACTIVATE, this, m_pVirtualProcessor, NULL);
#if defined(_DEBUG)
                if (fRMAwaken)
                {
                    SetDebugBits(CTX_DEBUGBIT_ACTIVATEDAFTERRMAWAKEN);
                }
#endif // _DEBUG

                CORE_ASSERT( !m_fCanceled || m_pScheduler->InFinalizationSweep() || m_pScheduler->HasCompletedShutdown());

                // We were woken up for one of the 3 reasons above. It is important to tell the scheduler we are not idle before
                // proceeding. If we were woken up due to the addition of work we need to ensure that the work is visible to the scheduler
                // until after we've reported that we are !IDLE. The scheduler makes one pass looking for work and blocked contexts
                // once all virtual processors have reported that they are idle, and if it doesn't find any it will finalize the scheduler.
                // Therefore, we must register as ACTIVE, via VirtualProcessorIdle(false), *before* removing any work from the scheduler queues.
                // In addition, the call allows us to synchronize with finalization. If we were woken up for any reason, and finalization is in
                // progress at the same time, we need to ensure the context does not continue to execute its dispatch loop while the scheduler
                // is finalizing.
                m_pScheduler->VirtualProcessorIdle(false);

                // It is possible that the previous call to VirtualProcessorIdle(false) was suspended on the shutdown gate if
                // the scheduler is in the middle of shutdown, in which case the context could be canceled.
                // It is ok for the context to canceled right after its virtual processor was marked for retirement. We simply exit
                // the dispatch loop, and the virtual processor root in question is destroyed when the scheduler invokes
                // ISchedulerProxy::Shutdown, instead of IVirtualProcessorRoot::Remove, which is the normal path for
                // retired virtual processors.
                CORE_ASSERT(!m_fCanceled || m_pScheduler->InFinalizationSweep() || m_pScheduler->HasCompletedShutdown());

                CORE_ASSERT(!m_pVirtualProcessor->IsAvailable());
                CORE_ASSERT(m_pVirtualProcessor->GetExecutingContext() == this);

                m_fIsVisibleVirtualProcessor = false;
                m_searchCount = 0; 
            }
        }
    }

    /// <summary>
    ///     This function is called to execute the associated chore if one is available. The chore can be a stolen unrealized
    ///     chore or realized chore.
    /// </summary>
    /// <returns>
    ///     Returns true if an associated chore was executed, false otherwise.
    /// </returns>
    bool InternalContextBase::ExecutedAssociatedChore()
    {
        if (m_pAssociatedChore != NULL)
        {

#if defined(_DEBUG)
            m_workStartTimeStamp = __rdtsc();
            m_prepareCount = 0;
#endif // _DEBUG

            ExitCriticalRegion();
            if (m_fAssociatedChoreStolen)
            {
                static_cast<_UnrealizedChore*> (m_pAssociatedChore)->_Invoke();
            }
            else
            {
                RealizedChore * pRealizedChore = static_cast<RealizedChore*> (m_pAssociatedChore);
                pRealizedChore->Invoke();
                m_pScheduler->ReleaseRealizedChore(pRealizedChore);
            }
            EnterCriticalRegion();
            // Clean up chore
            m_pAssociatedChore = NULL;
            ReleaseWorkQueue();

            return true;
        }
        return false;
    }

    /// <summary>
    ///     Performs the necessary cleanup for a canceled context in its dispatch routine.
    /// <summary>
    void InternalContextBase::CleanupDispatchedContextOnCancel()
    {
        ASSERT(SchedulerBase::FastCurrentContext() == this);
        ASSERT(m_fCanceled);

#if defined(_DEBUG)
        //
        // At this point, we're shutting down this vproc/thread and we do not want to perform lock/heap validations that are no longer
        // true.
        //
        SetShutdownValidations();
#endif // _DEBUG

        // This indicates that the vproc is going away. From now until the end of time, this vproc is in a hyper-critical region.
        // We are no longer responsible for scheduling anything, so this is "perfectly" safe -- we cannot deadlock
        // between ourselves and some arbitrary piece of code we are responsible for scheduling.
        EnterHyperCriticalRegion();

        // The cleanup call *must* occur before the context releases its reference count on the scheduler.
        // Part of cleanup involves releasing a reference on the context's schedule group, and in some cases
        // we may need to spin until is safe to do so, keeping both the group and the scheduler alive.
        Cleanup();

        // NOTE: This call to DecrementInternalContextCount may well be the call that deletes the scheduler. The *this* pointer
        // should not be touched after this point!
        m_pScheduler->DecrementInternalContextCount();
    }

    /// <summary>
    ///     Called in the dispatch loop to check if the virtual processor the context is running on is marked for retirement,
    ///     and retires the virtual processor if it is.
    /// <summary>
    /// <summmary>
    ///     Returns true if the virtual processor was retired, false otherwise.
    /// </summary>
    bool InternalContextBase::IsVirtualProcessorRetired()
    {
        ASSERT(SchedulerBase::Fa

⌨️ 快捷键说明

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