// ==++== // // 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