📄 umsthreadinternalcontext.h
字号:
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// UMSThreadInternalContext.h
//
// Header file containing the metaphor for a UMS thread based internal execution context/stack.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#pragma once
namespace Concurrency
{
namespace details
{
class UMSThreadInternalContext : public InternalContextBase
{
public:
//
// Public Methods
//
/// <summary>
/// Construct an internal thread based context.
/// </summary>
UMSThreadInternalContext(SchedulerBase *pScheduler);
/// <summary>
/// Destroys an internal thread based context.
/// </summary>
virtual ~UMSThreadInternalContext();
/// <summary>
/// Causes the internal context to block yielding the virtual processor to a different internal context.
/// </summary>
virtual void Block();
/// <summary>
/// Returns the type of context
/// </summary>
virtual ContextKind GetContextKind() const
{
return UMSThreadContext;
}
/// <summary>
/// Depending on the argument, causes the scheduler to add an extra virtual processor for the
/// duration of a block of code or remove a previously added one.
///
/// Oversubscribe(true);
/// // some slow kernel or I/O code, etc.
/// Oversubscribe(false);
///
/// UMS provides automatic oversubscription. This API is largely ignored in UMS except for enforcing
/// the matching Oversubscribe(false) API as per the spec.
/// </summary>
/// <param name="beginOversubscription">
/// A boolean value specifying whether oversubscription is to be turned on or off.
/// </param>
virtual void Oversubscribe(bool beginOversubscription);
/// <summary>
/// The method that is called when a thread proxy starts executing a particular context. The thread proxy which executes
/// the context is passed into this method and must be saved and returned on a call to the get_Proxy method.
/// </summary>
/// <param name="pDispatchState">
/// The state under which this IExecutionContext is being dispatched.
/// </param>
virtual void Dispatch(DispatchState * pDispatchState);
/// <summary>
/// Enters a critical region of the scheduler. Calling this guarantees that the virtual processor on which this context lives
/// is guaranteed to be stable throughout the critical region. For some context types, this is virtually a NOP. For others
/// (UMS), this makes it appear that blocking on the context actually blocks the UMS thread instead of triggering return to
/// primary. Note that critical regions suppress asynchronous blocking but not synchronous blocking.
/// </summary>
virtual int EnterCriticalRegionHelper();
/// <summary>
/// Exits a critical region of the scheduler.
/// </summary>
virtual int ExitCriticalRegionHelper();
/// <summary>
/// Checks if a context is in a critical region. This is only safe from either the current context or from a UMS primary which
/// has woken due to a given context blocking.
/// </summary>
virtual CriticalRegionType GetCriticalRegionType() const
{
CORE_ASSERT(m_pThreadProxy != NULL);
return (static_cast<IUMSThreadProxy *>(m_pThreadProxy))->GetCriticalRegionType();
}
/// <summary>
/// Enters a hyper-critical region of the scheduler. Calling this guarantees not only the conditions of a critical region but it
/// guarantees that synchronous blocking is suppressed as well. This allows for lock sharing between the primary and hyper-critical
/// regions running on UTs. No lock sharing can occur between the inside of this region type and the outside of this region type
/// on a UT.
/// </summary>
virtual int EnterHyperCriticalRegionHelper();
/// <summary>
/// Exits a hyper-critical region of the scheduler.
/// </summary>
virtual int ExitHyperCriticalRegionHelper();
/// <summary>
/// Determines whether or not the context is synchronously blocked at this given time.
/// </summary>
/// <returns>
/// Whether context is in synchronous block state.
/// </returns>
virtual bool IsSynchronouslyBlocked() const
{
return InternalContextBase::IsSynchronouslyBlocked() || ((m_blockedState & CONTEXT_UMS_SYNC_BLOCKED) != 0);
}
private:
friend class UMSThreadScheduler;
friend class UMSSchedulingContext;
friend class UMSThreadVirtualProcessor;
enum BlockingType
{
// No blocking registered.
BlockingNone,
// Normal blocking
BlockingNormal,
// Critical blocking (e.g.: Win32 blocking call inside a normal critical region)
BlockingCritical
};
// The previous virtual processor we were running on.
UMSThreadVirtualProcessor *m_pLastVirtualProcessor;
// How we last blocked.
volatile BlockingType m_blockingType;
/// <summary>
/// Sets the blocking type.
/// </summary>
void SetBlockingType(BlockingType blockingType)
{
m_blockingType = blockingType;
}
/// <summary>
/// Notifies the context that it's blocked.
/// </summary>
/// <param name="fAsynchronous">
/// Is previously running context asynchronously blocked.
/// </param>
void NotifyBlocked(bool fAsynchronous);
/// <summary>
/// Spins until we've registered what blocking type the last block operation was and returns the type.
/// </summary>
BlockingType SpinOnAndReturnBlockingType();
/// <summary>
/// Called before this executes on a given virtual processor.
/// </summary>
virtual void PrepareToRun(VirtualProcessor *pVProc);
private:
friend class UMSThreadScheduler;
friend class UMSSchedulingContext;
friend class UMSThreadVirtualProcessor;
/// <summary>
/// Called when the RM wakes up the thread for some reason.
/// </summary>
virtual void RMAwaken()
{
CMTRACE(MTRACE_EVT_RMAWAKEN, this, m_pVirtualProcessor, NULL);
//
// *NOTE*: It is absolutely imperative that this bias awakening of virtual processors to the scheduling node of
// m_pVirtualProcessor. RMAwaken is called from an internal context when something comes back on the completion
// list of a deactivated virtual processor. Because things are added to runnables on an internal context, a cache
// local schedule group may put items in the LRC. The LRC is *NOT* checked when rambling from virtual processors
// belonging to other scheduling nodes. If we unblock a context and add it to the LRC on node A and then awake
// a virtual processor on node B, the work will never be found and the scheduler will deadlock. This is an artifact
// of the way in which UMS completion list events come back on deactivated virtual processors.
//
(static_cast<UMSThreadScheduler *>(m_pScheduler))->MoveCompletionListToRunnables(m_pVirtualProcessor);
}
void * operator new(size_t _Size)
{
return ::operator new(_Size);
}
void operator delete(void * _Ptr)
{
::operator delete(_Ptr);
}
};
} // namespace details
} // namespace Concurrency
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -