diff options
Diffstat (limited to 'src/vm/threadpoolrequest.h')
-rw-r--r-- | src/vm/threadpoolrequest.h | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/vm/threadpoolrequest.h b/src/vm/threadpoolrequest.h new file mode 100644 index 0000000000..8d2c7e486a --- /dev/null +++ b/src/vm/threadpoolrequest.h @@ -0,0 +1,362 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +//========================================================================= + +// +// ThreadPoolRequest.h +// + +// +// This file contains definitions of classes needed to mainain per-appdomain +// thread pool work requests. This is needed as unmanaged and managed work +// requests are allocted, managed and dispatched in drastically different ways. +// However, the scheduler need be aware of these differences, and it should +// simply talk to a common interface for managing work request counts. +// +//========================================================================= + +#ifndef _THREADPOOL_REQUEST_H +#define _THREADPOOL_REQUEST_H + +#define TP_QUANTUM 2 +#define UNUSED_THREADPOOL_INDEX (DWORD)-1 + +//-------------------------------------------------------------------------- +//IPerAppDomainTPCount is an interface for implementing per-appdomain thread +//pool state. It's implementation should include logic to maintain work-counts, +//notify thread pool class when work arrives or no work is left. Finally +//there is logic to dipatch work items correctly in the right domain. +// +//Notes: +//This class was designed to support both the managed and unmanaged uses +//of thread pool. The unmananged part may directly be used through com +//interfaces. The differences between the actual management of counts and +//dispatching of work is quite different between the two. This interface +//hides these differences to the thread scheduler implemented by the thread pool +//class. +// + +class IPerAppDomainTPCount{ +public: + virtual void ResetState() = 0; + virtual BOOL IsRequestPending() = 0; + + //This functions marks the begining of requests queued for the domain. + //It needs to notify the scheduler of work-arrival among other things. + virtual void SetAppDomainRequestsActive() = 0; + + //This functions marks the end of requests queued for this domain. + virtual void ClearAppDomainRequestsActive(BOOL bADU = FALSE) = 0; + + //Clears the "active" flag if it was set, and returns whether it was set. + virtual bool TakeActiveRequest() = 0; + + //Takes care of dispatching requests in the right domain. + virtual void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled) = 0; + virtual void SetAppDomainId(ADID id) = 0; + virtual void SetTPIndexUnused() = 0; + virtual BOOL IsTPIndexUnused() = 0; + virtual void SetTPIndex(TPIndex index) = 0; + virtual void SetAppDomainUnloading() = 0; + virtual void ClearAppDomainUnloading() = 0; +}; + +typedef DPTR(IPerAppDomainTPCount) PTR_IPerAppDomainTPCount; + +static const LONG ADUnloading = -1; + +#ifdef _MSC_VER +// Disable this warning - we intentionally want __declspec(align()) to insert padding for us +#pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +#endif + +//-------------------------------------------------------------------------- +//ManagedPerAppDomainTPCount maintains per-appdomain thread pool state. +//This class maintains the count of per-appdomain work-items queued by +//ThreadPool.QueueUserWorkItem. It also dispatches threads in the appdomain +//correctly by setting up the right exception handling frames etc. +// +//Note: The counts are not accurate, and neither do they need to be. The +//actual work queue is in managed (implemented in threadpool.cs). This class +//just provides heuristics to the thread pool scheduler, along with +//synchronization to indicate start/end of requests to the scheduler. +class ManagedPerAppDomainTPCount : public IPerAppDomainTPCount { +public: + + ManagedPerAppDomainTPCount(TPIndex index) {ResetState(); m_index = index;} + + inline void ResetState() + { + LIMITED_METHOD_CONTRACT; + VolatileStore(&m_numRequestsPending, (LONG)0); + m_id.m_dwId = 0; + } + + inline BOOL IsRequestPending() + { + LIMITED_METHOD_CONTRACT; + + LONG count = VolatileLoad(&m_numRequestsPending); + return count != ADUnloading && count > 0; + } + + void SetAppDomainRequestsActive(); + void ClearAppDomainRequestsActive(BOOL bADU); + bool TakeActiveRequest(); + + inline void SetAppDomainId(ADID id) + { + LIMITED_METHOD_CONTRACT; + //This function should be called during appdomain creation when no managed code + //has started running yet. That implies, no requests should be pending + //or dispatched to this structure yet. + + _ASSERTE(VolatileLoad(&m_numRequestsPending) != ADUnloading); + _ASSERTE(m_id.m_dwId == 0); + + m_id = id; + } + + inline void SetTPIndex(TPIndex index) + { + LIMITED_METHOD_CONTRACT; + //This function should be called during appdomain creation when no managed code + //has started running yet. That implies, no requests should be pending + //or dispatched to this structure yet. + + _ASSERTE(VolatileLoad(&m_numRequestsPending) != ADUnloading); + _ASSERTE(m_id.m_dwId == 0); + _ASSERTE(m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX); + + m_index = index; + } + + inline BOOL IsTPIndexUnused() + { + LIMITED_METHOD_CONTRACT; + if (m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX) + { + //This function is called during appdomain creation, and no new appdomains can be + //added removed at this time. So, make sure that the per-appdomain structures that + //have been cleared(reclaimed) don't have any pending requests to them. + + _ASSERTE(VolatileLoad(&m_numRequestsPending) != ADUnloading); + _ASSERTE(m_id.m_dwId == 0); + + return TRUE; + } + + return FALSE; + } + + inline void SetTPIndexUnused() + { + WRAPPER_NO_CONTRACT; + //This function should be called during appdomain unload when all threads have + //succesfully exited the appdomain. That implies, no requests should be pending + //or dispatched to this structure. + + _ASSERTE(m_id.m_dwId == 0); + + m_index.m_dwIndex = UNUSED_THREADPOOL_INDEX; + } + + inline void SetAppDomainUnloading() + { + LIMITED_METHOD_CONTRACT; + VolatileStore(&m_numRequestsPending, ADUnloading); + } + + inline void ClearAppDomainUnloading(); + + inline BOOL IsAppDomainUnloading() + { + return VolatileLoad(&m_numRequestsPending) == ADUnloading; + } + + void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled); + +private: + ADID m_id; + TPIndex m_index; + DECLSPEC_ALIGN(64) struct { + BYTE m_padding1[64 - sizeof(LONG)]; + // Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange + LONG m_numRequestsPending; + BYTE m_padding2[64]; + }; +}; + +//-------------------------------------------------------------------------- +//UnManagedPerAppDomainTPCount maintains the thread pool state/counts for +//unmanaged work requests. From thread pool point of view we treat unmanaged +//requests as a special "appdomain". This helps in scheduling policies, and +//follow same fairness policies as requests in other appdomains. +class UnManagedPerAppDomainTPCount : public IPerAppDomainTPCount { +public: + + UnManagedPerAppDomainTPCount() + { + LIMITED_METHOD_CONTRACT; + ResetState(); + } + + inline void InitResources() + { + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + INJECT_FAULT(COMPlusThrowOM()); + } + CONTRACTL_END; + + } + + inline void CleanupResources() + { + } + + inline void ResetState() + { + LIMITED_METHOD_CONTRACT; + m_NumRequests = 0; + VolatileStore(&m_outstandingThreadRequestCount, (LONG)0); + } + + inline BOOL IsRequestPending() + { + LIMITED_METHOD_CONTRACT; + return VolatileLoad(&m_outstandingThreadRequestCount) != (LONG)0 ? TRUE : FALSE; + } + + void SetAppDomainRequestsActive(); + + inline void ClearAppDomainRequestsActive(BOOL bADU) + { + LIMITED_METHOD_CONTRACT; + VolatileStore(&m_outstandingThreadRequestCount, (LONG)0); + } + + bool TakeActiveRequest(); + + inline void SetAppDomainId(ADID id) + { + } + + void QueueUnmanagedWorkRequest(LPTHREAD_START_ROUTINE function, PVOID context); + PVOID DeQueueUnManagedWorkRequest(bool* lastOne); + + void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled); + + inline void SetTPIndexUnused() + { + WRAPPER_NO_CONTRACT; + _ASSERT(FALSE); + } + + inline BOOL IsTPIndexUnused() + { + WRAPPER_NO_CONTRACT; + _ASSERT(FALSE); + return FALSE; + } + + inline void SetTPIndex(TPIndex index) + { + WRAPPER_NO_CONTRACT; + _ASSERT(FALSE); + } + + inline void SetAppDomainUnloading() + { + WRAPPER_NO_CONTRACT; + _ASSERT(FALSE); + } + + inline void ClearAppDomainUnloading() + { + WRAPPER_NO_CONTRACT; + _ASSERT(FALSE); + } + +private: + SpinLock m_lock; + ULONG m_NumRequests; + DECLSPEC_ALIGN(64) struct { + BYTE m_padding1[64 - sizeof(LONG)]; + // Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange + LONG m_outstandingThreadRequestCount; + BYTE m_padding2[64]; + }; +}; + +#ifdef _MSC_VER +#pragma warning(default: 4324) // structure was padded due to __declspec(align()) +#endif + +//-------------------------------------------------------------------------- +//PerAppDomainTPCountList maintains the collection of per-appdomain thread +//pool states. Per appdomain counts are added to the list during appdomain +//creation inside the sdomain lock. The counts are reset during appdomain +//unload after all the threads have +//This class maintains the count of per-appdomain work-items queued by +//ThreadPool.QueueUserWorkItem. It also dispatches threads in the appdomain +//correctly by setting up the right exception handling frames etc. +// +//Note: The counts are not accurate, and neither do they need to be. The +//actual work queue is in managed (implemented in threadpool.cs). This class +//just provides heuristics to the thread pool scheduler, along with +//synchronization to indicate start/end of requests to the scheduler. +class PerAppDomainTPCountList{ +public: + static void InitAppDomainIndexList(); + static void ResetAppDomainIndex(TPIndex index); + static void ResetAppDomainTPCounts(TPIndex index); + static bool AreRequestsPendingInAnyAppDomains(); + static LONG GetAppDomainIndexForThreadpoolDispatch(); + static void SetAppDomainId(TPIndex index, ADID id); + static TPIndex AddNewTPIndex(); + static void SetAppDomainUnloading(TPIndex index) + { + WRAPPER_NO_CONTRACT; + IPerAppDomainTPCount * pAdCount = dac_cast<PTR_IPerAppDomainTPCount> (s_appDomainIndexList.Get(index.m_dwIndex-1)); + _ASSERTE(pAdCount); + pAdCount->SetAppDomainUnloading(); + } + + static void ClearAppDomainUnloading(TPIndex index) + { + WRAPPER_NO_CONTRACT; + IPerAppDomainTPCount * pAdCount = dac_cast<PTR_IPerAppDomainTPCount> (s_appDomainIndexList.Get(index.m_dwIndex-1)); + _ASSERTE(pAdCount); + pAdCount->ClearAppDomainUnloading(); + } + + typedef Holder<TPIndex, SetAppDomainUnloading, ClearAppDomainUnloading> AppDomainUnloadingHolder; + + inline static IPerAppDomainTPCount* GetPerAppdomainCount(TPIndex index) + { + return dac_cast<PTR_IPerAppDomainTPCount>(s_appDomainIndexList.Get(index.m_dwIndex-1)); + } + + inline static UnManagedPerAppDomainTPCount* GetUnmanagedTPCount() + { + return &s_unmanagedTPCount; + } + +private: + static DWORD FindFirstFreeTpEntry(); + + static BYTE s_padding[64 - sizeof(LONG)]; + DECLSPEC_ALIGN(64) static LONG s_ADHint; + DECLSPEC_ALIGN(64) static UnManagedPerAppDomainTPCount s_unmanagedTPCount; + + //The list of all per-appdomain work-request counts. + static ArrayListStatic s_appDomainIndexList; +}; + +#endif //_THREADPOOL_REQUEST_H
\ No newline at end of file |