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
43class IPerAppDomainTPCount{
44public:
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
68typedef DPTR(IPerAppDomainTPCount) PTR_IPerAppDomainTPCount;
69
70static 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.
87class ManagedPerAppDomainTPCount : public IPerAppDomainTPCount {
88public:
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
183private:
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.
199class UnManagedPerAppDomainTPCount : public IPerAppDomainTPCount {
200public:
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
288private:
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.
316class PerAppDomainTPCountList{
317public:
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
353private:
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