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