ReactOS  0.4.15-dev-3181-g4acf100
fxinterruptthreadpoolum.cpp
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7  FxInterruptThreadpoolUm.cpp
8 
9 Abstract:
10 
11  Threadpool functions for interrupt handling
12 
13 Author:
14 
15 
16 
17 
18 Environment:
19 
20  User mode only
21 
22 Revision History:
23 
24 --*/
25 
26 #include "fxmin.hpp"
27 #include "FxInterruptThreadpoolUm.hpp"
28 
29 extern "C" {
30 #include "FxInterruptThreadpoolUm.tmh"
31 }
32 
33 #define STRSAFE_LIB
34 #include <strsafe.h>
35 
37  PFX_DRIVER_GLOBALS FxDriverGlobals
38  ) :
39  FxGlobalsStump(FxDriverGlobals),
40  m_Pool(NULL),
41  m_MinimumThreadCount(MINIMUM_THREAD_COUNT_DEFAULT)
42 {
43  InitializeThreadpoolEnvironment(&m_CallbackEnvironment);
44 }
45 
47 {
48  //
49  // close pool
50  //
51  if (m_Pool != NULL) {
52  CloseThreadpool(m_Pool);
53  m_Pool = NULL;
54  }
55 
56  DestroyThreadpoolEnvironment(&m_CallbackEnvironment);
57 }
58 
59 HRESULT
62  _Out_ FxInterruptThreadpool** ppThreadpool
63  )
64 {
65  HRESULT hr;
67 
68  FX_VERIFY_WITH_NAME(INTERNAL, CHECK_NOT_NULL(ppThreadpool),
69  DriverGlobals->Public.DriverName);
70 
71  *ppThreadpool = NULL;
72 
74  if (pool == NULL)
75  {
79  "FxInterruptThreadpool creation failed, "
80  "%!hresult!", hr);
81  return hr;
82  }
83 
84  hr = pool->Initialize();
85 
86  if (SUCCEEDED(hr))
87  {
88  *ppThreadpool = pool;
89  }
90  else {
91  delete pool;
92  }
93 
94  return hr;
95 }
96 
97 HRESULT
99  )
100 {
101  HRESULT hr = S_OK;
102  DWORD error;
103  BOOL bRet;
104 
105  //
106  // Create a thread pool using win32 APIs
107  //
108  m_Pool = CreateThreadpool(NULL);
109  if (m_Pool == NULL)
110  {
111  error = GetLastError();
115  "Threadpool creation failed, %!winerr!", error);
116  return hr;
117  }
118 
119  //
120  // Set maximum thread count to equal the total number of interrupts.
121  // Set minimum thread count (persistent threads) to equal the lower of
122  // number of interrupt and number of processors.
123  //
124  // We want minimum number of persistent threads to be at least equal to the
125  // number of interrupt objects so that there is no delay in servicing the
126  // interrupt if there are processors available (the delay can come due to
127  // thread pool delay in allocating and initializing a new thread). However,
128  // there is not much benefit in having more persistent threads than
129  // processors, as threads would have to wait for processor to become
130  // available anyways. Therefore, we chose to have the number of persistent
131  // equal to the Min(number of interrupts, number of processors).
132  //
133  // In the current design, if there are more interrupts than
134  // processors, as soon as one thread finishes servicing the interrupt, both
135  // the thread and processor will be available to service the next queued
136  // interrupt. Note that thread pool will queue all the waits and will
137  // satisfy them in a FIFO manner.
138  //
139  // Since we don't know the number of interrupts in the beginning, we will
140  // start with one and update it when we know the actual number of interrupts
141  // after processing driver's OnPrepareHardware callback.
142  //
143  // Note on interrupt servicing:
144  // When fx connects the interrupt, it queues an event-based wait block
145  // to thread pool for each interrupt, so that the interrupt can get serviced
146  // using one of the thread pool threads when the auto-reset event shared
147  // with reflector is set by reflector in the DpcForIsr routine queued by Isr.
148  // While the interrupt is being serviced by one of the threads, no wait block
149  // is queued to thread pool for that interrupt, so arrival of same interrupt
150  // signals the event in DpcForIsr but doesn't cause new threads to pick up
151  // the servicing. Note that other interrupts still have their wait blocks
152  // queued so they will be serviced as they arrive.
153  //
154  // When previous servicing is over (i.e. the ISR callback has been
155  // invoked and it has returned), Fx queues another wait block to thread pool
156  // for that interrupt. If the event is already signalled, it would result in
157  // the same thread (or another) picking up servicing immediately.
158  //
159  // This means the interrupt ISR routine can never run concurrently
160  // for same interrupt. Therefore, there is no need to have more than one
161  // thread for each interrupt.
162  //
163  SetThreadpoolThreadMaximum(m_Pool, 1);
164 
165  //
166  // Create one persistent thread since atleast one interrupt is the most
167  // likely scenario. We will update this number to have either the same
168  // number of threads as there are interrupts, or the number of
169  // processors on the machine.
170  //
171  bRet = SetThreadpoolThreadMinimum(m_Pool, m_MinimumThreadCount);
172  if (bRet == FALSE) {
173  error = GetLastError();
177  "%!FUNC!: Failed to set minimum threads (%d) in threadpool,"
178  " %!winerr!", m_MinimumThreadCount, error);
179  goto cleanup;
180  }
181 
182  //
183  // Associate thread pool with callback environment.
184  //
185  SetThreadpoolCallbackPool(&m_CallbackEnvironment, m_Pool);
186 
187 cleanup:
188 
189  if (FAILED(hr)) {
190  CloseThreadpool(m_Pool);
191  m_Pool = NULL;
192  }
193 
194  return hr;
195 }
196 
197 HRESULT
199  _In_ ULONG InterruptCount
200  )
201 {
202  BOOL bRet;
203  HRESULT hr = S_OK;
204  SYSTEM_INFO sysInfo;
205  ULONG minThreadCount = 0;
206  ULONG procs;
207  DWORD error;
208 
209  FX_VERIFY(INTERNAL, CHECK_NOT_NULL(m_Pool));
210 
211  //
212  // if there are more than one interrupts then we need to update minimum
213  // thread count.
214  //
215  if (m_MinimumThreadCount >= InterruptCount) {
216  //
217  // nothing to do
218  //
219  return S_OK;
220  }
221 
222  //
223  // We want to have number of minimum persistent threads
224  // = Min(number of interrupts, number of processors).
225  // See comments in Initialize routine for details.
226  //
227  GetSystemInfo(&sysInfo);
228  procs = sysInfo.dwNumberOfProcessors;
229 
230  minThreadCount = min(InterruptCount, procs);
231 
232  if (m_MinimumThreadCount < minThreadCount) {
233  //
234  // Set threadpool min
235  //
236  bRet = SetThreadpoolThreadMinimum(m_Pool, minThreadCount);
237  if (bRet == FALSE) {
238  error = GetLastError();
242  "Failed to set minimum threads in threadpool,"
243  " TP_POOL 0x%p to %d %!winerr!", m_Pool, minThreadCount,
244  error);
245  return hr;
246  }
247 
248  m_MinimumThreadCount = minThreadCount;
249  }
250 
251  //
252  // set thread pool max to max number of interrupts
253  //
254  SetThreadpoolThreadMaximum(m_Pool, InterruptCount);
255 
258  "Threads in thread pool TP_POOL 0x%p updated"
259  " to Max %d Min %d threads", m_Pool,
260  InterruptCount, minThreadCount);
261 
262  return hr;
263 }
264 
266  VOID
267  )
268 {
269  //
270  // close the thread pool wait structure
271  //
272  if (m_Wait) {
273  //
274  // Make sure no event is registered.
275  //
277 
278  //
279  // Wait for all the callbacks to finish.
280  //
282 
283  //
284  // close the wait
285  //
287 
288  m_Wait = NULL;
289  }
290 
291  //
292  // close event handle
293  //
294  if (m_Event) {
296  m_Event = NULL;
297  }
298 }
299 
300 HRESULT
302  _In_ FxInterruptThreadpool* Threadpool,
304  _In_ PTP_WAIT_CALLBACK WaitCallback,
305  _Out_ FxInterruptWaitblock** Waitblock
306  )
307 {
308  HRESULT hr = S_OK;
309  FxInterruptWaitblock* waitblock = NULL;
310  PFX_DRIVER_GLOBALS driverGlobals;
311 
312  FX_VERIFY(INTERNAL, CHECK_NOT_NULL(Waitblock));
313  *Waitblock = NULL;
314  driverGlobals = Interrupt->GetDriverGlobals();
315 
316  //
317  // create an instance of interrupt wait block
318  //
319  waitblock = new (driverGlobals) FxInterruptWaitblock(driverGlobals);
320  if (waitblock == NULL) {
321  hr = E_OUTOFMEMORY;
322  DoTraceLevelMessage(driverGlobals,
324  "Waitblock creation failed %!hresult!", hr);
325  goto exit;
326  }
327 
328  hr = waitblock->Initialize(Threadpool,
329  Interrupt,
330  WaitCallback);
331  if (SUCCEEDED(hr)) {
332  *Waitblock = waitblock;
333  }
334  else {
335  DoTraceLevelMessage(driverGlobals,
337  "Waitblock init failed %!hresult!", hr);
338  }
339 
340 exit:
341 
342  if(FAILED(hr) && waitblock != NULL) {
343  delete waitblock;
344  }
345 
346  return hr;
347 }
348 
349 HRESULT
351  __in FxInterruptThreadpool* Threadpool,
353  __in PTP_WAIT_CALLBACK WaitCallback
354  )
355 {
356  HRESULT hr = S_OK;
357  DWORD error;
358 
359  //
360  // create a per-interrupt auto-reset event, non-signalled to begin with.
361  //
363  NULL, // LPSECURITY_ATTRIBUTES lpEventAttributes,
364  FALSE, // BOOL bManualReset,
365  FALSE, // BOOL bInitialState,
366  NULL // LPCTSTR lpName
367  );
368 
369  if (m_Event == NULL) {
370  error = GetLastError();
374  "Event creation failed for FxInterrupt object"
375  " %!winerr!", error);
376  goto exit;
377  }
378 
379  //
380  // create a per-interrupt thread pool wait structure. This wait structure is
381  // needed to associate an event-based wait callback with threadpool.
382  //
383  m_Wait = Threadpool->CreateThreadpoolWait(WaitCallback,
384  Interrupt);
385  if (m_Wait == NULL) {
386  error = GetLastError();
390  "Event creation failed for FxInterrupt object"
391  " %!winerr!", error);
392  goto exit;
393  }
394 
395 exit:
396 
397  return hr;
398 }
399 
400 
#define CreateEvent
Definition: winbase.h:3604
#define CloseHandle
Definition: compat.h:598
#define HRESULT_FROM_WIN32(x)
Definition: winerror.h:92
HRESULT hr
Definition: shlfolder.c:183
#define error(str)
Definition: mkdosfs.c:1605
#define _Out_
Definition: ms_sal.h:345
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_INTERRUPT_CONFIG _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFINTERRUPT * Interrupt
Definition: wdfinterrupt.h:372
static HRESULT _CreateAndInit(_In_ PFX_DRIVER_GLOBALS DriverGlobals, _Out_ FxInterruptThreadpool **ppThreadpool)
#define ERROR_NOT_ENOUGH_MEMORY
Definition: dderror.h:7
DWORD WINAPI GetLastError(VOID)
Definition: except.c:1040
static HRESULT _CreateAndInit(_In_ FxInterruptThreadpool *Threadpool, _In_ FxInterrupt *Interrupt, _In_ PTP_WAIT_CALLBACK WaitCallback, _Out_ FxInterruptWaitblock **Waitblock)
DriverGlobals
FX_VERIFY_WITH_NAME(DRIVER(BadArgument, TODO), CHECK(ERROR_STRING_HW_ACCESS_NOT_ALLOWED,(pDevice->IsDirectHardwareAccessAllowed()==TRUE)), DriverGlobals->DriverName)
FxInterruptWaitblock(PFX_DRIVER_GLOBALS FxDriverGlobals)
#define E_OUTOFMEMORY
Definition: ddrawi.h:100
#define FALSE
Definition: types.h:117
unsigned int BOOL
Definition: ntddk_ex.h:94
if SUCCEEDED(hr)
HRESULT Initialize(__in FxInterruptThreadpool *Threadpool, __in FxInterrupt *Interrupt, __in PTP_WAIT_CALLBACK WaitCallback)
#define _In_
Definition: ms_sal.h:308
FX_VERIFY(INTERNAL, CHECK_NOT_NULL(LoaderInterface->pIWudfHost))
LONG HRESULT
Definition: typedefs.h:79
unsigned long DWORD
Definition: ntddk_ex.h:95
HRESULT UpdateThreadPoolThreadLimits(_In_ ULONG InterruptCount)
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
#define TRACINGPNP
Definition: dbgtrace.h:67
#define S_OK
Definition: intsafe.h:52
TP_CALLBACK_ENVIRON m_CallbackEnvironment
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP, "Enter, WDFDEVICE %p", Device)
VOID WINAPI GetSystemInfo(IN LPSYSTEM_INFO lpSystemInfo)
Definition: sysinfo.c:142
#define min(a, b)
Definition: monoChain.cc:55
#define NULL
Definition: types.h:112
#define MINIMUM_THREAD_COUNT_DEFAULT
DWORD dwNumberOfProcessors
Definition: winbase.h:1159
unsigned int ULONG
Definition: retypes.h:1
char * cleanup(char *str)
Definition: wpickclick.c:99
static TP_CALLBACK_ENVIRON *static PTP_WAIT_CALLBACK
Definition: threadpool.c:27
void exit(int exitcode)
Definition: _exit.c:33
#define __in
Definition: dbghelp.h:35
FxInterruptThreadpool(PFX_DRIVER_GLOBALS FxDriverGlobals)
PFX_DRIVER_GLOBALS GetDriverGlobals(VOID)
Definition: fxstump.hpp:98