| 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 | |