ReactOS  0.4.15-dev-2704-gd5265b0
fxsystemthread.cpp
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7  FxSystemThread.cpp
8 
9 Abstract:
10 
11  This is the implementation of the FxSystemThread object.
12 
13 
14 Author:
15 
16 
17 
18 Environment:
19 
20  Kernel mode only
21 
22 Revision History:
23 
24 
25 --*/
26 
27 #include "fxcorepch.hpp"
28 
29 extern "C" {
30 // #include "FxSystemThread.tmh"
31 }
32 
33 
35  __in PFX_DRIVER_GLOBALS FxDriverGlobals
36  ) :
37  FxNonPagedObject(FX_TYPE_SYSTEMTHREAD, 0, FxDriverGlobals)
38 {
39 
41  m_ThreadPtr = NULL;
42  m_Exit = FALSE;
43  m_PEThread = NULL;
44 
45  //m_Spinup.WorkerRoutine = NULL; // Async-Thread-Spinup
47 
51 }
52 
54 
55 }
56 
61  __in PFX_DRIVER_GLOBALS FxDriverGlobals,
62  __in WDFDEVICE Device,
64  )
65 {
67  FxSystemThread *pThread = NULL;
68 
69  *SystemThread = NULL;
70 
71  pThread = new(FxDriverGlobals) FxSystemThread(FxDriverGlobals);
72  if (pThread == NULL) {
74 
76  FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
77  "WDFDEVICE 0x%p !devobj %p could not allocate a thread for handling "
78  "power requests %!STATUS!",
80 
81  return status;
82  }
83 
84  if (pThread->Initialize() == FALSE) {
86 
88  FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGPNP,
89  "WDFDEVICE 0x%p, !devobj %p, could not initialize power thread, "
90  "%!STATUS!",
92 
93  pThread->DeleteFromFailedCreate();
94 
95  return status;
96  }
97 
98  *SystemThread = pThread;
99 
101 
102  return status;
103 }
104 
105 //
106 // Create the system thread in order to be able to service work items
107 //
108 // It is recommended this is done from the system process context
109 // since the threads handle is available to the user mode process
110 // for a temporary window. XP and later supports OBJ_KERNELHANDLE, but
111 // DriverFrameworks must support W2K with the same binary.
112 //
113 // It is safe to call this at DriverEntry which is in the system process
114 // to create an initial driver thread, and this driver thread should be
115 // used for creating any child driver threads on demand.
116 //
117 BOOLEAN
119 {
121  KIRQL irql;
122 
123  Lock(&irql);
124 
125  // Frameworks bug if this is called with a thread already running
126  ASSERT(m_ThreadPtr == NULL);
127 
129 
131 
132  Unlock(irql);
133 
134  status = CreateThread();
135 
136  return NT_SUCCESS(status) ? TRUE : FALSE;
137 }
138 
140 NTSTATUS
142  VOID
143  )
144 {
145  HANDLE threadHandle;
147 
148  //
149  // Take an extra object reference on ourselves to account
150  // for the system thread referencing the object while it is running.
151  //
152  // The thread itself will release this reference in its exit routine
153  //
155 
157  &threadHandle,
159  NULL, // Obja
160  NULL, // hProcess
161  NULL, // CLIENT_ID,
163  this
164  );
165 
166  if (!NT_SUCCESS(status)) {
168 
170  "Could not create system thread %!STATUS!", status);
171  //
172  // Release the reference taken above due to failure
173  //
175  }
176  else {
178  threadHandle,
180  NULL,
181  KernelMode,
182  &m_ThreadPtr,
183  NULL
184  );
185 
187 
188  // We can now close the thread handle since we have a pointer reference
189  ZwClose(threadHandle);
190  }
191 
192  return status;
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 // This is called to tell the thread to exit.
232 //
233 // It must be called from thread context such as
234 // the driver unload routine since it will wait for the
235 // thread to exit.
236 //
237 BOOLEAN
239 {
241  KIRQL irql;
242 
243  Lock(&irql);
244 
245  if( !m_Initialized ) {
246  Unlock(irql);
247  return TRUE;
248  }
249 
250  if( m_Exit ) {
251  ASSERT(FALSE); // This is not race free, so don't allow it
252  Unlock(irql);
253  return TRUE;
254  }
255 
256  // Tell the system thread to exit
257  m_Exit = TRUE;
258 
259  //
260  // The thread could still be spinning up, so we must handle this condition.
261  //
262  if( m_ThreadPtr == NULL ) {
263 
264  Unlock(irql);
265 
267 
268  // Wait for thread to start
270 
272 
275 
276  //
277  // Now we have a thread, wait for it to go away
278  //
279  ASSERT(m_ThreadPtr != NULL);
280  }
281  else {
282  Unlock(irql);
283  }
284 
285  m_WorkEvent.Set();
286 
287  //
288  // We can't be waiting in our own thread for the thread to exit.
289  //
291 
293 
294  // Wait for thread to exit
296  m_ThreadPtr,
297  Executive,
298  KernelMode,
299  FALSE,
300  NULL
301  );
302 
304 
307 
309 
310  //
311  // Now safe to unload the driver or object
312  // the thread worker is pointing to
313  //
314 
315  return TRUE;
316 }
317 
318 BOOLEAN
320  __inout FxSystemThread* Reaper
321  )
322 {
323  KIRQL irql;
324 
325  //
326  // Without a top level reaper, the frameworks can not ensure
327  // all worker threads have exited at driver unload in the async
328  // case. If this is a top level thread, then DriverUnload should
329  // call the synchronous ExitThread() method.
330  //
331  ASSERT(Reaper != NULL);
332 
333  Lock(&irql);
334 
335  if( !m_Initialized ) {
336  Unlock(irql);
337  return TRUE;
338  }
339 
340  if( m_Exit ) {
341  ASSERT(FALSE);
342  Unlock(irql);
343  return TRUE;
344  }
345 
346  // Tell the system thread to exit
347  m_Exit = TRUE;
348 
349  // Add a reference which will be released by the reaper
351 
352  Unlock(irql);
353 
354  m_WorkEvent.Set();
355 
357 
358  // We are the only user of this field protected by setting m_Exit
360  m_Reaper.Parameter = this;
361 
362  Reaper->QueueWorkItem(&m_Reaper);
363 
364  //
365  // It is not safe to unload the driver until the reaper
366  // thread has processed the work item and waited for this
367  // thread to exit.
368  //
369 
370  return TRUE;
371 }
372 
373 BOOLEAN
376  )
377 {
378  KIRQL irql;
379  BOOLEAN result;
380 
381  Lock(&irql);
382 
383  if (m_Exit) {
384  result = FALSE;
385  }
386  else {
387  result = TRUE;
389 
390  m_WorkEvent.Set();
391  }
392 
393  Unlock(irql);
394 
395  return result;
396 }
397 
398 //
399 // Attempt to cancel the work item.
400 //
401 // Returns TRUE if success.
402 //
403 // If returns FALSE, the work item
404 // routine either has been called, is running,
405 // or is about to be called.
406 //
407 BOOLEAN
410  )
411 {
413  KIRQL irql;
414 
415  Lock(&irql);
416 
418 
419  while( Entry != &this->m_WorkList ) {
420 
421  if( Entry == &WorkItem->List ) {
422  RemoveEntryList(&WorkItem->List);
423  Unlock(irql);
424  return TRUE;
425  }
426 
427  Entry = Entry->Flink;
428  }
429 
430  // Not found
431  Unlock(irql);
432 
433  return FALSE;
434 }
435 
436 VOID
438 {
442  PWORK_QUEUE_ITEM pItem;
443 
444  //DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
445  // "Created, entering work loop");
446 
447  //
448  // The worker thread will process all list entries before
449  // checking for exit. This allows the top level FxDriver
450  // thread to process all deferred cleanup work items at
451  // driver unload.
452  //
453  // CancelWorkItem may be used to remove entries
454  // before requesting a thread exit if the caller wants to
455  // interrupt work queue processing.
456  //
457 
459 
460  // Initialize for IsCurrentThread()
462 
463  // Set the event that the thread now exists
464  m_InitEvent.Set();
465 
466  for ( ; ; ) {
467  KIRQL irql;
468 
469  //
470  // Lock the list so we are in sync with QueueWorkItem
471  //
472  Lock(&irql);
473 
474  while(!IsListEmpty(&m_WorkList)) {
475  //
476  // Instead of popping each LIST_ENTRY off of the old list and
477  // enqueueing it to the local list head, manipulate the first and
478  // last entry in the list to point to our new head.
479  //
480  head.Flink = m_WorkList.Flink;
481  head.Blink = m_WorkList.Blink;
482 
483  // First link in the list point backwrad to the new head
484  m_WorkList.Flink->Blink = &head;
485 
486  // Last link in the list point fwd to the new head
487  m_WorkList.Blink->Flink = &head;
488 
489  ASSERT(!IsListEmpty(&head));
490 
491  //
492  // Reinitialize the work list head
493  //
495 
496  //
497  // Process the workitems while unlocked so that the work item can
498  // requeue itself if needed and work at passive level.
499  //
500  Unlock(irql);
501 
502  while (!IsListEmpty(&head)) {
503  ple = RemoveHeadList(&head);
505  pItem->WorkerRoutine(pItem->Parameter);
506 
507  //
508  // NOTE: pItem may have been pool released by the called worker
509  // routine, so it may not be accessed after the callback
510  //
511  }
512 
513  //
514  // Relock the list and repeat until the work list is empty
515  //
516  Lock(&irql);
517  }
518 
519  // No more items on list, check for exit
520  if (m_Exit) {
521  //DoTraceLevelMessage(FxDriverGlobals, TRACE_LEVEL_ERROR, TRACINGDEVICE,
522  // "Terminating");
523 
524  Unlock(irql);
525 
526  // Release the object reference held by the thread
528 
532 
533  // NOT REACHED
534  return;
535  }
536 
537  //
538  // No work to do, clear event under lock to prevent a race with a work
539  // enqueue
540  //
541  m_WorkEvent.Clear();
542 
543  Unlock(irql);
544 
545  // We are a system thread, so we do not need KeEnterCriticalRegion()
548 
551  }
552 
553  // NOT REACHED
554  return;
555 }
556 
557 
558 
559 
560 
561 
562 
563 
564 
565 
566 
567 
568 
569 
570 
571 
572 
573 
574 
575 
576 
577 
578 
579 
580 
581 
582 
583 VOID
585 {
587  KIRQL irql;
588 
589  Lock(&irql);
590 
592  ASSERT(m_Exit);
593 
594  //
595  // The thread could still be spinning up, so we must handle this condition.
596  //
597  if( m_ThreadPtr == NULL ) {
598 
599  Unlock(irql);
600 
602 
603  // Wait for thread to start
605 
607 
610 
611  //
612  // Now we have a thread, wait for it to go away
613  //
614  ASSERT(m_ThreadPtr != NULL);
615  }
616  else {
617  Unlock(irql);
618  }
619 
621 
622  // Wait for thread to exit
624  m_ThreadPtr,
625  Executive,
626  KernelMode,
627  FALSE,
628  NULL
629  );
630 
632 
635 
637 
639 
640  return;
641 }
642 
643 VOID
646  )
647 {
649 
650  thread->Thread();
651 }
652 
653 
654 
655 
656 
657 
658 
659 
660 
661 
662 
663 
664 
665 
666 
667 VOID
670  )
671 {
673 
674  thread->Reaper();
675 }
676 
677 
__drv_restoresIRQL KIRQL __in BOOLEAN Unlock
Definition: fxobject.hpp:1474
BOOLEAN QueueWorkItem(__inout PWORK_QUEUE_ITEM WorkItem)
WORK_QUEUE_ITEM m_Reaper
#define THREAD_ALL_ACCESS
Definition: nt_native.h:1339
#define _Must_inspect_result_
Definition: no_sal2.h:62
static VOID STDCALL StaticReaperThunk(__inout PVOID Context)
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
struct _Entry Entry
Definition: kefuncs.h:627
GLuint64EXT * result
Definition: glext.h:11304
struct outqueuenode * head
Definition: adnsresfilter.c:66
#define TRUE
Definition: types.h:120
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
NTSYSAPI NTSTATUS NTAPI ZwClose(_In_ HANDLE Handle)
struct _LIST_ENTRY * Blink
Definition: typedefs.h:122
BOOLEAN Initialize(VOID)
LONG NTSTATUS
Definition: precomp.h:26
__inline VOID Set()
Definition: mxeventkm.h:91
KIRQL irql
Definition: wave.h:1
#define RELEASE(_tag)
Definition: fxobject.hpp:50
#define InsertTailList(ListHead, Entry)
NTSTATUS NTAPI KeWaitForSingleObject(IN PVOID Object, IN KWAIT_REASON WaitReason, IN KPROCESSOR_MODE WaitMode, IN BOOLEAN Alertable, IN PLARGE_INTEGER Timeout OPTIONAL)
Definition: wait.c:416
volatile PVOID Parameter
Definition: extypes.h:205
_Must_inspect_result_ FORCEINLINE BOOLEAN IsListEmpty(_In_ const LIST_ENTRY *ListHead)
Definition: rtlfuncs.h:57
virtual ~FxSystemThread(VOID)
PSINGLE_LIST_ENTRY ple
FORCEINLINE BOOLEAN RemoveEntryList(_In_ PLIST_ENTRY Entry)
Definition: rtlfuncs.h:105
UCHAR KIRQL
Definition: env_spec_w32.h:591
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
NTSTATUS NTAPI ObReferenceObjectByHandle(IN HANDLE Handle, IN ACCESS_MASK DesiredAccess, IN POBJECT_TYPE ObjectType, IN KPROCESSOR_MODE AccessMode, OUT PVOID *Object, OUT POBJECT_HANDLE_INFORMATION HandleInformation OPTIONAL)
Definition: obref.c:494
CHECK_RETURN_IF_USER_MODE __inline NTSTATUS Initialize(__in EVENT_TYPE Type, __in BOOLEAN InitialState)
Definition: mxeventkm.h:55
BOOLEAN ExitThreadAsync(__inout FxSystemThread *Reaper)
#define FALSE
Definition: types.h:117
#define ADDREF(_tag)
Definition: fxobject.hpp:49
static VOID NTAPI SystemThread(_In_ PVOID Context)
unsigned char BOOLEAN
__inline BOOLEAN IsCurrentThread()
FORCEINLINE PLIST_ENTRY RemoveHeadList(_Inout_ PLIST_ENTRY ListHead)
Definition: rtlfuncs.h:128
__inline VOID Clear()
Definition: mxeventkm.h:102
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
BOOLEAN ExitThread(VOID)
Status
Definition: gdiplustypes.h:24
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
_Must_inspect_result_ __inline NTSTATUS WaitFor(__in KWAIT_REASON WaitReason, __in KPROCESSOR_MODE WaitMode, __in BOOLEAN Alertable, __in_opt PLARGE_INTEGER Timeout)
Definition: mxeventkm.h:115
#define TRACINGDEVICE
Definition: dbgtrace.h:58
static _Must_inspect_result_ NTSTATUS _CreateAndInit(__deref_out FxSystemThread **SystemThread, __in PFX_DRIVER_GLOBALS FxDriverGlobals, __in WDFDEVICE Device, __in MdDeviceObject DeviceObject)
LIST_ENTRY m_WorkList
#define ASSERT(a)
Definition: mode.c:44
MdEThread m_PEThread
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
#define ObDereferenceObject
Definition: obfuncs.h:203
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
static HANDLE thread
Definition: service.c:33
PWORKER_THREAD_ROUTINE WorkerRoutine
Definition: extypes.h:204
_Must_inspect_result_ _In_ WDFCMRESLIST List
Definition: wdfresource.h:550
#define KeEnterCriticalRegion()
Definition: ke_x.h:88
#define __inout
Definition: dbghelp.h:50
Definition: typedefs.h:119
FxSystemThread(__in PFX_DRIVER_GLOBALS FxDriverGlobals)
_Must_inspect_result_ _In_ WDFDEVICE Device
Definition: wdfchildlist.h:474
static __inline MdEThread GetCurrentEThread()
Definition: mxgeneralkm.h:69
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
#define TRACINGPNP
Definition: dbgtrace.h:67
#define KeLeaveCriticalRegion()
Definition: ke_x.h:119
__inline PFX_DRIVER_GLOBALS GetDriverGlobals(VOID)
Definition: fxobject.hpp:734
static VOID STDCALL StaticThreadThunk(__inout PVOID Context)
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
NTSTATUS NTAPI PsTerminateSystemThread(IN NTSTATUS ExitStatus)
Definition: kill.c:1144
NTSTATUS NTAPI PsCreateSystemThread(OUT PHANDLE ThreadHandle, IN ACCESS_MASK DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes, IN HANDLE ProcessHandle, IN PCLIENT_ID ClientId, IN PKSTART_ROUTINE StartRoutine, IN PVOID StartContext)
Definition: thread.c:602
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP, "Enter, WDFDEVICE %p", Device)
#define NULL
Definition: types.h:112
VOID DeleteFromFailedCreate(VOID)
Definition: fxobject.cpp:391
#define __deref_out
Definition: dbghelp.h:26
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWAITLOCK * Lock
Definition: wdfsync.h:124
#define STATUS_SUCCESS
Definition: shellext.h:65
_Must_inspect_result_ _In_ PWDF_WORKITEM_CONFIG _In_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWORKITEM * WorkItem
Definition: wdfworkitem.h:110
#define __in
Definition: dbghelp.h:35
static SERVICE_STATUS status
Definition: service.c:31
_Must_inspect_result_ NTSTATUS CreateThread(VOID)
base of all file and directory entries
Definition: entries.h:82
BOOLEAN CancelWorkItem(__inout PWORK_QUEUE_ITEM WorkItem)
Definition: ps.c:97