| 1 | // Licensed to the .NET Foundation under one or more agreements. | 
|---|
| 2 | // The .NET Foundation licenses this file to you under the MIT license. | 
|---|
| 3 | // See the LICENSE file in the project root for more information. | 
|---|
| 4 |  | 
|---|
| 5 | //========================================================================= | 
|---|
| 6 |  | 
|---|
| 7 | // | 
|---|
| 8 | // ThreadPoolRequest.h | 
|---|
| 9 | // | 
|---|
| 10 |  | 
|---|
| 11 | // | 
|---|
| 12 | // This file contains definitions of classes needed to mainain per-appdomain | 
|---|
| 13 | // thread pool work requests. This is needed as unmanaged and managed work | 
|---|
| 14 | // requests are allocted, managed and dispatched in drastically different ways. | 
|---|
| 15 | // However, the scheduler need be aware of these differences, and it should | 
|---|
| 16 | // simply talk to a common interface for managing work request counts. | 
|---|
| 17 | // | 
|---|
| 18 | //========================================================================= | 
|---|
| 19 |  | 
|---|
| 20 | #ifndef _THREADPOOL_REQUEST_H | 
|---|
| 21 | #define _THREADPOOL_REQUEST_H | 
|---|
| 22 |  | 
|---|
| 23 | #include "util.hpp" | 
|---|
| 24 |  | 
|---|
| 25 | #define TP_QUANTUM 2 | 
|---|
| 26 | #define UNUSED_THREADPOOL_INDEX (DWORD)-1 | 
|---|
| 27 |  | 
|---|
| 28 | //-------------------------------------------------------------------------- | 
|---|
| 29 | //IPerAppDomainTPCount is an interface for implementing per-appdomain thread | 
|---|
| 30 | //pool state. It's implementation should include logic to maintain work-counts, | 
|---|
| 31 | //notify thread pool class when work arrives or no work is left. Finally | 
|---|
| 32 | //there is logic to dipatch work items correctly in the right domain. | 
|---|
| 33 | // | 
|---|
| 34 | //Notes: | 
|---|
| 35 | //This class was designed to support both the managed and unmanaged uses | 
|---|
| 36 | //of thread pool. The unmananged part may directly be used through com | 
|---|
| 37 | //interfaces. The differences between the actual management of counts and | 
|---|
| 38 | //dispatching of work is quite different between the two. This interface | 
|---|
| 39 | //hides these differences to the thread scheduler implemented by the thread pool | 
|---|
| 40 | //class. | 
|---|
| 41 | // | 
|---|
| 42 |  | 
|---|
| 43 | class IPerAppDomainTPCount{ | 
|---|
| 44 | public: | 
|---|
| 45 | virtual void ResetState() = 0; | 
|---|
| 46 | virtual BOOL IsRequestPending() = 0; | 
|---|
| 47 |  | 
|---|
| 48 | //This functions marks the begining of requests queued for the domain. | 
|---|
| 49 | //It needs to notify the scheduler of work-arrival among other things. | 
|---|
| 50 | virtual void SetAppDomainRequestsActive() = 0; | 
|---|
| 51 |  | 
|---|
| 52 | //This functions marks the end of requests queued for this domain. | 
|---|
| 53 | virtual void ClearAppDomainRequestsActive(BOOL bADU = FALSE) = 0; | 
|---|
| 54 |  | 
|---|
| 55 | //Clears the "active" flag if it was set, and returns whether it was set. | 
|---|
| 56 | virtual bool TakeActiveRequest() = 0; | 
|---|
| 57 |  | 
|---|
| 58 | //Takes care of dispatching requests in the right domain. | 
|---|
| 59 | virtual void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled) = 0; | 
|---|
| 60 | virtual void SetAppDomainId(ADID id) = 0; | 
|---|
| 61 | virtual void SetTPIndexUnused() = 0; | 
|---|
| 62 | virtual BOOL IsTPIndexUnused() = 0; | 
|---|
| 63 | virtual void SetTPIndex(TPIndex index) = 0; | 
|---|
| 64 | virtual void SetAppDomainUnloading() = 0; | 
|---|
| 65 | virtual void ClearAppDomainUnloading() = 0; | 
|---|
| 66 | }; | 
|---|
| 67 |  | 
|---|
| 68 | typedef DPTR(IPerAppDomainTPCount) PTR_IPerAppDomainTPCount; | 
|---|
| 69 |  | 
|---|
| 70 | static const LONG ADUnloading = -1; | 
|---|
| 71 |  | 
|---|
| 72 | #ifdef _MSC_VER | 
|---|
| 73 | // Disable this warning - we intentionally want __declspec(align()) to insert padding for us | 
|---|
| 74 | #pragma warning(disable: 4324) // structure was padded due to __declspec(align()) | 
|---|
| 75 | #endif | 
|---|
| 76 |  | 
|---|
| 77 | //-------------------------------------------------------------------------- | 
|---|
| 78 | //ManagedPerAppDomainTPCount maintains per-appdomain thread pool state. | 
|---|
| 79 | //This class maintains the count of per-appdomain work-items queued by | 
|---|
| 80 | //ThreadPool.QueueUserWorkItem. It also dispatches threads in the appdomain | 
|---|
| 81 | //correctly by setting up the right exception handling frames etc. | 
|---|
| 82 | // | 
|---|
| 83 | //Note: The counts are not accurate, and neither do they need to be. The | 
|---|
| 84 | //actual work queue is in managed (implemented in threadpool.cs). This class | 
|---|
| 85 | //just provides heuristics to the thread pool scheduler, along with | 
|---|
| 86 | //synchronization to indicate start/end of requests to the scheduler. | 
|---|
| 87 | class ManagedPerAppDomainTPCount : public IPerAppDomainTPCount { | 
|---|
| 88 | public: | 
|---|
| 89 |  | 
|---|
| 90 | ManagedPerAppDomainTPCount(TPIndex index) {ResetState(); m_index = index;} | 
|---|
| 91 |  | 
|---|
| 92 | inline void ResetState() | 
|---|
| 93 | { | 
|---|
| 94 | LIMITED_METHOD_CONTRACT; | 
|---|
| 95 | VolatileStore(&m_numRequestsPending, (LONG)0); | 
|---|
| 96 | m_id.m_dwId = 0; | 
|---|
| 97 | } | 
|---|
| 98 |  | 
|---|
| 99 | inline BOOL IsRequestPending() | 
|---|
| 100 | { | 
|---|
| 101 | LIMITED_METHOD_CONTRACT; | 
|---|
| 102 |  | 
|---|
| 103 | LONG count = VolatileLoad(&m_numRequestsPending); | 
|---|
| 104 | return count != ADUnloading && count > 0; | 
|---|
| 105 | } | 
|---|
| 106 |  | 
|---|
| 107 | void SetAppDomainRequestsActive(); | 
|---|
| 108 | void ClearAppDomainRequestsActive(BOOL bADU); | 
|---|
| 109 | bool TakeActiveRequest(); | 
|---|
| 110 |  | 
|---|
| 111 | inline void SetAppDomainId(ADID id) | 
|---|
| 112 | { | 
|---|
| 113 | LIMITED_METHOD_CONTRACT; | 
|---|
| 114 | //This function should be called during appdomain creation when no managed code | 
|---|
| 115 | //has started running yet. That implies, no requests should be pending | 
|---|
| 116 | //or dispatched to this structure yet. | 
|---|
| 117 |  | 
|---|
| 118 | _ASSERTE(VolatileLoad(&m_numRequestsPending) != ADUnloading); | 
|---|
| 119 | _ASSERTE(m_id.m_dwId == 0); | 
|---|
| 120 |  | 
|---|
| 121 | m_id = id; | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | inline void SetTPIndex(TPIndex index) | 
|---|
| 125 | { | 
|---|
| 126 | LIMITED_METHOD_CONTRACT; | 
|---|
| 127 | //This function should be called during appdomain creation when no managed code | 
|---|
| 128 | //has started running yet. That implies, no requests should be pending | 
|---|
| 129 | //or dispatched to this structure yet. | 
|---|
| 130 |  | 
|---|
| 131 | _ASSERTE(VolatileLoad(&m_numRequestsPending) != ADUnloading); | 
|---|
| 132 | _ASSERTE(m_id.m_dwId == 0); | 
|---|
| 133 | _ASSERTE(m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX); | 
|---|
| 134 |  | 
|---|
| 135 | m_index = index; | 
|---|
| 136 | } | 
|---|
| 137 |  | 
|---|
| 138 | inline BOOL IsTPIndexUnused() | 
|---|
| 139 | { | 
|---|
| 140 | LIMITED_METHOD_CONTRACT; | 
|---|
| 141 | if (m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX) | 
|---|
| 142 | { | 
|---|
| 143 | //This function is called during appdomain creation, and no new appdomains can be | 
|---|
| 144 | //added removed at this time. So, make sure that the per-appdomain structures that | 
|---|
| 145 | //have been cleared(reclaimed) don't have any pending requests to them. | 
|---|
| 146 |  | 
|---|
| 147 | _ASSERTE(VolatileLoad(&m_numRequestsPending) != ADUnloading); | 
|---|
| 148 | _ASSERTE(m_id.m_dwId == 0); | 
|---|
| 149 |  | 
|---|
| 150 | return TRUE; | 
|---|
| 151 | } | 
|---|
| 152 |  | 
|---|
| 153 | return FALSE; | 
|---|
| 154 | } | 
|---|
| 155 |  | 
|---|
| 156 | inline void SetTPIndexUnused() | 
|---|
| 157 | { | 
|---|
| 158 | WRAPPER_NO_CONTRACT; | 
|---|
| 159 | //This function should be called during appdomain unload when all threads have | 
|---|
| 160 | //succesfully exited the appdomain. That implies, no requests should be pending | 
|---|
| 161 | //or dispatched to this structure. | 
|---|
| 162 |  | 
|---|
| 163 | _ASSERTE(m_id.m_dwId == 0); | 
|---|
| 164 |  | 
|---|
| 165 | m_index.m_dwIndex = UNUSED_THREADPOOL_INDEX; | 
|---|
| 166 | } | 
|---|
| 167 |  | 
|---|
| 168 | inline void SetAppDomainUnloading() | 
|---|
| 169 | { | 
|---|
| 170 | LIMITED_METHOD_CONTRACT; | 
|---|
| 171 | VolatileStore(&m_numRequestsPending, ADUnloading); | 
|---|
| 172 | } | 
|---|
| 173 |  | 
|---|
| 174 | inline void ClearAppDomainUnloading(); | 
|---|
| 175 |  | 
|---|
| 176 | inline BOOL IsAppDomainUnloading() | 
|---|
| 177 | { | 
|---|
| 178 | return VolatileLoad(&m_numRequestsPending) == ADUnloading; | 
|---|
| 179 | } | 
|---|
| 180 |  | 
|---|
| 181 | void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled); | 
|---|
| 182 |  | 
|---|
| 183 | private: | 
|---|
| 184 | ADID m_id; | 
|---|
| 185 | TPIndex m_index; | 
|---|
| 186 | DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct { | 
|---|
| 187 | BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)]; | 
|---|
| 188 | // Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange | 
|---|
| 189 | LONG m_numRequestsPending; | 
|---|
| 190 | BYTE m_padding2[MAX_CACHE_LINE_SIZE]; | 
|---|
| 191 | }; | 
|---|
| 192 | }; | 
|---|
| 193 |  | 
|---|
| 194 | //-------------------------------------------------------------------------- | 
|---|
| 195 | //UnManagedPerAppDomainTPCount maintains the thread pool state/counts for | 
|---|
| 196 | //unmanaged work requests. From thread pool point of view we treat unmanaged | 
|---|
| 197 | //requests as a special "appdomain". This helps in scheduling policies, and | 
|---|
| 198 | //follow same fairness policies as requests in other appdomains. | 
|---|
| 199 | class UnManagedPerAppDomainTPCount : public IPerAppDomainTPCount { | 
|---|
| 200 | public: | 
|---|
| 201 |  | 
|---|
| 202 | UnManagedPerAppDomainTPCount() | 
|---|
| 203 | { | 
|---|
| 204 | LIMITED_METHOD_CONTRACT; | 
|---|
| 205 | ResetState(); | 
|---|
| 206 | } | 
|---|
| 207 |  | 
|---|
| 208 | inline void InitResources() | 
|---|
| 209 | { | 
|---|
| 210 | CONTRACTL | 
|---|
| 211 | { | 
|---|
| 212 | THROWS; | 
|---|
| 213 | MODE_ANY; | 
|---|
| 214 | GC_NOTRIGGER; | 
|---|
| 215 | INJECT_FAULT(COMPlusThrowOM()); | 
|---|
| 216 | } | 
|---|
| 217 | CONTRACTL_END; | 
|---|
| 218 |  | 
|---|
| 219 | } | 
|---|
| 220 |  | 
|---|
| 221 | inline void CleanupResources() | 
|---|
| 222 | { | 
|---|
| 223 | } | 
|---|
| 224 |  | 
|---|
| 225 | inline void ResetState() | 
|---|
| 226 | { | 
|---|
| 227 | LIMITED_METHOD_CONTRACT; | 
|---|
| 228 | m_NumRequests = 0; | 
|---|
| 229 | VolatileStore(&m_outstandingThreadRequestCount, (LONG)0); | 
|---|
| 230 | } | 
|---|
| 231 |  | 
|---|
| 232 | inline BOOL IsRequestPending() | 
|---|
| 233 | { | 
|---|
| 234 | LIMITED_METHOD_CONTRACT; | 
|---|
| 235 | return VolatileLoad(&m_outstandingThreadRequestCount) != (LONG)0 ? TRUE : FALSE; | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | void SetAppDomainRequestsActive(); | 
|---|
| 239 |  | 
|---|
| 240 | inline void ClearAppDomainRequestsActive(BOOL bADU) | 
|---|
| 241 | { | 
|---|
| 242 | LIMITED_METHOD_CONTRACT; | 
|---|
| 243 | VolatileStore(&m_outstandingThreadRequestCount, (LONG)0); | 
|---|
| 244 | } | 
|---|
| 245 |  | 
|---|
| 246 | bool TakeActiveRequest(); | 
|---|
| 247 |  | 
|---|
| 248 | inline void SetAppDomainId(ADID id) | 
|---|
| 249 | { | 
|---|
| 250 | } | 
|---|
| 251 |  | 
|---|
| 252 | void QueueUnmanagedWorkRequest(LPTHREAD_START_ROUTINE  function, PVOID context); | 
|---|
| 253 | PVOID DeQueueUnManagedWorkRequest(bool* lastOne); | 
|---|
| 254 |  | 
|---|
| 255 | void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled); | 
|---|
| 256 |  | 
|---|
| 257 | inline void SetTPIndexUnused() | 
|---|
| 258 | { | 
|---|
| 259 | WRAPPER_NO_CONTRACT; | 
|---|
| 260 | _ASSERT(FALSE); | 
|---|
| 261 | } | 
|---|
| 262 |  | 
|---|
| 263 | inline BOOL IsTPIndexUnused() | 
|---|
| 264 | { | 
|---|
| 265 | WRAPPER_NO_CONTRACT; | 
|---|
| 266 | _ASSERT(FALSE); | 
|---|
| 267 | return FALSE; | 
|---|
| 268 | } | 
|---|
| 269 |  | 
|---|
| 270 | inline void SetTPIndex(TPIndex index) | 
|---|
| 271 | { | 
|---|
| 272 | WRAPPER_NO_CONTRACT; | 
|---|
| 273 | _ASSERT(FALSE); | 
|---|
| 274 | } | 
|---|
| 275 |  | 
|---|
| 276 | inline void SetAppDomainUnloading() | 
|---|
| 277 | { | 
|---|
| 278 | WRAPPER_NO_CONTRACT; | 
|---|
| 279 | _ASSERT(FALSE); | 
|---|
| 280 | } | 
|---|
| 281 |  | 
|---|
| 282 | inline void ClearAppDomainUnloading() | 
|---|
| 283 | { | 
|---|
| 284 | WRAPPER_NO_CONTRACT; | 
|---|
| 285 | _ASSERT(FALSE); | 
|---|
| 286 | } | 
|---|
| 287 |  | 
|---|
| 288 | private: | 
|---|
| 289 | SpinLock m_lock; | 
|---|
| 290 | ULONG m_NumRequests; | 
|---|
| 291 | DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) struct { | 
|---|
| 292 | BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)]; | 
|---|
| 293 | // Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange | 
|---|
| 294 | LONG m_outstandingThreadRequestCount; | 
|---|
| 295 | BYTE m_padding2[MAX_CACHE_LINE_SIZE]; | 
|---|
| 296 | }; | 
|---|
| 297 | }; | 
|---|
| 298 |  | 
|---|
| 299 | #ifdef _MSC_VER | 
|---|
| 300 | #pragma warning(default: 4324)  // structure was padded due to __declspec(align()) | 
|---|
| 301 | #endif | 
|---|
| 302 |  | 
|---|
| 303 | //-------------------------------------------------------------------------- | 
|---|
| 304 | //PerAppDomainTPCountList maintains the collection of per-appdomain thread | 
|---|
| 305 | //pool states. Per appdomain counts are added to the list during appdomain | 
|---|
| 306 | //creation inside the sdomain lock. The counts are reset during appdomain | 
|---|
| 307 | //unload after all the threads have | 
|---|
| 308 | //This class maintains the count of per-appdomain work-items queued by | 
|---|
| 309 | //ThreadPool.QueueUserWorkItem. It also dispatches threads in the appdomain | 
|---|
| 310 | //correctly by setting up the right exception handling frames etc. | 
|---|
| 311 | // | 
|---|
| 312 | //Note: The counts are not accurate, and neither do they need to be. The | 
|---|
| 313 | //actual work queue is in managed (implemented in threadpool.cs). This class | 
|---|
| 314 | //just provides heuristics to the thread pool scheduler, along with | 
|---|
| 315 | //synchronization to indicate start/end of requests to the scheduler. | 
|---|
| 316 | class PerAppDomainTPCountList{ | 
|---|
| 317 | public: | 
|---|
| 318 | static void InitAppDomainIndexList(); | 
|---|
| 319 | static void ResetAppDomainIndex(TPIndex index); | 
|---|
| 320 | static void ResetAppDomainTPCounts(TPIndex index); | 
|---|
| 321 | static bool AreRequestsPendingInAnyAppDomains(); | 
|---|
| 322 | static LONG GetAppDomainIndexForThreadpoolDispatch(); | 
|---|
| 323 | static void SetAppDomainId(TPIndex index, ADID id); | 
|---|
| 324 | static TPIndex AddNewTPIndex(); | 
|---|
| 325 | static void SetAppDomainUnloading(TPIndex index) | 
|---|
| 326 | { | 
|---|
| 327 | WRAPPER_NO_CONTRACT; | 
|---|
| 328 | IPerAppDomainTPCount * pAdCount = dac_cast<PTR_IPerAppDomainTPCount> (s_appDomainIndexList.Get(index.m_dwIndex-1)); | 
|---|
| 329 | _ASSERTE(pAdCount); | 
|---|
| 330 | pAdCount->SetAppDomainUnloading(); | 
|---|
| 331 | } | 
|---|
| 332 |  | 
|---|
| 333 | static void ClearAppDomainUnloading(TPIndex index) | 
|---|
| 334 | { | 
|---|
| 335 | WRAPPER_NO_CONTRACT; | 
|---|
| 336 | IPerAppDomainTPCount * pAdCount = dac_cast<PTR_IPerAppDomainTPCount> (s_appDomainIndexList.Get(index.m_dwIndex-1)); | 
|---|
| 337 | _ASSERTE(pAdCount); | 
|---|
| 338 | pAdCount->ClearAppDomainUnloading(); | 
|---|
| 339 | } | 
|---|
| 340 |  | 
|---|
| 341 | typedef Holder<TPIndex, SetAppDomainUnloading, ClearAppDomainUnloading> AppDomainUnloadingHolder; | 
|---|
| 342 |  | 
|---|
| 343 | inline static IPerAppDomainTPCount* GetPerAppdomainCount(TPIndex index) | 
|---|
| 344 | { | 
|---|
| 345 | return dac_cast<PTR_IPerAppDomainTPCount>(s_appDomainIndexList.Get(index.m_dwIndex-1)); | 
|---|
| 346 | } | 
|---|
| 347 |  | 
|---|
| 348 | inline static UnManagedPerAppDomainTPCount* GetUnmanagedTPCount() | 
|---|
| 349 | { | 
|---|
| 350 | return &s_unmanagedTPCount; | 
|---|
| 351 | } | 
|---|
| 352 |  | 
|---|
| 353 | private: | 
|---|
| 354 | static DWORD FindFirstFreeTpEntry(); | 
|---|
| 355 |  | 
|---|
| 356 | static BYTE s_padding[MAX_CACHE_LINE_SIZE - sizeof(LONG)]; | 
|---|
| 357 | DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG s_ADHint; | 
|---|
| 358 | DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static UnManagedPerAppDomainTPCount s_unmanagedTPCount; | 
|---|
| 359 |  | 
|---|
| 360 | //The list of all per-appdomain work-request counts. | 
|---|
| 361 | static ArrayListStatic s_appDomainIndexList; | 
|---|
| 362 | }; | 
|---|
| 363 |  | 
|---|
| 364 | #endif //_THREADPOOL_REQUEST_H | 
|---|
| 365 |  | 
|---|