ReactOS  0.4.15-dev-2961-gecb0c09
fxobjectstatemachine.cpp
Go to the documentation of this file.
1 /*++
2 
3 Copyright (c) Microsoft Corporation
4 
5 Module Name:
6 
7  FxObjectStateMachine.cpp
8 
9 Abstract:
10 
11  This module contains the implementation of the base object's state machine.
12 
13 Author:
14 
15 
16 
17 
18 
19 Environment:
20 
21  Both kernel and user mode
22 
23 Revision History:
24 
25 
26 
27 
28 
29 
30 
31 
32 --*/
33 
34 #include "fxobjectpch.hpp"
35 
36 extern "C" {
37 
38 #if defined(EVENT_TRACING)
39 #include "FxObjectStateMachine.tmh"
40 #endif
41 
42 }
43 
44 VOID
46  VOID
47  )
48 /*++
49 
50 Routine Description:
51  This is a public method that is called on an object to request that it Delete.
52 
53 Arguments:
54  None
55 
56 Returns:
57  NTSTATUS
58 
59 --*/
60 {
62  KIRQL oldIrql;
64 
65  m_SpinLock.Acquire(&oldIrql);
66 
68 
69  // This method should only be called once per object
70  ASSERT(result);
71  UNREFERENCED_PARAMETER(result); //for fre build
72 
73  //
74  // Perform the right action based on the objects current state
75  //
76  switch(m_ObjectState) {
78  //
79  // If we have a parent object, notify it of our deletion
80  //
81  if (m_ParentObject != NULL) {
82  //
83  // We call this holding our spinlock, the hierachy is child->parent
84  // when the lock is held across calls
85  //
87 
89 
90  //
91  // We won the race to ourselves (still FxObjectStateCreated),
92  // but lost the race on the parent who is going to try and
93  // dispose us through the ParentDeleteEvent().
94  //
95  // This is OK since the state machine protects us from
96  // doing improper actions, but we must not rundown and
97  // release our reference count till the parent object
98  // eventually calls our ParentDeleteEvent().
99  //
100  // So we note the state, and return waiting for the
101  // parent to dispose us.
102  //
103 
104  //
105  // Wait for our parent to come in and dispose us through
106  // the ParentDeleteEvent().
107  //
109  m_SpinLock.Release(oldIrql);
110  break;
111  }
112  else {
113  //
114  // We no longer have a parent object
115  //
117  }
118  }
119 
120  //
121  // Start Dispose, do delete state machine
122  // returns with m_SpinLock released
123  //
124  DeleteWorkerAndUnlock(oldIrql, TRUE);
125  break;
126 
128 
129  if (m_ParentObject != NULL) {
131 
132  if (status == STATUS_DELETE_PENDING) {
134  m_SpinLock.Release(oldIrql);
135  break;
136  }
137  else {
138  //
139  // We no longer have a parent object
140  //
142  }
143  }
144 
145  //
146  // This will release the spinlock
147  //
148  DeletedAndDisposedWorkerLocked(oldIrql);
149  break;
150 
153  case FxObjectStateDeletedDisposing: // Do nothing, workitem will move into disposed and deleted
154  case FxObjectStateDeletedAndDisposed: // Do nothing, already deleted
156  m_SpinLock.Release(oldIrql);
157  break;
158 
159  // These are bad states for this event
163  default:
165  // Bad state
166  ASSERT(FALSE);
167  m_SpinLock.Release(oldIrql);
168  }
169 }
170 
171 VOID
173  VOID
174  )
175 /*++
176 
177 Routine Description:
178  Deletes an object which has already been explicitly early disposed.
179 
180 Arguments:
181  None
182 
183 Return Value:
184  None
185 
186  --*/
187 {
188  BOOLEAN result;
189 
192 
194  ASSERT(result);
195  UNREFERENCED_PARAMETER(result); //for fre build
196 
197  if (m_ParentObject != NULL) {
199  KIRQL irql;
200 
201  m_SpinLock.Acquire(&irql);
202 
203  if (m_ParentObject != NULL) {
205 
206  if (status == STATUS_DELETE_PENDING) {
208  m_SpinLock.Release(irql);
209  return;
210  }
211  else {
212  //
213  // We no longer have a parent object
214  //
216  }
217  }
218 
219  m_SpinLock.Release(irql);
220  }
221 
222  //
223  // This will release the spinlock
224  //
225  DeletedAndDisposedWorkerLocked(PASSIVE_LEVEL, FALSE);
226 }
227 
228 BOOLEAN
230  VOID
231  )
232 /*++
233 
234 Routine Description:
235  This is a virtual function overriden by sub-classes if they want
236  Dispose notifications.
237 
238 Arguments:
239  None
240 
241 Returns:
242  TRUE if the registered cleanup routines on this object should be called
243  when this funciton returns
244 
245 --*/
246 {
247  return TRUE;
248 }
249 
250 VOID
252  VOID
253  )
254 {
255  FxTagTracker* pTagTracker;
256 
257  //
258  // Set the debug info to NULL so that we don't use it after the
259  // SelfDestruct call. Setting the DestroyFunction to NULL
260  // will also prevent reuse of the REF_OBJ after it has been destroyed.
261  //
262  pTagTracker = GetTagTracker();
263 
264  //
265  // We will free debug info later. It useful to hang on to the debug
266  // info after the destructor has been called for debugging purposes.
267  //
268  if (pTagTracker != NULL) {
269  pTagTracker->CheckForAbandondedTags();
270  }
271 
272  //
273  // Call the destroy callback *before* any desctructor is called. This
274  // way the callback has access to a full fledged object that hasn't been
275  // cleaned up yet.
276  //
277  // We only do this for committed objects. A non committed object will
278  // *NOT* have additional contexts to free.
279  //
280  if (m_ObjectSize > 0 && IsCommitted()) {
281  FxContextHeader* pHeader, *pNext;
282  WDFOBJECT h;
283  BOOLEAN first;
284 
285  //
286  // We are getting the object handle when the ref count is zero. We
287  // don't want to ASSERT in this case.
288  //
290 
291 
292 
293 
294 
295 
296 
297 
298 
299 
300 
301 
302 
303  for (pHeader = GetContextHeader();
304  pHeader != NULL;
306 
307  //
308  // Cleanup *may* have been called earlier in the objects
309  // destruction, and in this case the EvtCleanupCallback will
310  // be set to NULL. Ensuring its always called provides
311  // symmetry to the framework.
312  //
313 
314  //
315  // No need to interlockexchange out the value of
316  // EvtCleanupCallback because any codepath that might be calling
317  // CallCleanup must have a valid reference and no longer have
318  // any outstanding references
319  //
320  if (pHeader->EvtCleanupCallback != NULL) {
323  }
324 
325  if (pHeader->EvtDestroyCallback != NULL) {
328  }
329  }
330 
331  first = TRUE;
332  for (pHeader = GetContextHeader(); pHeader != NULL; pHeader = pNext) {
333 
334  pNext = pHeader->NextHeader;
335 
336  //
337  // The first header is embedded, so it will be freed with the
338  // object
339  //
340  if (first == FALSE) {
342  }
343 
344  first = FALSE;
345  }
346  }
347 
348 
349 
350 
351 
352 
353 
354 
355 
356 
357  //
358  // NOTE: The delete of the tag tracker *MUST* occur before the SelfDestruct()
359  // of this object. The tag tracker has a back pointer to this object which
360  // it dereferences in its own destructor. If SelfDestruct() is called
361  // first, then ~FxTagTracker will touch freed pool and bugcheck.
362  //
363  if (pTagTracker != NULL) {
365  delete pTagTracker;
366  }
367 
368  //
369  // See NOTE above.
370  //
371  SelfDestruct();
372 }
373 
374 BOOLEAN
376  VOID
377  )
378 /*++
379 
380 Routine Description:
381  Public early dipose functionality. Removes the object from the parent's
382  list of children. This assumes the caller or someone else will eventually
383  invoke DeleteObject() on this object.
384 
385 Arguments:
386  None
387 
388 Return Value:
389  BOOLEAN - same semantic as DisposeChildrenWorker.
390  TRUE - dispose of this object and its children occurred synchronously in
391  this call
392  FALSE - the dispose was pended to a work item
393 
394  --*/
395 {
397  KIRQL oldIrql;
398  BOOLEAN result;
399 
400  //
401  // By default, we assume a synchronous diposal
402  //
403  result = TRUE;
404 
405  m_SpinLock.Acquire(&oldIrql);
406 
407  switch(m_ObjectState) {
409  //
410  // If we have a parent object, notify it of our deletion
411  //
412  if (m_ParentObject != NULL) {
413  //
414  // We call this holding our spinlock, the hierachy is child->parent
415  // when the lock is held across calls
416  //
418 
419  if (status == STATUS_DELETE_PENDING) {
420 
421  //
422  // We won the race to ourselves (still FxObjectStateCreated),
423  // but lost the race on the parent who is going to try and
424  // dispose us through the PerformEarlyDipose().
425  //
426  // This is OK since the state machine protects us from
427  // doing improper actions, but we must not rundown and
428  // release our reference count till the parent object
429  // eventually calls our ParentDeleteEvent().
430  //
431  // So we note the state, and return waiting for the
432  // parent to dispose us.
433  //
434 
435  //
436  // Wait for our parent to come in and dispose us through
437  // the PerformEarlyDipose().
438  //
440  m_SpinLock.Release(oldIrql);
441 
442  return FALSE;
443  }
444  else {
445  //
446  // We no longer have a parent object
447  //
449  }
450  }
451 
452  //
453  // Mark that this object was early disposed externally wrt the
454  // state machine.
455  //
457 
458  //
459  // Start the dispose path. This call will release the spinlock.
460  //
461  result = PerformEarlyDisposeWorkerAndUnlock(oldIrql, TRUE);
462  break;
463 
464  default:
465  //
466  // Not in the right state for an early dispose
467  //
468  result = FALSE;
469  m_SpinLock.Release(oldIrql);
470  }
471 
472  return result;
473 }
474 
475 BOOLEAN
477  VOID
478  )
479 /*++
480 
481 Routine Description:
482  Allows Dispose() processing on an object to occur before calling DeleteObject().
483 
484 Arguments:
485  CanDefer - if TRUE, can defer to a dispose list if IRQL requirements are
486  incorrect. If FALSE, the caller has guaranteed that we are at
487  the correct IRQL
488 
489 Returns:
490  None
491 
492 --*/
493 {
494  KIRQL oldIrql;
495  BOOLEAN result;
496 
497  //
498  // By default we assume that the dispose was synchronous
499  //
500  result = TRUE;
501 
502 
503  //
504  // It's OK for an object to already be disposing due to
505  // a parent delete call.
506  //
507  // To check for verifier errors in which two calls to
508  // PerformEarlyDispose() occur, a separate flag is used
509  // rather than complicating the state machine.
510  //
511  m_SpinLock.Acquire(&oldIrql);
512 
513  //
514  // Perform the right action based on the objects current state
515  //
516  switch(m_ObjectState) {
518  //
519  // Start dispose, move into Disposing state
520  // returns with m_SpinLock released
521  //
522  result = PerformEarlyDisposeWorkerAndUnlock(oldIrql, FALSE);
523  break;
524 
526  //
527  // Start the dispose path.
528  //
529  result = PerformEarlyDisposeWorkerAndUnlock(oldIrql, FALSE);
530  break;
531 
533  //
534  // We should only get an early dispose in this state once we have thunked
535  // to passive level via the dispose list.
536  //
537  result = PerformDisposingDisposeChildrenLocked(oldIrql, FALSE);
538  break;
539 
540  case FxObjectStateWaitingForParentDeleteAndDisposed: // Do nothing, parent object will delete and dispose
541  case FxObjectStateDisposed: // Do nothing
542  case FxObjectStateDisposingEarly: // Do nothing
543  case FxObjectStateDeletedDisposing: // Do nothing, workitem will moved into disposed
546  m_SpinLock.Release(oldIrql);
547  break;
548 
549  // These are bad states for this event
552 
553  default:
555  // Bad state
556  ASSERT(FALSE);
557  m_SpinLock.Release(oldIrql);
558  break;
559  }
560 
561  return result;
562 }
563 
565 NTSTATUS
567  VOID
568  )
569 /*++
570 
571 Routine Description:
572  Remove the current objects ParentObject.
573 
574 Arguments:
575  None
576 
577 Returns:
578  NTSTATUS of action
579 
580 --*/
581 {
582  KIRQL oldIrql;
584 
585  m_SpinLock.Acquire(&oldIrql);
586 
587  //
588  // Object is already being deleted, this object will be removed as a child
589  // by the parents Dispose()
590  //
593  m_SpinLock.Release(oldIrql);
594  return STATUS_DELETE_PENDING;
595  }
596 
597  // We should have a parent
599 
601  if (NT_SUCCESS(status)) {
603  }
604 
605  m_SpinLock.Release(oldIrql);
606 
607  return status;
608 }
609 
610 VOID
612  VOID
613  )
614 /*++
615 
616 Routine Description:
617 
618  This is invoked by the parent object when it is Dispose()'ing us.
619 
620 Arguments:
621  None
622 
623 Returns:
624  None
625 
626 --*/
627 {
628  KIRQL oldIrql;
629 
630  //
631  // Note: It's ok for an object to already be in the delete
632  // state since there can be an allowed race between
633  // parent disposing an object, and the DeleteObject()
634  // call on the object itself.
635  //
636  m_SpinLock.Acquire(&oldIrql);
637 
638  //
639  // We no longer have a parent object
640  //
642 
643  //
644  // Perform the right action based on the objects current state
645  //
646  switch(m_ObjectState) {
649  //
650  // This will release the spinlock
651  //
652  DeletedAndDisposedWorkerLocked(oldIrql, TRUE);
653  break;
654 
656  //
657  // Do nothing, workitem will move into disposed
658  //
660  m_SpinLock.Release(oldIrql);
661  break;
662 
663  case FxObjectStateDeletedAndDisposed: // Do nothing, already deleted
664  m_SpinLock.Release(oldIrql);
665  break;
666 
668  //
669  // In the process of deleting, ignore it
670  //
671  m_SpinLock.Release(oldIrql);
672  break;
673 
674  // These are bad states for this event
675 
676 
677 
678 
679 
682 
685  default:
686  //
687  // Bad state
688  //
689  ASSERT(FALSE);
690  m_SpinLock.Release(oldIrql);
691  break;
692  }
693 }
694 
695 VOID
697  VOID
698  )
699 /*++
700 
701 Routine Description:
702  Invoked by deferred dispose workitem. This is invoked at PASSIVE_LEVEL,
703  and returns at PASSIVE_LEVEL
704 
705 Arguments:
706  None
707 
708 Return Value:
709  None
710 
711  --*/
712 {
713  KIRQL oldIrql;
715 
716  destroy = FALSE;
717 
718  m_SpinLock.Acquire(&oldIrql);
719 
720  ASSERT(oldIrql == PASSIVE_LEVEL);
721 
722  //
723  // Perform the right action based on the objects current state
724  //
725  // DisposeChildrenWorker return result can be ignored because we are
726  // guaranteed to be calling it at PASSIVE.
727  //
728  switch (m_ObjectState) {
730  //
731  // This will drop the spinlock and move the object to the right state
732  // before returning.
733  //
734  result = PerformDisposingDisposeChildrenLocked(oldIrql, FALSE);
735 
736  //
737  // The substree should never defer to the dispose list if we pass FALSE.
738  //
739  ASSERT(result);
740  UNREFERENCED_PARAMETER(result); //for fre build
741 
742  return;
743 
746  result = DisposeChildrenWorker(FxObjectStateDeferedDeleting, oldIrql, FALSE);
747  ASSERT(result);
748  UNREFERENCED_PARAMETER(result); //for fre build
749 
750  //
751  // This will release the spinlock
752  //
753  DeletedAndDisposedWorkerLocked(oldIrql, FALSE);
754  return;
755 
757  // Perform final destroy actions now that we are at passive level
758  destroy = TRUE;
759  break;
760 
761  // These are bad states for this event
762  case FxObjectStateDeletedAndDisposed: // Do nothing
769 
770  default:
771  // Bad state
772  ASSERT(FALSE);
773  break;
774  }
775 
776  m_SpinLock.Release(oldIrql);
777 
778  if (destroy) {
779  ProcessDestroy();
780  }
781 }
782 
785 BOOLEAN
786 FxObject::PerformDisposingDisposeChildrenLocked(
789  )
790 /*++
791 
792 Routine Description:
793  This is entered with m_SpinLock held, and returns with it released.
794 
795 Arguments:
796  OldIrql - the IRQL before m_SpinLock was acquired
797 
798  CanDefer - if TRUE, can defer to a dispose list if IRQL requirements are
799  incorrect. If FALSE, the caller has guaranteed that we are at
800  the correct IRQL
801 
802 Return Value:
803  BOOLEAN - same semantic as DisposeChildrenWorker.
804  TRUE - delete of this object and its children occurred synchronously in
805  this call
806  FALSE - the delete was pended to a work item
807 
808  --*/
809 {
810  static const USHORT edFlags = (FXOBJECT_FLAGS_DELETECALLED |
812 
814 
815  if (DisposeChildrenWorker(FxObjectStateDeferedDisposing, OldIrql, CanDefer)) {
816  //
817  // Upon returning TRUE, the lock is still held
818  //
819 
820  //
821  // If this object was early disposed externally, destroy the children
822  // immediately (FxRequest relies on this so that the WDFMEMORYs created
823  // for probed and locked buffers are freed before the request is
824  // completed.)
825  //
826  // Otherwise, wait for DeleteObject or ParentDeleteEvent() to occur.
827  //
828  if ((m_ObjectFlags & edFlags) == edFlags) {
829  //
830  // This will drop the lock
831  //
832  DeletedAndDisposedWorkerLocked(OldIrql, FALSE);
833  }
834  else {
835  //
836  // Will wait for the parent deleted event
837  //
839  }
840 
841  return TRUE;
842  }
843  else {
844  //
845  // Upon return FALSE, the lock was released and a work item was
846  // queued to dispose of children at passive level
847  //
848  DO_NOTHING();
849 
850  return FALSE;
851  }
852 }
853 
856 BOOLEAN
857 FxObject::PerformEarlyDisposeWorkerAndUnlock(
860  )
861 /*++
862 
863 Routine Description:
864  This is entered with m_SpinLock held, and returns with it released.
865 
866 Arguments:
867  OldIrql - the previous IRQL before the object lock was acquired
868 
869  CanDefer - if TRUE, can defer to a dispose list if IRQL requirements are
870  incorrect. If FALSE, the caller has guaranteed that we are at
871  the correct IRQL
872 
873 Return Value:
874  BOOLEAN - same semantic as DisposeChildrenWorker.
875  TRUE - delete of this object and its children occurred synchronously in
876  this call
877  FALSE - the delete was pended to a work item
878 
879  --*/
880 {
883 
885 
888  m_SpinLock.Release(OldIrql);
889 
890  return FALSE;
891  }
892  else {
893  return PerformDisposingDisposeChildrenLocked(OldIrql, CanDefer);
894  }
895 }
896 
899 BOOLEAN
900 FxObject::DeleteWorkerAndUnlock(
903  )
904 /*++
905 
906 Routine Description:
907  This is entered with m_SpinLock held, and returns with it released.
908 
909 
910 Arguments:
911  OldIrql - the IRQL before m_SpinLock was acquired
912 
913  CanDefer - if TRUE, can defer to a dispose list if IRQL requirements are
914  incorrect. If FALSE, the caller has guaranteed that we are at
915  the correct IRQL
916 
917 Return Value:
918  BOOLEAN - same semantic as DisposeChildrenWorker.
919  TRUE - delete of this object and its children occurred synchronously in
920  this call
921  FALSE - the delete was pended to a work item
922 
923  --*/
924 {
926  // m_ObjectState == FxObjectStateWaitingForParentDelete);
927 
930  m_SpinLock.Release(OldIrql);
931 
932  return FALSE;
933  }
934  else {
936 
937  if (DisposeChildrenWorker(FxObjectStateDeferedDeleting, OldIrql, CanDefer)) {
938  //
939  // This will release the spinlock
940  //
941  DeletedAndDisposedWorkerLocked(OldIrql, FALSE);
942 
943  return TRUE;
944  }
945  else {
946  //
947  // Upon return FALSE, the lock was released and a work item was
948  // queued to dispose of children at passive level
949  //
950  DO_NOTHING();
951 
952  return FALSE;
953  }
954  }
955 }
956 
957 VOID
959  __in FxObjectState NewDeferedState
960  )
961 /*++
962 
963 Routine Description:
964  Queues this object onto a work item list which will dispose it at passive
965  level. The work item will be owned by the parent device or driver.
966 
967  This is called with the object's m_SpinLock held.
968 
969  NOTE: This function only looks at this object and the parent to attempt to
970  find the owning FxDeviceBase*. If this is a deeper hierarchy, the deeply
971  rooted FxDeviceBase will not be used.
972 
973 Arguments:
974  Parent - the parent of this objec (it may have already been removed from
975  m_ParentObject, so we can't use that field
976 
977 Return Value:
978  None
979 
980  --*/
981 {
982  //
983  // Queue workitem, which will run DisposeChildrenWorker()
984  //
985  ASSERT(m_Globals != NULL);
987 
988  SetObjectStateLocked(NewDeferedState);
989 
990  //FxToObjectItf::FxAddToDisposeList(m_DeviceBase, m_Globals, this);
991  if (m_DeviceBase != NULL) {
993  }
994  else {
995  m_Globals->Driver->GetDisposeList()->Add(this);
996  }
997 }
998 
1001 BOOLEAN
1002 FxObject::DisposeChildrenWorker(
1003  __in FxObjectState NewDeferedState,
1006  )
1007 
1008 /*++
1009 
1010 Routine Description:
1011 
1012  Rundown list of child objects removing their entries and invoking
1013  their ParentDeleteEvent() on them.
1014 
1015  This is called with the m_SpinLock held and upon returning the lock is
1016  released.
1017 
1018 Arguments:
1019  NewDeferedState - If the state transition requires defering to a dispose
1020  list, this is the new state to move to
1021 
1022  OldIrql - the previous IRQL when the caller acquired the object's lock
1023 
1024  CanDefer - if TRUE, can defer to a dispose list if IRQL requirements are
1025  incorrect. If FALSE, the caller has guaranteed that we are at
1026  the correct IRQL
1027 
1028 Returns:
1029  TRUE: Dispose is completed in this function.
1030  FALSE: Dispose is deferred either to a workitem (if CanDefer is TRUE) or will
1031  be done later in the current thread (if CanDefer is FALSE).
1032 
1033  In either case lock is released.
1034 
1035  If the OldIrql == PASSIVE_LEVEL, TRUE is guaranteed to be returned
1036 
1037 Comments:
1038 
1039  This routine is entered with the spinlock held, and may return with it released.
1040 
1041  The state machine ensures that this is only invoked once in
1042  an objects lifetime, regardless of races between PerformEarlyDispose,
1043  DeleteObject, or a ParentDeleteEvent. If there are requirements for passive
1044  level dispose and the previous IRQL is != PASSIVE, this function will be
1045  called twice, the first at IRQL > PASSIVE, the second at PASSIVE.
1046 
1047  Top level code has ensured this is invoked under the right IRQL level
1048  for the object to perform the Dispose() callbacks.
1049 
1050 --*/
1051 {
1052  LIST_ENTRY *ple;
1053  FxObject* childObject;
1054 
1055  //
1056  // Called from:
1057  //
1058  // DeferredDisposeWorkItem (will complete, and release in current thread)
1059  // PerformDisposeWorkerAndUnlock(PerformEarlyDispose), no release, but possible thread deferral
1060  // DeleteWorkerAndUnlock (will release, but may defer, its logic must change!)
1061  //
1062 
1065 
1066  //
1067  // This routine will attempt to dispose as many children as possible
1068  // in the current thread. It may have to stop if the thread is
1069  // not at PASSIVE_LEVEL when the object spinlock was acquired, and
1070  // a child object is marked as a passive level object.
1071  //
1072  // If this occurs, dispose processing is suspended and resumed in
1073  // a passive level workitem, which calls this routine back to
1074  // complete the processing.
1075  //
1076  // Once all child object's Dispose() callback has returned, we then
1077  // can call Dispose() on the parent object.
1078  //
1079  // This must be done in this order to guarantee the contract with the
1080  // device driver (and internal object system) that all child
1081  // EvtObjectCleanup callbacks have returned before their parents
1082  // EvtObjectCleanup event.
1083  //
1084  // This is important to avoid extra references
1085  // when child objects expect their parent object to be valid until
1086  // EvtObjectCleanup is called.
1087  //
1088 
1089  // Rundown list removing entries and calling Dispose on child objects
1090 
1091  //
1092  // If this object requires being forced onto the dispose thread, do it now
1093  //
1095  //
1096  // Workitem will re-run this function at PASSIVE_LEVEL
1097  //
1098  if (CanDefer) {
1099  QueueDeferredDisposeLocked(NewDeferedState);
1100  }
1101  else {
1102  SetObjectStateLocked(NewDeferedState);
1103  }
1104 
1105  m_SpinLock.Release(OldIrql);
1106 
1107  return FALSE;
1108  }
1109 
1110  for (ple = m_ChildListHead.Flink;
1111  ple != &m_ChildListHead;
1112  ple = ple->Flink) {
1113  //
1114  // Before removing the child object, we need to see if we need to defer
1115  // to a work item to dispose the child.
1116  //
1117  childObject = CONTAINING_RECORD(ple, FxObject, m_ChildEntry);
1118 
1119  //
1120  // Should not associate with self
1121  //
1122  ASSERT(childObject != this);
1123 
1124  //
1125  // If current threads IRQL before acquiring the spinlock is not
1126  // passive, and the child object is passive constrained, we must
1127  // defer the current disposal processing to a workitem.
1128  //
1129  // We stay in the Disposing state, which this routine will continue
1130  // processing when called back from the workitem.
1131  //
1132  // This code is re-entered at the proper passive_level to complete
1133  // processing where it left off (at the head of the m_ChildListHead).
1134  //
1135  if (OldIrql != PASSIVE_LEVEL && childObject->IsPassiveDisposeLocked()) {
1136  //
1137  // Workitem will re-run this function at PASSIVE_LEVEL
1138  //
1139  if (CanDefer) {
1140  QueueDeferredDisposeLocked(NewDeferedState);
1141  }
1142  else {
1143  SetObjectStateLocked(NewDeferedState);
1144  }
1145  m_SpinLock.Release(OldIrql);
1146 
1147  return FALSE;
1148  }
1149  }
1150 
1151  m_SpinLock.Release(OldIrql);
1152 
1153  for (ple = m_ChildListHead.Flink; ple != &m_ChildListHead; ple = ple->Flink) {
1154  childObject = CONTAINING_RECORD(ple, FxObject, m_ChildEntry);
1155 
1156  //
1157  // Inform child object of disposal. We will release the reference on
1158  // the child only after we have disposed ourself.
1159  //
1160  if (childObject->PerformEarlyDispose() == FALSE) {
1161 
1162  m_SpinLock.Acquire(&OldIrql);
1163  if (CanDefer) {
1164  QueueDeferredDisposeLocked(NewDeferedState);
1165  }
1166  else {
1167  SetObjectStateLocked(NewDeferedState);
1168  }
1169  m_SpinLock.Release(OldIrql);
1170 
1171  return FALSE;
1172  }
1173 
1174  ASSERT(childObject->GetRefCnt() > 0);
1175  }
1176 
1177  //
1178  // Call Dispose virtual callback on ourselves for benefit
1179  // of sub-classes if it is overridden.
1180  //
1181  if ((m_ObjectFlags & FXOBJECT_FLAGS_DISPOSE_OVERRIDE) == 0x00 || Dispose()) {
1182  //
1183  // Now call Cleanup on any handle context's exposed
1184  // to the device driver.
1185  //
1186  CallCleanup();
1187  }
1188 
1189  return TRUE;
1190 }
1191 
1192 //
1193 // Despite the name this function may not always be called with lock held
1194 // but if Unlock is TRUE, lock must be held.
1195 //
1198 VOID
1199 FxObject::DeletedAndDisposedWorkerLocked(
1202  )
1203 {
1205 
1206  if (Unlock) {
1207  m_SpinLock.Release(OldIrql);
1208  }
1209 
1210  DestroyChildren();
1211 
1212  //
1213  // Release the final reference on the object
1214  //
1215  RELEASE(NULL);
1216 }
1217 
virtual VOID DeleteObject(VOID)
#define STATUS_DELETE_PENDING
Definition: ntstatus.h:322
VOID CheckForAbandondedTags(VOID)
VOID __inline SetObjectStateLocked(__in FxObjectState NewState)
Definition: fxobject.hpp:369
FxContextHeader * NextHeader
Definition: fxhandle.h:71
FxTagTracker * GetTagTracker(VOID)
Definition: fxobject.hpp:766
_Must_inspect_result_ NTSTATUS RemoveParentAssignment(VOID)
virtual VOID SelfDestruct(VOID)
Definition: fxobject.hpp:437
GLuint64EXT * result
Definition: glext.h:11304
VOID ProcessDestroy(VOID)
#define TRUE
Definition: types.h:120
#define UNREFERENCED_PARAMETER(P)
Definition: ntbasedef.h:317
FxObject * m_ParentObject
Definition: fxobject.hpp:303
#define __drv_restoresIRQL
Definition: driverspecs.h:321
MdLock m_Lock
Definition: mxlock.h:40
LIST_ENTRY m_ChildEntry
Definition: fxobject.hpp:311
const GLint * first
Definition: glext.h:5794
LONG NTSTATUS
Definition: precomp.h:26
_Releases_lock_(this->m_SpinLock.m_Lock) __drv_requiresIRQL(DISPATCH_LEVEL) BOOLEAN FxObject
VOID ParentDeleteEvent(VOID)
VOID DeferredDisposeWorkItem(VOID)
KIRQL irql
Definition: wave.h:1
PFN_WDF_OBJECT_CONTEXT_CLEANUP EvtCleanupCallback
Definition: fxhandle.h:76
#define RELEASE(_tag)
Definition: fxobject.hpp:50
MxLock m_SpinLock
Definition: fxobject.hpp:296
VOID DeleteEarlyDisposedObject(VOID)
GLfloat GLfloat GLfloat GLfloat h
Definition: glext.h:7723
PSINGLE_LIST_ENTRY ple
BOOLEAN PerformEarlyDispose(VOID)
BOOLEAN MarkDeleteCalledLocked(VOID)
Definition: fxobject.hpp:1345
BOOLEAN EarlyDispose(VOID)
UCHAR KIRQL
Definition: env_spec_w32.h:591
__drv_requiresIRQL(DISPATCH_LEVEL)) VOID FxObject
#define FALSE
Definition: types.h:117
BOOLEAN IsPassiveDisposeLocked(VOID)
Definition: fxobject.hpp:1367
FxDriver * Driver
Definition: fxglobals.h:374
_When_(Unlock, _Releases_lock_(this->m_SpinLock.m_Lock)) __drv_when(Unlock
FxContextHeader * pHeader
Definition: handleapi.cpp:604
unsigned char BOOLEAN
VOID AddToDisposeList(__inout FxObject *Object)
Definition: fxdevice.hpp:387
PFLT_MESSAGE_WAITER_QUEUE CONTAINING_RECORD(Csq, DEVICE_EXTENSION, IrpQueue)) -> WaiterQ.mLock) _IRQL_raises_(DISPATCH_LEVEL) VOID NTAPI FltpAcquireMessageWaiterLock(_In_ PIO_CSQ Csq, _Out_ PKIRQL Irql)
Definition: Messaging.c:560
struct _LIST_ENTRY * Flink
Definition: typedefs.h:121
PFN_WDF_OBJECT_CONTEXT_DESTROY EvtDestroyCallback
Definition: fxhandle.h:82
#define ASSERT(a)
Definition: mode.c:44
#define NT_SUCCESS(StatCode)
Definition: apphelp.c:32
VOID Add(FxObject *object)
#define __drv_when(cond, annotes)
Definition: driverspecs.h:334
BOOLEAN IsCommitted(VOID)
Definition: fxobject.hpp:1087
__in BOOLEAN CanDefer
Definition: fxobject.hpp:1432
USHORT m_ObjectState
Definition: fxobject.hpp:290
FxObjectDebugExtension * GetDebugExtension(VOID)
Definition: fxobject.hpp:401
BOOLEAN IsForceDisposeThreadLocked(VOID)
Definition: fxobject.hpp:1375
PVOID __inline GetObjectHandleUnchecked(VOID)
Definition: fxobject.hpp:446
void destroy(_Tp *__pointer)
Definition: _construct.h:278
_Requires_lock_held_ Interrupt _Releases_lock_ Interrupt _In_ _IRQL_restores_ KIRQL OldIrql
Definition: kefuncs.h:790
CfxDeviceBase * m_DeviceBase
Definition: fxobject.hpp:328
FxObjectState
Definition: fxobject.hpp:158
VOID QueueDeferredDisposeLocked(__in FxObjectState NewDeferedState)
Definition: typedefs.h:119
#define PASSIVE_LEVEL
Definition: env_spec_w32.h:693
#define _Must_inspect_result_
Definition: ms_sal.h:558
__inline FxDisposeList * GetDisposeList()
Definition: fxdriver.hpp:354
USHORT m_ObjectSize
Definition: fxobject.hpp:254
VOID __inline DestroyChildren(VOID)
Definition: fxobject.hpp:464
__inline FxContextHeader * GetContextHeader(VOID)
Definition: fxobject.hpp:720
#define DISPATCH_LEVEL
Definition: env_spec_w32.h:696
LONG GetRefCnt(VOID)
Definition: fxobject.hpp:758
USHORT m_ObjectFlags
Definition: fxobject.hpp:263
unsigned short USHORT
Definition: pedump.c:61
__inline VOID CallCleanup(VOID)
Definition: fxobject.hpp:815
PFX_DRIVER_GLOBALS m_Globals
Definition: fxobject.hpp:259
#define NULL
Definition: types.h:112
FxTagTracker * TagTracker
Definition: fxobject.hpp:208
virtual BOOLEAN Dispose(VOID)
LIST_ENTRY m_ChildListHead
Definition: fxobject.hpp:293
void FxPoolFree(__in_xcount(ptr is at an offset from AllocationStart) PVOID ptr)
Definition: wdfpool.cpp:361
BOOLEAN ShouldDeferDisposeLocked(__out_opt PKIRQL PreviousIrql=NULL)
Definition: fxobject.hpp:1383
#define __in
Definition: dbghelp.h:35
static SERVICE_STATUS status
Definition: service.c:31
VOID __inline TraceDroppedEvent(__in FxObjectDroppedEvent Event)
Definition: fxobject.hpp:930
#define DO_NOTHING()
Definition: mxgeneral.h:32
_Must_inspect_result_ NTSTATUS RemoveChildObjectInternal(__in FxObject *ChildObject)
Definition: fxobject.cpp:770
Definition: ps.c:97