ReactOS  0.4.15-dev-2701-g34593d9
eventqueue.cpp
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) Microsoft. All rights reserved.
4 
5 Module Name:
6 
7  EventQueue.cpp
8 
9 Abstract:
10 
11  This module implements a baseline event queue structure which takes care of
12  90% of the work requireed to run a state machine
13 
14 Author:
15 
16 
17 
18 Environment:
19 
20  Both kernel and user mode
21 
22 Revision History:
23 
24 
25 
26 --*/
27 
28 #include "pnppriv.hpp"
29 
30 extern "C" {
31 #if defined(EVENT_TRACING)
32 #include "EventQueue.tmh"
33 #endif
34 }
35 
37  __in UCHAR QueueDepth
38  )
39 {
40  m_PkgPnp = NULL;
42 
43  m_HistoryIndex = 0;
44  m_QueueHead = 0;
45  m_QueueTail = 0;
46  m_QueueDepth = QueueDepth;
47 
49  m_QueueFlags = 0x0;
51 }
52 
57  )
58 {
60 
61  //
62  // For KM, lock initialize always succeeds. For UM, it might fail.
63  //
65  if (!NT_SUCCESS(status)) {
68  "Initializing state machine lock failed for EventQueue 0x%p, "
69  "status %!STATUS!",
70  this, status);
71  }
72 
73  return status;
74 }
75 
76 VOID
78  __in FxPkgPnp* Pnp,
81  )
82 {
83  m_PkgPnp = Pnp;
86 
87  return;
88 }
89 
90 BOOLEAN
93  )
94 /*++
95 
96 Routine Description:
97  Puts the queue into a closed state. If the queue cannot be closed and
98  finished in this context, the Event is stored and set when it moves into
99  the finished state
100 
101 Arguments:
102  Event - the event to set when we move into the finished state
103 
104 Return Value:
105  TRUE if the queue is closed in this context, FALSE if the Event should be
106  waited on.
107 
108  --*/
109 {
110  KIRQL irql;
111  BOOLEAN result;
112 
113  result = TRUE;
114 
115  Lock(&irql);
118 
119  result = IsIdleLocked();
120 
121  if (result == FALSE) {
123  }
124 
125  Unlock(irql);
126 
127  if (result) {
128  Event->Set();
129  }
130 
131  return result;
132 }
133 
134 VOID
136  VOID
137  )
138 {
139  KIRQL irql;
140 
143  "WDFDEVICE 0x%p !devobj 0x%p delaying deletion to outside state machine",
146 
147  Lock(&irql);
150  Unlock(irql);
151 }
152 
153 BOOLEAN
155  VOID
156  )
157 /*++
158 
159 Routine Description:
160  Generic worker function which encapsulates the logic of whether to enqueue
161  onto a different thread if the thread has not already been queued to.
162 
163  NOTE: this function could have been virtual, or call a virtual worker function
164  once we have determined that we need to queue to a thread. But to save
165  space on vtable storage (why have one unless you really need one?),
166  we rearrange the code so that the derived class calls the worker function
167  and this function indicates in its return value what the caller should
168  do
169 
170 Arguments:
171  None
172 
173 Return Value:
174  TRUE if the caller should queue to a thread to do the work
175  FALSE if the caller shoudl not queue to a thread b/c it has already been
176  queued
177 
178  --*/
179 {
180  KIRQL irql;
181  BOOLEAN result;
182 
183  Lock(&irql);
184 
185  //
186  // For one reason or another, we couldn't run the state machine on this
187  // thread. So queue a work item to do it.
188  //
189  if (IsEmpty()) {
190  //
191  // There is no work to do. This means that the caller inserted the
192  // event into the queue, dropped the lock, and then another thread came
193  // in and processed the event.
194  //
195  // This check also helps in the rundown case when the queue is closing
196  // and the following happens between 2 thread:
197  // #1 #2
198  // insert event
199  // drop lock
200  // process event queue
201  // queue goes to empty, so event is set
202  // try to queue work item
203  //
204  result = FALSE;
205 
208  "WDFDEVICE 0x%p !devobj 0x%p not queueing work item to process "
209  "event queue", m_PkgPnp->GetDevice()->GetHandle(),
211  }
212  else if ((m_QueueFlags & FxEventQueueFlagWorkItemQueued) == 0x00) {
214  result = TRUE;
215  }
216  else {
217  //
218  // Somebody is already in the process of enqueuing the work item.
219  //
220  result = FALSE;
221  }
222 
223  Unlock(irql);
224 
225  return result;
226 }
227 
228 VOID
230  VOID
231  )
232 /*++
233 
234 Routine Description:
235  This is the work item that attempts to run the queue state machine on
236  the special power thread.
237 
238 
239 --*/
240 {
242  KIRQL irql;
243  FxPkgPnp* pPkgPnp;
244 
245 #if (FX_CORE_MODE==FX_CORE_KERNEL_MODE)
247 #endif
248 
249  //
250  // Cache away m_PkgPnp while we know we still have a valid object. Once
251  // we Unlock() after the worker routine, the object could be gone until
252  // the worker routine set a flag postponing deletion.
253  //
254  pPkgPnp = m_PkgPnp;
255 
256  Lock(&irql);
257 
259 
260  //
261  // Clear the queued flag, so that it's clear that the work item can
262  // be safely re-enqueued.
263  //
265 
266  //
267  // We should only see this count rise to a small number (like 10 or so).
268  //
271 
272  Unlock(irql);
273 
274  //
275  // Call the function that will actually run the state machine.
276  //
278 
279  Lock(&irql);
282  Unlock(irql);
283 
284  //
285  // NOTE: There is no need to use a reference count to keep this event queue
286  // (and the containing state machine) alive. Instead, the thread
287  // which wants to delete the state machine must wait for this work
288  // item to exit. If there was a reference to release, we would have
289  // a race between Unlock()ing and releasing the reference if the state
290  // machine moved into the finished state and deletes the device after
291  // we dropped the lock, but before we released the reference.
292  //
293  // This is important in that the device deletion can trigger
294  // DriverUnload to run before the release executes. DriverUnload
295  // frees the IFR buffer. If this potential release logs something to
296  // the IFR, you would bugcheck. Since it is impossible to defensively
297  // prevent all destructors from logging to the IFR, we can't use a
298  // ref count here to keep the queue alive.
299  //
300 
301  //
302  // If Evaluate needs to use pPkgPnp, then the call to the worker routine
303  // above made sure that pPkgPnp has not yet been freed.
304  //
305  info.Evaluate(pPkgPnp);
306 }
307 
309  __in UCHAR QueueDepth
310  ) : FxEventQueue(QueueDepth)
311 {
312 }
313 
315 {
316  m_WorkItem.Free();
317 }
318 
320 NTSTATUS
322  __inout FxPkgPnp* Pnp,
324  __in PVOID WorkerContext
325  )
326 {
328 
329  Configure(Pnp, WorkerRoutine, WorkerContext);
330 
331 
332 
333 
334 
335 
336 
337 
338 
339 
340 
343  );
344 
345  if (!NT_SUCCESS(status)) {
346  return status;
347  }
348 
349  return STATUS_SUCCESS;
350 }
351 
353  __in UCHAR QueueDepth
354  ) : FxEventQueue(QueueDepth)
355 {
358  this);
359 }
360 
362  VOID
363  )
364 {
365  m_WorkItem.Free();
366 }
367 
369 NTSTATUS
371  __inout FxPkgPnp* Pnp,
373  __in PVOID WorkerContext
374  )
375 {
377 
378  Configure(Pnp, WorkerRoutine, WorkerContext);
379 
380 
381  status = m_WorkItem.Allocate(Pnp->GetDevice()->GetDeviceObject());
382  if (!NT_SUCCESS(status)) {
383  return status;
384  }
385 
386  return STATUS_SUCCESS;
387 }
388 
389 VOID
392  )
393 {
395 
396  This->EventQueueWorker();
397 }
398 
399 VOID
401  VOID
402  )
403 {
404  if (m_PkgPnp->HasPowerThread()) {
405  //
406  // Use the power thread for the stack
407  //
409  }
410  else {
411  //
412  // Use the work item since the power thread is not available
413  //
415  (FxEventQueue*) this);
416  }
417 }
418 
419 VOID
423  )
424 /*++
425 
426 Routine Description:
427  This is the work item that attempts to run the machine on a thread
428  separate from the one the caller was using.
429 
430 --*/
431 {
433 
435 
436  This->EventQueueWorker();
437 }
438 
#define _Must_inspect_result_
Definition: no_sal2.h:62
__inline CfxDevice * GetDevice(VOID)
Definition: fxpackage.hpp:46
MdDeviceObject __inline GetDeviceObject(VOID)
Definition: fxdevice.hpp:174
GLint x0
Definition: linetemp.h:95
GLuint64EXT * result
Definition: glext.h:11304
VOID QueueToPowerThread(__in PWORK_QUEUE_ITEM WorkItem)
Definition: fxpkgpnp.hpp:3516
#define TRUE
Definition: types.h:120
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
WDFDEVICE __inline GetHandle(VOID)
Definition: fxdevice.hpp:237
CHECK_RETURN_IF_USER_MODE NTSTATUS Initialize()
Definition: fxwaitlock.hpp:235
_Must_inspect_result_ _In_ PFLT_CALLBACK_DATA _In_ PFLT_DEFERRED_IO_WORKITEM_ROUTINE WorkerRoutine
Definition: fltkernel.h:1977
LONG NTSTATUS
Definition: precomp.h:26
FxCREvent * m_WorkItemFinished
KIRQL irql
Definition: wave.h:1
DriverGlobals
FxThreadedEventQueue(__in UCHAR QueueDepth)
Definition: eventqueue.cpp:352
#define TRACE_LEVEL_INFORMATION
Definition: storswtr.h:29
_Must_inspect_result_ NTSTATUS Init(__inout FxPkgPnp *Pnp, __in PFN_PNP_EVENT_WORKER WorkerRoutine, __in PVOID WorkerContext=NULL)
Definition: eventqueue.cpp:370
static WORKER_THREAD_ROUTINE _WorkerThreadRoutine
UCHAR KIRQL
Definition: env_spec_w32.h:591
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
_Must_inspect_result_ NTSTATUS Initialize(__in PFX_DRIVER_GLOBALS DriverGlobals)
Definition: eventqueue.cpp:55
#define FALSE
Definition: types.h:117
VOID SetDelayedDeletion(VOID)
Definition: eventqueue.cpp:135
FxEventQueue(__in UCHAR QueueDepth)
Definition: eventqueue.cpp:36
VOID(* PFN_PNP_EVENT_WORKER)(__in FxPkgPnp *PkgPnp, __in FxPostProcessInfo *Info, __in PVOID Context)
VOID EventQueueWorker(VOID)
Definition: eventqueue.cpp:229
_In_ PVOID _In_ ULONG Event
Definition: iotypes.h:467
unsigned char BOOLEAN
PVOID m_EventWorkerContext
FxPkgPnp * pPkgPnp
_Must_inspect_result_ __inline NTSTATUS Allocate(__in MdDeviceObject DeviceObject, __in_opt PVOID ThreadPoolEnv=NULL)
Definition: mxworkitemkm.h:41
FxPkgPnp * m_PkgPnp
PFN_PNP_EVENT_WORKER m_EventWorker
#define ExInitializeWorkItem(Item, Routine, Context)
Definition: exfuncs.h:265
#define ASSERT(a)
Definition: mode.c:44
FxWorkItemEventQueue(__in UCHAR QueueDepth)
Definition: eventqueue.cpp:308
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
UCHAR m_WorkItemRunningCount
__inline VOID Enqueue(__in PMX_WORKITEM_ROUTINE Callback, __in PVOID Context)
Definition: mxworkitemkm.h:58
VOID QueueWorkItem(VOID)
Definition: eventqueue.cpp:400
struct _test_info info[]
Definition: SetCursorPos.c:19
unsigned char UCHAR
Definition: xmlstorage.h:181
#define __inout
Definition: dbghelp.h:50
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
VOID GetFinishedState(__inout FxPostProcessInfo *Info)
BOOLEAN QueueToThreadWorker(VOID)
Definition: eventqueue.cpp:154
BOOLEAN IsIdleLocked(VOID)
static MX_WORKITEM_ROUTINE _WorkItemCallback
#define TRACINGPNP
Definition: dbgtrace.h:67
WORK_QUEUE_ITEM m_EventWorkQueueItem
__inline PFX_DRIVER_GLOBALS GetDriverGlobals(VOID)
Definition: fxobject.hpp:734
BOOLEAN IsEmpty(VOID)
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP, "Enter, WDFDEVICE %p", Device)
_Must_inspect_result_ NTSTATUS Init(__inout FxPkgPnp *Pnp, __in PFN_PNP_EVENT_WORKER WorkerRoutine, __in PVOID WorkerContext=NULL)
Definition: eventqueue.cpp:321
BOOLEAN HasPowerThread(VOID)
Definition: fxpkgpnp.hpp:3508
#define NULL
Definition: types.h:112
__inline VOID Free()
Definition: mxworkitemkm.h:90
UCHAR m_HistoryIndex
struct tagContext Context
Definition: acpixf.h:1034
FX_TRACK_DRIVER(fxDriverGlobals)
_Must_inspect_result_ PVOID GetIoMgrObjectForWorkItemAllocation(VOID)
Definition: supportkm.cpp:189
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWAITLOCK * Lock
Definition: wdfsync.h:124
#define STATUS_SUCCESS
Definition: shellext.h:65
BOOLEAN SetFinished(__in FxCREvent *Event)
Definition: eventqueue.cpp:91
#define __in
Definition: dbghelp.h:35
static SERVICE_STATUS status
Definition: service.c:31
FxWaitLockInternal m_StateMachineLock
WORKER_THREAD_ROUTINE * PWORKER_THREAD_ROUTINE
Definition: extypes.h:200
VOID Configure(__in FxPkgPnp *Pnp, __in PFN_PNP_EVENT_WORKER WorkerRoutine, __in PVOID Context)
Definition: eventqueue.cpp:77
Definition: ps.c:97