ReactOS  0.4.15-dev-2700-g4b4ffa9
fxworkitem.cpp
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7  FxWorkItem.hpp
8 
9 Abstract:
10 
11  This module implements a frameworks managed WORKITEM that
12  can synchrononize with driver frameworks object locks.
13 
14 Author:
15 
16 
17 
18 Environment:
19 
20  Both kernel and user mode
21 
22 Revision History:
23 
24 
25 --*/
26 
27 #include "coreprivshared.hpp"
28 
29 #include "fxworkitem.hpp"
30 
31 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
32 //
33 // For DRIVER_OBJECT_UM definition
34 //
35 #include "fxldrum.h"
36 #endif
37 
38 // Tracing support
39 extern "C" {
40 // #include "FxWorkItem.tmh"
41 }
42 
44  __in PFX_DRIVER_GLOBALS FxDriverGlobals
45  ) :
47  m_WorkItemCompleted(NotificationEvent, TRUE)
48 {
49  m_Object = NULL;
50  m_Callback = NULL;
54  m_Enqueued = FALSE;
57 
58  //
59  // All operations on a workitem are PASSIVE_LEVEL so ensure that any Dispose
60  // and Destroy callbacks to the driver are as well.
61  //
63 
65 }
66 
67 
69  VOID
70  )
71 {
73 
75 
76  //
77  // If this hits, it's because someone destroyed the WORKITEM by
78  // removing too many references by mistake without calling WdfObjectDelete
79  //
80  if (m_RunningDown == FALSE && m_Callback != NULL) {
83  "WDFWORKITEM %p destroyed without calling WdfObjectDelete, or by "
84  "Framework processing DeviceRemove. Possible reference count "
85  "problem?", GetObjectHandleUnchecked());
87  }
88 
89  // Release our parent object reference
90  if (m_Object != NULL) {
91  m_Object->RELEASE(this);
92  m_Object = NULL;
93  }
94 
95  // Free the workitem
96  if (m_WorkItem.GetWorkItem() != NULL) {
97  m_WorkItem.Free();
98  //m_WorkItem = NULL;
99  }
100 
101  ASSERT(m_Enqueued == FALSE);
103 
104  return;
105 }
106 
108 NTSTATUS
110  __in PFX_DRIVER_GLOBALS FxDriverGlobals,
113  __in FxObject* ParentObject,
114  __out WDFWORKITEM* WorkItem
115  )
116 {
119 
120  pFxWorkItem = new(FxDriverGlobals, Attributes) FxWorkItem(FxDriverGlobals);
121 
122  if (pFxWorkItem == NULL) {
124  }
125 
127  Attributes,
128  Config,
129  ParentObject,
130  WorkItem
131  );
132 
133  if (!NT_SUCCESS(status)) {
135  }
136 
137  return status;
138 }
139 
141 NTSTATUS
145  __in FxObject* ParentObject,
146  __out WDFWORKITEM* WorkItem
147  )
148 {
150  IFxHasCallbacks* pCallbacks;
152 
154 
155 #if (FX_CORE_MODE == FX_CORE_USER_MODE)
157  if(!NT_SUCCESS(status)) {
159  "Could not initialize m_WorkItemCompleted event "
160  "%!STATUS!", status);
161  return status;
162  }
163 #endif
164 
165  ASSERT(Config->EvtWorkItemFunc != NULL);
166 
167  // Set users callback function
168  m_Callback = Config->EvtWorkItemFunc;
169 
170  //
171  // As long as we are associated, the parent object holds a reference
172  // count on the WDFWORKITEM.
173  //
174  // This reference must be taken early before we return any failure,
175  // since Dispose() expects this extra reference, and Dispose() will
176  // be called even if we return a failure status right now.
177  //
178  ADDREF(this);
179 
180  //
181  // WorkItems can be parented by, and optionally serialize with an FxDevice or an FxQueue
182  //
183  m_DeviceBase = FxDeviceBase::_SearchForDevice(ParentObject, &pCallbacks);
184 
185  if (m_DeviceBase == NULL) {
187  }
188 
189  //
190  // Determine if it's an FxDevice, or FxIoQueue and get the
191  // CallbackSpinLock pointer for it.
192  //
194  ParentObject,
195  pCallbacks,
196  Config->AutomaticSerialization,
197  TRUE,
200  );
201 
202  if (!NT_SUCCESS(status)) {
204 
205 
206 
207 
208 
209 
212  "ParentObject %p cannot automatically synchronize callbacks "
213  "with a WorkItem since it is not configured for passive level "
214  "callback constraints. Use a WDFDPC instead or set "
215  "AutomaticSerialization to FALSE."
216  "%!STATUS!", Attributes->ParentObject, status);
217  }
218 
219  return status;
220  }
221 
222  //
223  // Allocate the PIO_WORKITEM we will re-use
224  //
225 #if (FX_CORE_MODE == FX_CORE_KERNEL_MODE)
227 #elif (FX_CORE_MODE == FX_CORE_USER_MODE)
230  (PVOID)&m_Device->GetDriver()->GetDriverObject()->ThreadPoolEnv);
231 #endif
232  if (m_WorkItem.GetWorkItem() == NULL) {
236  "Could not allocate IoWorkItem, %!STATUS!", status);
237  return status;
238  }
239 
240  //
241  // We automatically synchronize with and reference count
242  // the lifetime of the framework object to prevent any WORKITEM races
243  // that can access the object while it is going away.
244  //
245 
246  //
247  // The caller supplied object is the object the caller wants the
248  // WorkItem to be associated with, and the framework must ensure this
249  // object remains live until the WorkItem object is destroyed. Otherwise,
250  // it could access either object context memory, or an object API
251  // on a freed object.
252  //
253  // Due to the locking model of the framework, the lock may actually
254  // be owned by a higher level object as well. This is the lockObject
255  // returned. As long as we are a child of this object, the lockObject
256  // does not need to be dereferenced since it will notify us of Cleanup
257  // before it goes away.
258  //
259 
260  //
261  // Associate the FxWorkItem with the object. When this object Cleans up, it
262  // will notify our Cleanup function as well.
263  //
264 
265  //
266  // Add a reference to the parent object we are associated with.
267  // We will be notified of Cleanup to release this reference.
268  //
269  ParentObject->ADDREF(this);
270 
271  // Save the ptr to the object the WorkItem is associated with
272  m_Object = ParentObject;
273 
274  //
275  // Attributes->ParentObject is the same as ParentObject. Since we already
276  // converted it to an object, use that.
277  //
278  status = Commit(Attributes, (WDFOBJECT*)WorkItem, ParentObject);
279 
280  if (!NT_SUCCESS(status)) {
281  return status;
282  }
283 
284  return status;
285 }
286 
287 VOID
289  VOID
290  )
291 {
293  KIRQL irql;
294  BOOLEAN enqueue;
295 
297  enqueue = FALSE;
298 
299  Lock(&irql);
300 
301  if (m_Enqueued) {
304  "Previously queued WDFWORKITEM 0x%p is already pending. "
305  "Ignoring the request to queue again", GetHandle());
306  }
307  else if (m_RunningDown) {
309  "WDFWORKITEM 0x%p is already deleted", GetHandle());
311  }
312  else {
314 
315  m_Enqueued = TRUE;
316 
317  //
318  // We are going to enqueue the work item. Reference this FxWorkItem
319  // object and Globals while they are outstanding.
320  // These will be released when the workitem completes.
321  //
324 
325  enqueue = TRUE;
326  }
327 
328  Unlock(irql);
329 
330  if (enqueue) {
332  }
333 
334  return;
335 }
336 
337 VOID
339  VOID
340  )
341 {
342  KIRQL irql;
343 
345 
346  Lock(&irql);
347 
348  //
349  // Mark the workitem as no longer enqueued and completed
350  //
351  // The handler is allowed to re-enqueue, so mark it before the callback
352  //
353  m_Enqueued = FALSE;
354 
356 
357  Unlock(irql);
358 
359  if (m_CallbackLock != NULL) {
361 #if FX_IS_KERNEL_MODE
363 #endif
366  }
367  else {
368 #if FX_IS_KERNEL_MODE
370 #endif
372  }
373 
374  Lock(&irql);
375 
377 
378  //
379  // The workitem can be re-enqueued by the drivers
380  // work item handler routine. We can't set the work
381  // item completed event until we are sure there are
382  // no outstanding work items.
383  //
384 
385  if (m_WorkItemRunningCount == 0L && m_Enqueued == FALSE) {
387  }
388 
389  Unlock(irql);
390 }
391 
392 VOID
396  )
397 /*++
398 
399 Routine Description:
400 
401  This is the static routine called by the kernels PIO_WORKITEM handler.
402 
403  A reference count was taken by Enqueue, which this routine releases
404  on return.
405 
406 Arguments:
407  DeviceObject - the devobj we passed to IoCreateWorkItem
408 
409  Context - the internal Fx object
410 
411 Return Value:
412  None
413 
414 --*/
415 {
416  FxWorkItem* pWorkItem;
418 
420 
421  pWorkItem = (FxWorkItem*)Context;
422  pFxDriverGlobals = pWorkItem->GetDriverGlobals();
423 
424  //
425  // Save the current worker thread object pointer. We will use this avoid
426  // deadlock if the driver tries to delete or flush the workitem from within
427  // the callback.
428  //
430 
431  pWorkItem->WorkItemHandler();
432 
433  pWorkItem->m_WorkItemThread = NULL;
434 
435  //
436  // Release the reference on the FxWorkItem and Globals taken when Enqueue
437  // was done. This may release the FxWorkItem if it is running down.
438  //
439  pWorkItem->RELEASE((PVOID)WorkItemThunk);
440 
441  //
442  // This may release the driver if it is running down.
443  //
445 }
446 
447 VOID
449  VOID
450  )
451 {
452  FxObject* pObject;
453 
454  //
455  // Wait for any outstanding workitem to complete if the workitem is not
456  // deleted from within the workitem callback to avoid deadlock.
457  //
459  WaitForSignal();
460  }
461 
462  //
463  // Release our reference count to the associated parent object if present
464  //
465  if (m_Object != NULL) {
466  pObject = m_Object;
467  m_Object = NULL;
468 
469  pObject->RELEASE(this);
470  }
471 
472  //
473  // Perform our final release to ourselves, destroying the FxWorkItem
474  //
475  RELEASE(this);
476 }
477 
478 BOOLEAN
480  VOID
481  )
482 /*++
483 
484 Routine Description:
485  Called when DeleteObject is called, or when the parent is being deleted or
486  Disposed.
487 
488 Arguments:
489  None
490 
491 Return Value:
492  TRUE if the cleanup routines should be invoked, FALSE otherwise
493 
494  --*/
495 {
496  KIRQL irql;
497 
498  Lock(&irql);
500  Unlock(irql);
501 
502  FlushAndRundown();
503 
504  return TRUE;
505 }
506 
507 VOID
509 {
511 
513 
516  "Calling WdfWorkItemFlush from within the WDFWORKITEM "
517  "%p callback will lead to deadlock, PRKTHREAD %p",
520  return;
521  }
522 
523  //
524  // Wait for any outstanding workitem to complete.
525  // The event is only set upon return from the callback
526  // into the driver *and* the driver did not re-queue
527  // the workitem. See similar comment in WorkItemHandler().
528  //
529  WaitForSignal();
530  return;
531 }
532 
533 VOID
535  VOID
536  )
537 {
538  LARGE_INTEGER timeOut;
540 
542 
543  timeOut.QuadPart = WDF_REL_TIMEOUT_IN_SEC(60);
544 
545  do {
547  if (status == STATUS_TIMEOUT) {
548  DbgPrint("Thread 0x%p is waiting on WDFWORKITEM 0x%p\n",
550  GetHandle());
551  }
552  else {
554  break;
555  }
556  } WHILE(TRUE);
557 
558 
560  return;
561 }
__drv_restoresIRQL KIRQL __in BOOLEAN Unlock
Definition: fxobject.hpp:1474
static MX_WORKITEM_ROUTINE WorkItemThunk
Definition: fxworkitem.hpp:216
CfxDevice * m_Device
Definition: fxobject.hpp:329
FxObject * pObject
BOOLEAN m_RunningDown
Definition: fxworkitem.hpp:56
#define _Must_inspect_result_
Definition: no_sal2.h:62
VOID WaitForSignal(VOID)
Definition: fxworkitem.cpp:534
CHECK_RETURN_IF_USER_MODE NTSTATUS Initialize(__in BOOLEAN InitialState=FALSE)
Definition: fxwaitlock.hpp:51
VOID FlushAndRundown()
Definition: fxworkitem.cpp:448
BOOLEAN m_Enqueued
Definition: fxworkitem.hpp:61
#define STATUS_INSUFFICIENT_RESOURCES
Definition: udferr_usr.h:158
VOID WorkItemHandler()
Definition: fxworkitem.cpp:338
MdDeviceObject __inline GetDeviceObject(VOID)
Definition: fxdevice.hpp:174
_Must_inspect_result_ NTSTATUS Commit(__in_opt PWDF_OBJECT_ATTRIBUTES Attributes, __out_opt WDFOBJECT *ObjectHandle, __in_opt FxObject *Parent=NULL, __in BOOLEAN AssignDriverAsDefaultParent=TRUE)
Definition: fxobject.cpp:904
ActualNumberDriverObjects * sizeof(PDRIVER_OBJECT)) PDRIVER_OBJECT *DriverObjectList
#define DbgPrint
Definition: loader.c:25
static _Must_inspect_result_ NTSTATUS _GetEffectiveLock(__in FxObject *Object, __in_opt IFxHasCallbacks *Callbacks, __in BOOLEAN AutomaticLocking, __in BOOLEAN PassiveCallbacks, __out FxCallbackLock **CallbackLock, __out_opt FxObject **CallbackLockObject)
Definition: fxobject.cpp:1044
__inline MdWorkItem GetWorkItem()
Definition: mxworkitemkm.h:73
#define __in_opt
Definition: dbghelp.h:38
#define TRUE
Definition: types.h:120
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
LONG NTSTATUS
Definition: precomp.h:26
MxThread m_WorkItemThread
Definition: fxworkitem.hpp:105
#define STATUS_INVALID_DEVICE_REQUEST
Definition: udferr_usr.h:138
FxCallbackLock * m_CallbackLock
Definition: fxworkitem.hpp:81
PFN_WDF_WORKITEM m_Callback
Definition: fxworkitem.hpp:92
KIRQL irql
Definition: wave.h:1
#define RELEASE(_tag)
Definition: fxobject.hpp:50
VOID Set(VOID)
Definition: fxwaitlock.hpp:144
FxWorkItem(__in PFX_DRIVER_GLOBALS FxDriverGlobals)
Definition: fxworkitem.cpp:43
virtual ~FxWorkItem()
Definition: fxworkitem.cpp:68
static FxDeviceBase * _SearchForDevice(__in FxObject *Object, __out_opt IFxHasCallbacks **Callbacks)
WDFWORKITEM GetHandle(VOID)
Definition: fxworkitem.hpp:193
#define STATUS_TIMEOUT
Definition: ntstatus.h:81
UCHAR KIRQL
Definition: env_spec_w32.h:591
_In_ PDEVICE_OBJECT DeviceObject
Definition: wdfdevice.h:2055
MxWorkItem m_WorkItem
Definition: fxworkitem.hpp:53
#define FALSE
Definition: types.h:117
#define TRACE_LEVEL_VERBOSE
Definition: storswtr.h:30
FORCEINLINE VOID FxPerfTraceWorkItem(_In_ PVOID DriverCallback)
#define ADDREF(_tag)
Definition: fxobject.hpp:49
virtual void Lock(__out PKIRQL PreviousIrql)=0
VOID MarkDisposeOverride(__in FxObjectLockState State=ObjectLock)
Definition: fxobject.hpp:1101
#define STATUS_WDF_INCOMPATIBLE_EXECUTION_LEVEL
Definition: wdfstatus.h:198
#define __out
Definition: dbghelp.h:62
NTSTATUS EnterCRAndWaitAndLeave(VOID)
Definition: fxwaitlock.hpp:87
unsigned char BOOLEAN
_Must_inspect_result_ __inline NTSTATUS Allocate(__in MdDeviceObject DeviceObject, __in_opt PVOID ThreadPoolEnv=NULL)
Definition: mxworkitemkm.h:41
void * PVOID
Definition: retypes.h:9
FxCREvent m_WorkItemCompleted
Definition: fxworkitem.hpp:98
VOID MarkPassiveCallbacks(__in FxObjectLockState State=ObjectLock)
Definition: fxobject.hpp:972
#define TRACINGDEVICE
Definition: dbgtrace.h:58
FxObject * m_CallbackLockObject
Definition: fxworkitem.hpp:87
PFX_DRIVER_GLOBALS pFxDriverGlobals
#define WHILE(constant)
Definition: fxmacros.hpp:226
#define ASSERT(a)
Definition: mode.c:44
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
VOID Clear(VOID)
Definition: fxwaitlock.hpp:152
VOID Enqueue()
Definition: fxworkitem.cpp:288
_Must_inspect_result_ NTSTATUS Initialize(__in PWDF_OBJECT_ATTRIBUTES Attributes, __in PWDF_WORKITEM_CONFIG Config, __in FxObject *ParentObject, __out WDFWORKITEM *WorkItem)
Definition: fxworkitem.cpp:142
ULONG m_WorkItemRunningCount
Definition: fxworkitem.hpp:69
static __inline KIRQL MxGetCurrentIrql()
Definition: mxgeneralkm.h:86
VOID FlushAndWait(VOID)
Definition: fxworkitem.cpp:508
__inline FxDriver * GetDriver(VOID)
Definition: fxdevice.hpp:164
PVOID __inline GetObjectHandleUnchecked(VOID)
Definition: fxobject.hpp:446
static const WCHAR L[]
Definition: oid.c:1250
CfxDeviceBase * m_DeviceBase
Definition: fxobject.hpp:328
FxObject * m_Object
Definition: fxworkitem.hpp:75
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
virtual void Unlock(__in KIRQL PreviousIrql)=0
static __inline MdEThread GetCurrentEThread()
Definition: mxgeneralkm.h:69
_Must_inspect_result_ _In_ WDFDEVICE _In_ PWDF_CHILD_LIST_CONFIG Config
Definition: wdfchildlist.h:474
static _Must_inspect_result_ NTSTATUS _Create(__in PFX_DRIVER_GLOBALS FxDriverGlobals, __in PWDF_WORKITEM_CONFIG Config, __in PWDF_OBJECT_ATTRIBUTES Attributes, __in FxObject *ParentObject, __out WDFWORKITEM *WorkItem)
Definition: fxworkitem.cpp:109
#define TRACE_LEVEL_ERROR
Definition: storswtr.h:27
FORCEINLINE LONGLONG WDF_REL_TIMEOUT_IN_SEC(_In_ ULONGLONG Time)
Definition: wdfcore.h:62
__inline MdDriverObject GetDriverObject(VOID)
Definition: fxdriver.hpp:252
static __inline MxThread MxGetCurrentThread()
Definition: mxgeneralkm.h:61
__inline PFX_DRIVER_GLOBALS GetDriverGlobals(VOID)
Definition: fxobject.hpp:734
DoTraceLevelMessage(pFxDriverGlobals, TRACE_LEVEL_VERBOSE, TRACINGPNP, "Enter, WDFDEVICE %p", Device)
#define NULL
Definition: types.h:112
__inline VOID Free()
Definition: mxworkitemkm.h:90
VOID DeleteFromFailedCreate(VOID)
Definition: fxobject.cpp:391
FX_TRACK_DRIVER(fxDriverGlobals)
_Must_inspect_result_ _In_opt_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWAITLOCK * Lock
Definition: wdfsync.h:124
_Must_inspect_result_ _In_ PWDF_WORKITEM_CONFIG _In_ PWDF_OBJECT_ATTRIBUTES _Out_ WDFWORKITEM * WorkItem
Definition: wdfworkitem.h:110
FxWorkItem * pFxWorkItem
virtual BOOLEAN Dispose(VOID)
Definition: fxworkitem.cpp:479
#define __in
Definition: dbghelp.h:35
static SERVICE_STATUS status
Definition: service.c:31
FxVerifierDbgBreakPoint(pFxDriverGlobals)
LONGLONG QuadPart
Definition: typedefs.h:114
_Must_inspect_result_ _In_ WDFDMAENABLER _In_ _In_opt_ PWDF_OBJECT_ATTRIBUTES Attributes
Definition: ps.c:97