1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7     FxSystemWorkItem.hpp
8 
9 Abstract:
10 
11     This implements an internal framework workitem that manages
12     cleanup.
13 
14 Author:
15 
16 
17 
18 Environment:
19 
20     Both kernel and user mode
21 
22 Revision History:
23 
24 
25 --*/
26 
27 #ifndef _FXSYSTEMWORKITEM_H
28 #define _FXSYSTEMWORKITEM_H
29 
30 //
31 // This class provides a common place for code to deal with
32 // cleanup and synchronization issues of workitems utilized
33 // internally by the framework
34 //
35 
36 _Function_class_(EVT_SYSTEMWORKITEM)
37 __drv_maxIRQL(PASSIVE_LEVEL)
38 __drv_maxFunctionIRQL(DISPATCH_LEVEL)
39 __drv_sameIRQL
40 typedef
41 VOID
42 EVT_SYSTEMWORKITEM(
43     __in PVOID Parameter
44     );
45 
46 typedef EVT_SYSTEMWORKITEM FN_WDF_SYSTEMWORKITEM,*PFN_WDF_SYSTEMWORKITEM;
47 
48 class FxSystemWorkItem : public FxNonPagedObject {
49 
50 private:
51 
52     // Ensures only one of either Delete or Cleanup runs down the object
53     BOOLEAN            m_RunningDown;
54 
55     //
56     // If this is set, a WorkItem has been enqueued
57     //
58     BOOLEAN            m_Enqueued;
59 
60     //
61     // The workitem we use
62     //
63     MxWorkItem         m_WorkItem;
64 
65     //
66     // The callback function
67     //
68     PFN_WDF_SYSTEMWORKITEM m_Callback;
69 
70     PVOID              m_CallbackArg;
71 
72     //
73     // This event is signaled when the workitem is done processing
74     // an Enqueue request.
75     //
76     FxCREvent          m_WorkItemCompleted;
77 
78     //
79     // This count is used to prevent the object from being deleted if
80     // one worker thread is preempted right after we drop the lock to call
81     // the client callback and another workitem gets queued and runs
82     // to completion and signals the event.
83     //
84     ULONG       m_WorkItemRunningCount;
85 
86     //
87     // We will keep a count of workitems queued and wait for
88     // all the workitems to run to completion before allowing the
89     // dispose to complete. Since this object is also used in running
90     // down the dispose list during driver unload, this run-down
91     // protection is required to make sure that the unload after deleting
92     // this object doesn't run ahead of the dispose worker thread.
93     //
94     LONG   m_OutStandingWorkItem;
95 
96     //
97     // This event will be signed when the above count drops to zero.
98     // The initial value of the count is biased to zero to provide
99     // remlock semantics. This event is configured to be a synchronziation
100     // event because we know for sure the only thread that's going to
101     // wait on this event is the one that's going to call Dispose and
102     // after that the object will be destroyed.
103     //
104     FxCREvent  m_RemoveEvent;
105 
106 public:
107     static
108     _Must_inspect_result_
109     NTSTATUS
110     _Create(
111         __in PFX_DRIVER_GLOBALS FxDriverGlobals,
112         __in PVOID              WdmObject,
113         __out FxSystemWorkItem** pObject
114         );
115 
116     virtual
117     ~FxSystemWorkItem(
118        );
119 
120     virtual
121     _Must_inspect_result_
122     NTSTATUS
QueryInterface(__inout FxQueryInterfaceParams * Params)123     QueryInterface(
124         __inout FxQueryInterfaceParams* Params
125         )
126     {
127         switch (Params->Type) {
128         case FX_TYPE_SYSTEMWORKITEM:
129              *Params->Object = (FxSystemWorkItem*) this;
130              break;
131 
132         default:
133              return FxNonPagedObject::QueryInterface(Params); // __super call
134         }
135 
136         return STATUS_SUCCESS;
137     }
138 
139     __inline
140     MdWorkItem
GetWorkItemPtr(VOID)141     GetWorkItemPtr(
142         VOID
143         )
144     {
145         return m_WorkItem.GetWorkItem();
146     }
147 
148     __inline
149     BOOLEAN
Enqueue(__in PFN_WDF_SYSTEMWORKITEM CallbackFunc,__in PVOID Parameter)150     Enqueue(
151         __in PFN_WDF_SYSTEMWORKITEM CallbackFunc,
152         __in PVOID                  Parameter
153         )
154     {
155         return EnqueueWorker(CallbackFunc, Parameter, TRUE);
156     }
157 
158     __inline
159     BOOLEAN
TryToEnqueue(__in PFN_WDF_SYSTEMWORKITEM CallbackFunc,__in PVOID Parameter)160     TryToEnqueue(
161         __in PFN_WDF_SYSTEMWORKITEM CallbackFunc,
162         __in PVOID                  Parameter
163         )
164     {
165         return EnqueueWorker(CallbackFunc, Parameter, FALSE);
166     }
167 
168     VOID
169     WaitForExit(
170         VOID
171         );
172 
173     __inline
174     VOID
IncrementWorkItemQueued()175     IncrementWorkItemQueued(
176         )
177     {
178         ASSERT(m_OutStandingWorkItem >= 1);
179 
180         InterlockedIncrement(&m_OutStandingWorkItem);
181     }
182 
183     __inline
184     VOID
DecrementWorkItemQueued()185     DecrementWorkItemQueued(
186         )
187     {
188         LONG result;
189 
190         ASSERT(m_OutStandingWorkItem >= 1);
191 
192         result = InterlockedDecrement(&m_OutStandingWorkItem);
193 
194         if (result == 0) {
195             m_RemoveEvent.Set();
196         }
197     }
198 
199     __inline
200     VOID
ReleaseWorkItemQueuedCountAndWait()201     ReleaseWorkItemQueuedCountAndWait(
202         )
203     {
204         NTSTATUS status;
205 
206         //
207         // Drop the bias count to indicate the object is being removed.
208         //
209         DecrementWorkItemQueued();
210 
211         status = m_RemoveEvent.EnterCRAndWaitAndLeave();
212         ASSERT(NT_SUCCESS(status));
213         UNREFERENCED_PARAMETER(status);
214 
215         ASSERT(m_OutStandingWorkItem == 0);
216     }
217 
218     DECLARE_INTERNAL_NEW_OPERATOR();
219 
220 private:
221     FxSystemWorkItem(
222         __in PFX_DRIVER_GLOBALS FxDriverGlobals
223         );
224 
225     virtual
226     BOOLEAN
227     Dispose(
228         VOID
229         );
230 
231     _Must_inspect_result_
232     NTSTATUS
233     Initialize(
234         __in PVOID WdmObject
235         );
236 
237     VOID
238     WorkItemHandler(
239         );
240 
241     static
242     MX_WORKITEM_ROUTINE
243     _WorkItemThunk;
244 
245     BOOLEAN
246     EnqueueWorker(
247         __in PFN_WDF_SYSTEMWORKITEM  Func,
248         __in PVOID   Parameter,
249         __in BOOLEAN AssertIfAlreadyQueued
250         );
251 };
252 
253 #endif // _FXSYSTEMWORKITEM_H
254 
255