// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// UMSThreadProxy.h
//
// Proxy for a UMS thread.
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
#if defined(_DEBUG)
#define UMS_DEBUGBIT_CRITICALBLOCK 0x00000001
#define UMS_DEBUGBIT_BLOCKED 0x00000002
#define UMS_DEBUGBIT_COMPLETION 0x00000004
#define UMS_DEBUGBIT_TRANSFERLIST 0x00000008
#define UMS_DEBUGBIT_CRITICALNOTIFY 0x00000010
#define UMS_DEBUGBIT_SKIPPEDCOMPLETION 0x00000020
#define UMS_DEBUGBIT_HANDEDTOPOLLER 0x00000040
#define UMS_DEBUGBIT_PULLEDFROMTRANSFERLIST 0x00000080
#define UMS_DEBUGBIT_POLLERFOUNDCOMPLETION 0x00000100
#define UMS_DEBUGBIT_YIELDED 0x00000200
#define UMS_DEBUGBIT_FREELIST 0x00000400
#define UMS_DEBUGBIT_CRITICALAWAKENING 0x00000800
#define UMS_DEBUGBIT_DEACTIVATEDPROXY 0x00001000
#define UMS_DEBUGBIT_ACTIVATEDPROXY 0x00002000
#define UMS_DEBUGBIT_COMPLETIONTERMINATED 0x00004000
#define UMS_DEBUGBIT_EXECUTEFAILURESUSPENDED 0x80000000
#define UMS_DEBUGBIT_EXECUTEFAILURETERMINATED 0x40000000
#endif // _DEBUG
// A magic value for m_pTransmogrification that indicates it should be immediately unblocked when defer created.
#define TRANSMOGRIFICATION_UNBLOCKED ((PVOID)1)
namespace Concurrency
{
namespace details
{
class UMSThreadProxy : public UMSBaseObject, public ::Concurrency::IUMSThreadProxy, public ::Concurrency::IUMSUnblockNotification
{
public:
///
/// Constructs a thread proxy.
///
///
/// The thread proxy factory that created this thread proxy, and maintains the idle pool of thread proxies.
///
///
/// The initial UMS completion list that this UMS thread should be put on upon creation. A primary cannot schedule
/// the thread until it has appeared on the specified completion list.
///
///
/// The stack size of the created thread.
///
UMSThreadProxy(IThreadProxyFactory * pFactory, PUMS_COMPLETION_LIST pStartupList, unsigned int stackSize);
///
/// Destroys a thread proxy.
///
virtual ~UMSThreadProxy();
///
/// Retrieves a process unique id for the thread proxy.
///
unsigned int GetId() const;
///
/// Gets the stack size of the thread proxy. Multiply by 1 KB to get actual stack size in bytes.
///
unsigned int GetStackSize()
{
return m_stackSize;
}
///
/// Cancels the thread proxy causing the underlying thread to exit.
///
void Cancel();
///
/// Returns the virtual processor root the thread proxy is running on.
///
UMSFreeVirtualProcessorRoot *GetVirtualProcessorRoot()
{
return m_pRoot;
}
///
/// Returns the previous virtual processor root the thread proxy is running on. If it is running, the current one is returned.
///
UMSFreeVirtualProcessorRoot *GetLastVirtualProcessorRoot()
{
return m_pLastRoot;
}
///
/// Returns a Win32 handle to the thread that is backing this proxy.
///
HANDLE GetThreadHandle()
{
return m_hPhysicalContext;
}
///
/// Returns our understanding of a UMS context (a UMS thread proxy) from the UMS system's understanding (a UMS_CONTEXT)
///
///
/// UMS context
///
///
/// UMS thread proxy
///
static UMSThreadProxy *FromUMSContext(PUMS_CONTEXT pUMSContext);
///
/// Returns our understanding of a UMS context (a UMS thread proxy) from the SLIST_ENTRY used to place it on an SLIST (e.g.: the transfer list).
///
///
/// Pointer to the SList entry in the thread proxy
///
///
/// UMS thread proxy
///
static UMSThreadProxy *FromListEntry(PSLIST_ENTRY pListEntry);
///
/// Returns the current UMS context.
///
///
/// Current UMS thread proxy
///
static UMSThreadProxy *GetCurrent()
{
return UMSThreadProxy::FromUMSContext(UMS::GetCurrentUmsThread());
}
///
/// Returns the PUMS_CONTEXT associated with this UMS thread.
///
///
/// UMS context for this thread
///
PUMS_CONTEXT GetUMSContext() const
{
return m_pUMSContext;
}
///
/// Returns whether the action that this thread took to get itself "unscheduled" was a message/yield pair. This could be an explicit
/// SwitchTo, a Deactivate, a pooling, etc...
///
///
/// Returns true if the thread last yielded to the primary
///
bool MessagedYield()
{
return (m_yieldAction != ActionNone);
}
///
/// Enters a critical region within the resource manager. This definition is constrained to the resource manager's definition
/// of a critical region. While inside a critical region, asynchronous suspensions (page faults, etc...) are hidden from view
/// of the underlying scheduling code (within the RM and the scheduler).
///
///
/// Returns the current critical region count
///
virtual int EnterCriticalRegion()
{
// Entering a critical region should be done on the same thread
CONCRT_COREASSERT(UMSThreadProxy::GetCurrent() == this);
return(++m_criticalRegionCount);
}
///
/// Exits a critical region within the resource manager. This definition is constrained to the resource manager's definition
/// of a critical region. While inside a critical region, asynchronous suspensions (page faults, etc...) are hidden from view
/// of the underlying scheduling code (within the RM and the scheduler).
///
///
/// Returns the current critical region count
///
virtual int ExitCriticalRegion()
{
CONCRT_COREASSERT(m_criticalRegionCount > 0);
return(--m_criticalRegionCount);
}
///
/// Identical to EnterHyperCriticalRegion -- this is a private version for utilization within the RM that does not follow the
/// "on-the-owning" thread rule.
///
///
/// Returns the current hypercritical region count
///
int ForceEnterHyperCriticalRegion()
{
m_criticalRegionCount++;
return(++m_hyperCriticalRegionCount);
}
///
/// Enters a hyper-critical region within the resource manager. This definition is constrained to the resource manager's definition
/// of a hyper-critical region. When inside a hyper-critical region, any suspension is hidden from view of the underlying scheduling
/// code. This requires EXTRA care with regards to the locks which are taken inside the region. No lock taken inside the region
/// can be shared with code outside such a region!
///
///
/// Returns the current hypercritical region count
///
virtual int EnterHyperCriticalRegion()
{
// Entering a critical region should be done on the same thread
CONCRT_COREASSERT(UMSThreadProxy::GetCurrent() == this);
return ForceEnterHyperCriticalRegion();
}
///
/// Exit a hyper-critical region within the resource manager. This definition is constrained to the resource manager's definition
/// of a hyper-critical region. When inside a hyper-critical region, any suspension is hidden from view of the underlying scheduling
/// code. This requires EXTRA care with regards to the locks which are taken inside the region. No lock taken inside the region
/// can be shared with code outside such a region!
///
///
/// Returns the current hypercritical region count
///
virtual int ExitHyperCriticalRegion()
{
CONCRT_COREASSERT(m_hyperCriticalRegionCount > 0);
CONCRT_COREASSERT(m_criticalRegionCount > 0);
int val = --m_hyperCriticalRegionCount;
m_criticalRegionCount--;
return val;
}
///
/// Returns whether or not this UMS thread proxy is within a critical region (either inside the RM or inside the scheduler).
/// Note that the scheduler's definition of a critical region may be stronger than ours.
///
///
/// Whether or not this UMS thread proxy is within a critical region.
///
virtual CriticalRegionType GetCriticalRegionType() const
{
if (m_hyperCriticalRegionCount > 0)
return InsideHyperCriticalRegion;
if (m_criticalRegionCount > 0)
return InsideCriticalRegion;
return OutsideCriticalRegion;
}
///
/// Clears the critical region bits. This should be done before reusing the context or placing it on the idle pool as certain operations
/// require a "until the end of time" critical region.
///
void ClearCriticalRegion()
{
m_criticalRegionCount = m_hyperCriticalRegionCount = 0;
}
///
/// Gets the priority of the thread proxy.
///
///
/// The priority of the thread proxy
///
int GetPriority()
{
return m_threadPriority;
}
///
/// Sets the priority of the underlying thread.
///
///
/// The new priority value for the thread.
///
void SetPriority(int priority);
///
/// Notifies the thread proxy that it is blocked (due to UMS activation of the primary).
///
///
/// Indication that this thread is critically blocked (inside critical region)
///
void NotifyBlocked(bool fCritically)
{
CONCRT_COREASSERT(m_pRoot != NULL);
#if defined(_DEBUG)
CONCRT_COREASSERT((m_UMSDebugBits & (UMS_DEBUGBIT_DEACTIVATEDPROXY | UMS_DEBUGBIT_ACTIVATEDPROXY)) != UMS_DEBUGBIT_DEACTIVATEDPROXY);
#endif // _DEBUG
m_blockingType = fCritically ? BlockingCritical : BlockingNormal;
if (!fCritically)
InterlockedExchangePointer((volatile PVOID*)&m_pRoot, (PVOID)NULL);
}
///
/// Notify that this proxy is ready to be transmogrified.
///
void NotifyTransmogrification();
///
/// Returns whether or not this thread is currently suspended.
///
///
/// Returns true if the thread is suspended
///
bool IsSuspended();
///
/// Returns whether or not this thread is currently terminated.
///
///
/// Returns true if the thread is terminated
///
bool IsTerminated();
///
/// Returns the next context which was pulled off the UMS completion list. This call can only be made until one of these objects is
/// rescheduled. After that, the results of the call become undefined.
///
///
/// Pointer to the unblock notification (context).
///
IUMSUnblockNotification *GetNextUnblockNotification()
{
PSLIST_ENTRY psle = m_listEntry.Next;
UMSThreadProxy *pProxy = psle == NULL ? NULL : CONTAINING_RECORD(psle, UMSThreadProxy, m_listEntry);
return pProxy;
}
///
/// Indicates whether this is a primary or not.
///
///
/// True if this is the primary thread, false otherwise
///
virtual bool IsPrimary()
{
return false;
}
#if defined(_DEBUG)
///
/// Tells the proxy it's no longer shutting down a virtual processor root and normal lock validations apply.
///
void ClearShutdownValidations()
{
m_fShutdownValidations = false;
}
///
/// Tells the proxy it's shutting down a virtual processor root and normal lock validations don't apply.
///
void SetShutdownValidations()
{
m_fShutdownValidations = true;
}
///
/// Returns whether or not the proxy is in a "shutting down a virtual processor root" mode where normal lock validations don't apply.
///
bool IsShutdownValidations() const
{
return m_fShutdownValidations;
}
#endif // _DEBUG
#if _UMSTRACE
void Trace(int traceEvt, void *pCtx, void *pVproc, ULONG_PTR data)
{
m_traceBuffer.Trace(traceEvt, pCtx, pVproc, data);
}
#endif // _UMSTRACE
///
/// Returns whether this proxy is transmogrified (e.g.: running on its own dedicated primary to simulate being an NT thread).
/// This call is only valid on the *CURRENT PROXY* otherwise the answer may be stale at the moment of return.
///
///
/// Ture if this proxy is transmogrified.
///
bool IsTransmogrified() const
{
return m_pTransmogrification != NULL;
}
///
/// A scoped critical region.
///
class ScopedCriticalRegion
{
public:
ScopedCriticalRegion()
{
m_pProxy = UMSThreadProxy::GetCurrent();
if (m_pProxy != NULL)
m_pProxy->EnterCriticalRegion();
}
ScopedCriticalRegion(UMSThreadProxy *pProxy) :
m_pProxy(pProxy)
{
if (m_pProxy != NULL)
m_pProxy->EnterCriticalRegion();
}
~ScopedCriticalRegion()
{
if (m_pProxy != NULL)
m_pProxy->ExitCriticalRegion();
}
private:
UMSThreadProxy *m_pProxy;
};
///
/// A scoped hyper-critical region.
///
class ScopedHyperCriticalRegion
{
public:
ScopedHyperCriticalRegion()
{
m_pProxy = UMSThreadProxy::GetCurrent();
if (m_pProxy != NULL)
m_pProxy->EnterHyperCriticalRegion();
}
ScopedHyperCriticalRegion(UMSThreadProxy *pProxy) :
m_pProxy(pProxy)
{
if (m_pProxy != NULL)
m_pProxy->EnterHyperCriticalRegion();
}
~ScopedHyperCriticalRegion()
{
if (m_pProxy != NULL)
m_pProxy->ExitHyperCriticalRegion();
}
private:
UMSThreadProxy *m_pProxy;
};
protected:
// The thread proxy factory that created this thread proxy, and maintains the idle pool of thread proxies.
IThreadProxyFactory * m_pFactory;
// The UMS context for the underlying UT.
PUMS_CONTEXT m_pUMSContext;
// The OS handle for the underlying UT.
HANDLE m_hPhysicalContext;
// The blocking handle.
HANDLE m_hBlock;
enum BlockingType
{
// No blocking registered.
BlockingNone,
// Normal blocking
BlockingNormal,
// Critical blocking (e.g.: masking a page fault)
BlockingCritical
};
// How we last blocked.
volatile BlockingType m_blockingType;
// The virtual processor root on which this thread proxy is executing.
UMSFreeVirtualProcessorRoot * volatile m_pRoot;
// The last virtual processor root on which this thread proxy was executing.
UMSFreeVirtualProcessorRoot *m_pLastRoot;
// Stores the stack size of the thread proxy. Multiply by 1 KB to get actual stack size in bytes.
unsigned int m_stackSize;
// Stores the last priority value that was set on the thread. Initial value is normal priority.
int m_threadPriority;
// An indication as to whether this thread proxy was idle pooled.
bool m_fIdlePooled;
// **************************************************
// Messaging Block:
//
// Everything between these markers is a messaging block between the UT and the primary. These fields may *ONLY* be touched under
// guardianship of a hyper-critical region.
// vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// *************************
// Input to the primary:
// The action that the primary is to take.
enum YieldAction
{
// No message
ActionNone = 0,
// Switch to a new context. The old one is still in use.
ActionSwitchTo,
// Switch to a new context and retire the old one to a free pool.
ActionSwitchToAndRetire,
// Perform a transmogrification of the context (start it on the transmogrified primary) and switch to a provided context or the scheduling context
ActionTransmogrifyAndSwitch,
// Perform a transmogrification of the context (start it on the transmogrified primary) and rest the primary.
ActionTransmogrifyAndReset,
// Deactivate the primary
ActionDeactivate,
// Get rid of the thread
ActionFree,
// Reset the primary on a SwitchOut
ActionResetForSwitchOut,
// UT Startup
ActionStartup,
// Yield to the operating system (SwitchToThread)
ActionYieldToSystem
} m_yieldAction;
// The next thread proxy that should be executed.
UMSThreadProxy *m_pNextProxy;
// The primary which will transmogrify this UMS thread into a "virtual"-thread.
TransmogrifiedPrimary * volatile m_pTransmogrification;
// *************************
// Output from the primary:
// Why Deactivate returned.
enum ActivationCause
{
ActivationCauseNone = 0,
ActivationCauseActivate,
ActivationCauseCompletionNotification
};
// Results of messaged calls to the primary are marshaled back to the UT here.
union
{
// Indicates why Deactivate returned.
ActivationCause m_activationCause;
};
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// End Messaging Block:
// **************************************************
bool m_fSuspended;
volatile LONG m_fCanceled;
///
/// Called in order to prepare this thread proxy to run atop a given virtual processor root.
///
void PrepareToRun(UMSFreeVirtualProcessorRoot *pRoot);
///
/// Spins until we've registered blocking.
///
void SpinUntilBlocked();
///
/// Spins until we've registered what blocking type the last block operation was and returns the type.
///
BlockingType SpinOnAndReturnBlockingType();
///
/// Switch from the current thread proxy to pProxy.
///
///
/// The thread proxy to switch to.
///
///
/// The reason for the switch.
///
void InternalSwitchTo(UMSThreadProxy *pProxy, SwitchingProxyState switchState);
///
/// Switch out the current thread proxy.
///
///
/// The reason for the switch.
///
void InternalSwitchOut(SwitchingProxyState switchState);
///
/// Yield to the underlying Operating system
///
void InternalYieldToSystem();
///
/// Deactivate the current thread proxy. As this requires a message block set, it lives here
/// rather than in the VPROOT.
///
///
/// An indication of whether the awakening was due to an Activate call on the virtual processor root
/// (true) or an RM cause (e.g.: completion notification -- false).
///
bool Deactivate();
///
/// The caller has exited the dispatch loop. Free the thread and deactivate.
///
void FreeViaExit();
///
/// Request this proxy to be transmogrified.
///
void RequestTransmogrification();
private:
friend class UMSFreeVirtualProcessorRoot;
friend class UMSSchedulerProxy;
friend class UMSBackgroundPoller;
friend class Transmogrificator;
friend class TransmogrifiedPrimary;
friend class UMSFreeThreadProxyFactory;
template friend class SQueue;
#if _UMSTRACE
_TraceBuffer m_traceBuffer;
#endif // _UMSTRACE
// Intrusive Link Field
UMSThreadProxy *m_pNext;
// Process wide unique identifier.
unsigned int m_id;
// Tracks the depth of critical region entry within the RM.
DWORD m_criticalRegionCount;
// Tracks the depth of hyper-critical region entry within the RM.
DWORD m_hyperCriticalRegionCount;
// Thread id.
DWORD m_threadId;
// Link chain for the transfer list.
SLIST_ENTRY m_listEntry;
// The background poller list chain.
UMSBackgroundPollerEntry m_backgroundPollerEntry;
// The chain entries for transmogrification.
ListEntry m_transmogrificatorPendingQueue;
// The background poller notification proxy.
UMSSchedulerProxy *m_pPollInsertionProxy;
// BEGIN - USED IN DEBUG ONLY
__int64 m_lastRunPrepareTimeStamp;
DWORD m_UMSDebugBits;
DWORD m_UMSLastExecuteError;
bool m_fShutdownValidations;
// END - USED IN DEBUG ONLY
///
/// Indicate that the thread proxy is ready for dispatch.
///
void ReadyForDispatch();
///
/// Dispatch routine for thread proxies.
///
virtual void Dispatch() = 0;
///
/// Thread start routine for proxies.
///
static DWORD CALLBACK UMSThreadProxyMain(LPVOID lpParameter);
};
} // namespace details
} // namespace Concurrency