ReactOS 0.4.15-dev-7934-g1dc8d80
fxsystemthread.cpp
Go to the documentation of this file.
1/*++
2
3Copyright (c) Microsoft Corporation
4
5Module Name:
6
7 FxSystemThread.cpp
8
9Abstract:
10
11 This is the implementation of the FxSystemThread object.
12
13
14Author:
15
16
17
18Environment:
19
20 Kernel mode only
21
22Revision History:
23
24
25--*/
26
27#include "fxcorepch.hpp"
28
29extern "C" {
30// #include "FxSystemThread.tmh"
31}
32
33
35 __in PFX_DRIVER_GLOBALS FxDriverGlobals
36 ) :
37 FxNonPagedObject(FX_TYPE_SYSTEMTHREAD, 0, FxDriverGlobals)
38{
39
42 m_Exit = FALSE;
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
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//
119{
121 KIRQL irql;
122
123 Lock(&irql);
124
125 // Frameworks bug if this is called with a thread already running
127
129
131
132 Unlock(irql);
133
135
136 return NT_SUCCESS(status) ? TRUE : FALSE;
137}
138
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,
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//
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 //
280 }
281 else {
282 Unlock(irql);
283 }
284
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
297 Executive,
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
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
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
376 )
377{
378 KIRQL irql;
380
381 Lock(&irql);
382
383 if (m_Exit) {
384 result = FALSE;
385 }
386 else {
387 result = TRUE;
389
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//
410 )
411{
413 KIRQL irql;
414
415 Lock(&irql);
416
418
419 while( Entry != &this->m_WorkList ) {
420
421 if( Entry == &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
436VOID
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
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
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)) {
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 //
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
583VOID
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 //
615 }
616 else {
617 Unlock(irql);
618 }
619
621
622 // Wait for thread to exit
625 Executive,
627 FALSE,
628 NULL
629 );
630
632
635
637
639
640 return;
641}
642
643VOID
646 )
647{
649
650 thread->Thread();
651}
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667VOID
670 )
671{
673
674 thread->Reaper();
675}
676
677
unsigned char BOOLEAN
static VOID NTAPI SystemThread(_In_ PVOID Context)
struct outqueuenode * head
Definition: adnsresfilter.c:66
LONG NTSTATUS
Definition: precomp.h:26
static HANDLE thread
Definition: service.c:33
__inline PFX_DRIVER_GLOBALS GetDriverGlobals(VOID)
Definition: fxobject.hpp:734
__drv_restoresIRQL KIRQL __in BOOLEAN Unlock
Definition: fxobject.hpp:1474
VOID DeleteFromFailedCreate(VOID)
Definition: fxobject.cpp:391
MdEThread m_PEThread
BOOLEAN CancelWorkItem(__inout PWORK_QUEUE_ITEM WorkItem)
_Must_inspect_result_ NTSTATUS CreateThread(VOID)
BOOLEAN QueueWorkItem(__inout PWORK_QUEUE_ITEM WorkItem)
BOOLEAN ExitThreadAsync(__inout FxSystemThread *Reaper)
static VOID STDCALL StaticThreadThunk(__inout PVOID Context)
WORK_QUEUE_ITEM m_Reaper
static VOID STDCALL StaticReaperThunk(__inout PVOID Context)
FxSystemThread(__in PFX_DRIVER_GLOBALS FxDriverGlobals)
__inline BOOLEAN IsCurrentThread()
static _Must_inspect_result_ NTSTATUS _CreateAndInit(__deref_out FxSystemThread **SystemThread, __in PFX_DRIVER_GLOBALS FxDriverGlobals, __in WDFDEVICE Device, __in MdDeviceObject DeviceObject)
virtual ~FxSystemThread(VOID)
BOOLEAN ExitThread(VOID)
BOOLEAN Initialize(VOID)
LIST_ENTRY m_WorkList
_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
__inline VOID Clear()
Definition: mxeventkm.h:102
__inline VOID Set()
Definition: mxeventkm.h:91
CHECK_RETURN_IF_USER_MODE __inline NTSTATUS Initialize(__in EVENT_TYPE Type, __in BOOLEAN InitialState)
Definition: mxeventkm.h:55
static __inline MdEThread GetCurrentEThread()
Definition: mxgeneralkm.h:69
#define __in
Definition: dbghelp.h:35
#define __deref_out
Definition: dbghelp.h:26
#define __inout
Definition: dbghelp.h:50
#define TRACINGPNP
Definition: dbgtrace.h:67
#define TRACINGDEVICE
Definition: dbgtrace.h:58
#define NULL
Definition: types.h:112
#define TRUE
Definition: types.h:120
#define FALSE
Definition: types.h:117
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
KIRQL irql
Definition: wave.h:1
#define RemoveEntryList(Entry)
Definition: env_spec_w32.h:986
#define InsertTailList(ListHead, Entry)
#define IsListEmpty(ListHead)
Definition: env_spec_w32.h:954
UCHAR KIRQL
Definition: env_spec_w32.h:591
#define KeWaitForSingleObject(pEvt, foo, a, b, c)
Definition: env_spec_w32.h:478
#define RemoveHeadList(ListHead)
Definition: env_spec_w32.h:964
#define InitializeListHead(ListHead)
Definition: env_spec_w32.h:944
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP, "Enter, WDFDEVICE %p", Device)
PSINGLE_LIST_ENTRY ple
#define ADDREF(_tag)
Definition: fxobject.hpp:49
#define RELEASE(_tag)
Definition: fxobject.hpp:50
@ FX_TYPE_SYSTEMTHREAD
Definition: fxtypes.h:66
Status
Definition: gdiplustypes.h:25
GLuint64EXT * result
Definition: glext.h:11304
#define KeLeaveCriticalRegion()
Definition: ke_x.h:119
#define KeEnterCriticalRegion()
Definition: ke_x.h:88
#define ASSERT(a)
Definition: mode.c:44
#define _Must_inspect_result_
Definition: ms_sal.h:558
#define KernelMode
Definition: asm.h:34
NTSYSAPI NTSTATUS NTAPI ZwClose(_In_ HANDLE Handle)
#define THREAD_ALL_ACCESS
Definition: nt_native.h:1339
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
@ NotificationEvent
NTSTATUS NTAPI PsTerminateSystemThread(IN NTSTATUS ExitStatus)
Definition: kill.c:1145
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
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
#define STATUS_SUCCESS
Definition: shellext.h:65
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
base of all file and directory entries
Definition: entries.h:83
Definition: typedefs.h:120
struct _LIST_ENTRY * Blink
Definition: typedefs.h:122
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
volatile PVOID Parameter
Definition: extypes.h:205
PWORKER_THREAD_ROUTINE WorkerRoutine
Definition: extypes.h:204
Definition: ps.c:97
#define CONTAINING_RECORD(address, type, field)
Definition: typedefs.h:260
#define STATUS_UNSUCCESSFUL
Definition: udferr_usr.h:132
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
_Must_inspect_result_ _In_ WDFDEVICE Device
Definition: wdfchildlist.h:474
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
_Must_inspect_result_ _In_ WDFCMRESLIST List
Definition: wdfresource.h:550
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWAITLOCK * Lock
Definition: wdfsync.h:127
_Must_inspect_result_ _In_ PWDF_WORKITEM_CONFIG _In_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWORKITEM * WorkItem
Definition: wdfworkitem.h:115
@ Executive
Definition: ketypes.h:415
#define ObDereferenceObject
Definition: obfuncs.h:203