summaryrefslogtreecommitdiff
path: root/src/vm/threadpoolrequest.h
blob: 3b2da28b5663682afe383d9f323dbaff2b2a0735 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
// 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

#include "util.hpp"

#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() = 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 SetTPIndexUnused() = 0;
    virtual BOOL IsTPIndexUnused() = 0;
    virtual void SetTPIndex(TPIndex index) = 0; 
};

typedef DPTR(IPerAppDomainTPCount) PTR_IPerAppDomainTPCount;

#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);
    }
    
    inline BOOL IsRequestPending()
    {
        LIMITED_METHOD_CONTRACT;

        LONG count = VolatileLoad(&m_numRequestsPending);
        return count > 0;
    }

    void SetAppDomainRequestsActive();
    void ClearAppDomainRequestsActive();
    bool TakeActiveRequest();

    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(m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX);

        m_index = index;
    }
    
    inline BOOL IsTPIndexUnused()
    {
        LIMITED_METHOD_CONTRACT;
        if (m_index.m_dwIndex == UNUSED_THREADPOOL_INDEX)
        {
            return TRUE;
        }

        return FALSE;
    }

    inline void SetTPIndexUnused()
    {
        WRAPPER_NO_CONTRACT;
        m_index.m_dwIndex = UNUSED_THREADPOOL_INDEX;
    }

    void DispatchWorkItem(bool* foundWork, bool* wasNotRecalled);

private:
    TPIndex m_index;
    struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
        BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
        // Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
        LONG m_numRequestsPending;
        BYTE m_padding2[MAX_CACHE_LINE_SIZE];
    };
};

//--------------------------------------------------------------------------
//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()
    {
        LIMITED_METHOD_CONTRACT;
        VolatileStore(&m_outstandingThreadRequestCount, (LONG)0);
    }

    bool TakeActiveRequest();

    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 ULONG GetNumRequests()
    {
        LIMITED_METHOD_CONTRACT;
        return VolatileLoad(&m_NumRequests);
    }

private:
    SpinLock m_lock;
    ULONG m_NumRequests;
    struct DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) {
        BYTE m_padding1[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
        // Only use with VolatileLoad+VolatileStore+FastInterlockCompareExchange
        LONG m_outstandingThreadRequestCount;
        BYTE m_padding2[MAX_CACHE_LINE_SIZE];
    };
};

#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 bool AreRequestsPendingInAnyAppDomains();
    static LONG GetAppDomainIndexForThreadpoolDispatch();
    static TPIndex AddNewTPIndex();

    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[MAX_CACHE_LINE_SIZE - sizeof(LONG)];
    DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static LONG s_ADHint;
    DECLSPEC_ALIGN(MAX_CACHE_LINE_SIZE) static UnManagedPerAppDomainTPCount s_unmanagedTPCount;

    //The list of all per-appdomain work-request counts.
    static ArrayListStatic s_appDomainIndexList;
};

#endif //_THREADPOOL_REQUEST_H