// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// UMSFreeVirtualProcessorRoot.h
//
// Part of the ConcRT Resource Manager -- this header file contains the internal definition for the UMS free virtual
// processor root (represents a virtual processor as handed to a scheduler).
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
namespace Concurrency
{
namespace details
{
class UMSFreeVirtualProcessorRoot : public IThreadProxy, public UMSBaseObject, public VirtualProcessorRoot
{
public:
// Private helper class
class InitialThreadParam
{
public:
InitialThreadParam(UMSFreeVirtualProcessorRoot * pRoot)
{
m_pRoot = pRoot;
m_hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
if (m_hEvent == NULL)
{
throw scheduler_resource_allocation_error(HRESULT_FROM_WIN32(GetLastError()));
}
}
~InitialThreadParam()
{
if (m_hEvent != NULL)
{
CloseHandle(m_hEvent);
}
}
HANDLE m_hEvent;
UMSFreeVirtualProcessorRoot * m_pRoot;
};
///
/// Constructs a new free virtual processor root.
///
///
/// The scheduler proxy this root is created for. A scheduler proxy holds RM data associated with an instance of
/// a scheduler.
///
///
/// The processor node that this root belongs to. The processor node is one among the nodes allocated to the
/// scheduler proxy.
///
///
/// The index into the array of cores for the processor node specified.
///
UMSFreeVirtualProcessorRoot(UMSSchedulerProxy *pSchedulerProxy, SchedulerNode* pNode, unsigned int coreIndex);
///
/// Destroys a free virtual processor root.
///
virtual ~UMSFreeVirtualProcessorRoot();
///
/// Deletes the virtual processor.
///
virtual void DeleteThis();
///
/// Determines whether the virtual processor is marked for deletion.
///
bool IsDeleting() const
{
return m_fDelete;
}
// **************************************************
// IVirtualProcessorRoot:
// **************************************************
///
/// Causes the scheduler to start running a thread proxy on the specified virtual processor root which will execute
/// the Dispatch method of the context supplied by pContext.
///
///
/// The context which will be dispatched on a (potentially) new thread running atop this virtual processor root.
///
virtual void Activate(::Concurrency::IExecutionContext *pContext);
///
/// Causes the thread proxy running atop this virtual processor root to temporarily stop dispatching pContext.
///
///
/// The context which should temporarily stop being dispatched by the thread proxy running atop this virtual processor root.
///
virtual bool Deactivate(::Concurrency::IExecutionContext *pContext);
///
/// Forces all data in the memory heirarchy of one processor to be visible to all other processors.
///
///
/// The context which is currently being dispatched by this root.
///
virtual void EnsureAllTasksVisible(::Concurrency::IExecutionContext *pContext);
///
/// Notify the primary that a thread on which it is critically blocked (due to asynchronous interruption in a critical region)
/// is now runnable.
///
void CriticalNotify()
{
SetEvent(m_hCriticalNotificationEvent);
}
// **************************************************
// IThreadProxy (for the scheduling context):
// **************************************************
///
/// Returns a process unique identifier for the thread proxy.
///
///
/// The IThreadProxy id.
///
virtual unsigned int GetId() const;
///
/// Called in order to perform a cooperative context switch between one context and another. After this call, pContext will
/// be running atop the virtual processor root and the context which was running will not. What happens to the context that
/// was running depends on the value of the reason argument.
///
///
/// The context to cooperatively switch to.
///
///
/// Indicates the state of the thread proxy that is executing the switch. This can determine ownership of the underlying thread
/// proxy and context.
///
virtual void SwitchTo(IExecutionContext *pContext, SwitchingProxyState switchState);
///
/// Called in order to disassociate the currently executing context from its virtual processor root, and reinitialize the root
/// for future use.
///
///
/// Indicates the state of the thread proxy that is executing the switch. This can determine ownership of the underlying thread
/// proxy and context.
///
virtual void SwitchOut(SwitchingProxyState switchState = Blocking);
///
/// Called in order to yield to the underlying operating system. This allows the operating system to schedule
/// other work in that time quantum.
///
virtual void YieldToSystem();
///
/// Returns whether or not this object is the primary.
///
virtual bool IsPrimary()
{
return true;
}
#if _UMSTRACE
void Trace(int traceEvt, void *pCtx, void *pVproc, ULONG_PTR data)
{
m_traceBuffer.Trace(traceEvt, pCtx, pVproc, data);
}
#endif // _UMSTRACE
///
/// Returns whether or not the given context is the scheduling context.
///
bool IsSchedulingContext(IExecutionContext *pContext) const
{
return (pContext == m_pSchedulingContext);
}
///
/// Returns our RM.
///
ResourceManager *GetResourceManager();
///
/// Returns the currently executing proxy on this virtual processor root.
///
UMSThreadProxy *GetExecutingProxy()
{
return m_pExecutingProxy;
}
///
/// Called in order to reset this virtual processor root to a completely quiescent state (not running anything).
///
void ResetOnIdle();
protected:
///
/// Returns our scheduler proxy (cast appropriately to a UMSSchedulerProxy).
///
UMSSchedulerProxy *SchedulerProxy()
{
return static_cast(GetSchedulerProxy());
}
private:
#if _UMSTRACE
_TraceBuffer m_traceBuffer;
#endif // _UMSTRACE
// A handle to the primary thread.
HANDLE m_hPrimary;
// Blocking handle if the thread should block.
HANDLE m_hBlock;
// The critical notification event. This event is waited upon in conjunction with the completion list event
// when the primary gets stuck in a critical region (or other piece of critical code such as startup). When signaled,
// it informs the primary that there it can do something.
HANDLE m_hCriticalNotificationEvent;
// The unique scheduling context. This is hard bound to this virtual processor and is actually invoked on the primary thread.
IExecutionContext *m_pSchedulingContext;
// The UT which is running atop this primary.
UMSThreadProxy *m_pExecutingProxy;
// The TID of the primary thread.
DWORD m_primaryId;
// Process wide unique identifier.
unsigned int m_id;
// Variable used to manage subscription level for UMS, in addition to the activation fence which is used for both threads and UMS.
// Additional comments in UMSFreeVirtualProcessorRoot::Deactivate.
bool m_fWokenByScheduler;
// Set to true when the root is activated for the first time.
bool m_fActivated;
// Indicates deletion needs to take place.
bool m_fDelete;
// Indicates the primary is started.
bool m_fStarted;
///
/// Called in order to invoke the scheduler's scheduling context.
///
///
/// If invocation of this context is due to previous context blocking, then was it due to an asynchronous event (e.g.: page fault).
/// Otherwise, false is passed in.
///
void InvokeSchedulingContext(bool fAsynchronous);
///
/// Executes the specified proxy. This can only be called from the primary thread!
///
///
/// The thread proxy to execute.
///
///
/// 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.
///
///
/// An indication as to whether the execution was due to the result of a critical block and subsequent execute.
///
///
/// This does *NOT* return if execution is successful. Any return indicates failure.
///
void Execute(UMSFreeThreadProxy *pProxy, bool fromSchedulingContext, bool fCriticalBlockAndExecute);
///
/// Creates the primary thread.
///
void CreatePrimary();
///
/// Starts up the primary thread.
///
void StartupPrimary()
{
CONCRT_COREASSERT(m_fStarted == false);
m_fStarted = true;
SetEvent(m_hBlock);
}
///
/// Marks a particular UMS thread proxy as running atop this UMS virtual processor root.
///
///
/// The proxy which is to run atop this virtual processor root.
///
///
/// Is the affinitization due to a critical execution happening on the same vproc.
///
void Affinitize(UMSFreeThreadProxy *pProxy, bool fCriticalReexecute = false);
///
/// Called in order to handle a UMS thread blocking.
///
///
/// The thread that is blocking.
///
///
/// An indication of whether the blocking was due to an asynchronous event (e.g.: page fault) or a synchronous one (e.g.: calling an API
/// which explicitly blocked.
///
void HandleBlocking(UMSFreeThreadProxy *pBlockedProxy, bool fAsynchronous);
///
/// Called in order to handle a UMS thread cooperative yielding.
///
///
/// The thread that is yielding.
///
void HandleYielding(UMSFreeThreadProxy *pProxy);
///
/// 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!
///
void CriticalBlockAndExecute(UMSFreeThreadProxy *pProxy);
///
/// Returns whether or not we are executing on the primary thread for this virtual processor.
///
bool OnPrimary()
{
return (GetCurrentThreadId() == m_primaryId);
}
///
/// 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.
///
bool InternalDeactivate();
///
/// The UMS primary function. This is invoked when the virtual processor switches into UMS scheduling mode or whenever a given
/// context blocks or yields.
///
///
/// The reason for the UMS invocation.
///
///
/// The activation payload (depends on reason)
///
///
/// The context (the virtual processor pointer)
///
static void NTAPI PrimaryInvocation(UMS_SCHEDULER_REASON reason, ULONG_PTR activationPayload, PVOID pData);
///
/// The primary thread for this UMS virtual processor.
///
///
/// The UMSFreeVirtualProcessorRoot that the primary manages.
///
static DWORD CALLBACK PrimaryMain(LPVOID pContext);
};
} // namespace details
} // namespace Concurrency