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
45 FxObject::DeleteObject(
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 {
61     NTSTATUS status;
62     KIRQL oldIrql;
63     BOOLEAN result;
64 
65     m_SpinLock.Acquire(&oldIrql);
66 
67     result = MarkDeleteCalledLocked();
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) {
77     case FxObjectStateCreated:
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             //
86             status = m_ParentObject->RemoveChildObjectInternal(this);
87 
88             if (status == STATUS_DELETE_PENDING) {
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                 //
108                 SetObjectStateLocked(FxObjectStateWaitingForEarlyDispose);
109                 m_SpinLock.Release(oldIrql);
110                 break;
111             }
112             else {
113                 //
114                 // We no longer have a parent object
115                 //
116                 m_ParentObject = NULL;
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 
127     case FxObjectStateDisposed:
128 
129         if (m_ParentObject != NULL) {
130             status = m_ParentObject->RemoveChildObjectInternal(this);
131 
132             if (status == STATUS_DELETE_PENDING) {
133                 SetObjectStateLocked(FxObjectStateWaitingForParentDeleteAndDisposed);
134                 m_SpinLock.Release(oldIrql);
135                 break;
136             }
137             else {
138                 //
139                 // We no longer have a parent object
140                 //
141                 m_ParentObject = NULL;
142             }
143         }
144 
145         //
146         // This will release the spinlock
147         //
148         DeletedAndDisposedWorkerLocked(oldIrql);
149         break;
150 
151     case FxObjectStateDisposingDisposeChildren:
152     case FxObjectStateWaitingForEarlyDispose:
153     case FxObjectStateDeletedDisposing: // Do nothing, workitem will move into disposed and deleted
154     case FxObjectStateDeletedAndDisposed:     // Do nothing, already deleted
155         TraceDroppedEvent(FxObjectDroppedEventDeleteObject);
156         m_SpinLock.Release(oldIrql);
157         break;
158 
159     // These are bad states for this event
160     case FxObjectStateInvalid:
161     case FxObjectStateDestroyed:
162     case FxObjectStateWaitingForParentDeleteAndDisposed:
163     default:
164         TraceDroppedEvent(FxObjectDroppedEventDeleteObject);
165         // Bad state
166         ASSERT(FALSE);
167         m_SpinLock.Release(oldIrql);
168     }
169 }
170 
171 VOID
172 FxObject::DeleteEarlyDisposedObject(
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 
190     ASSERT(m_ObjectFlags & FXOBJECT_FLAGS_EARLY_DISPOSED_EXT);
191     ASSERT(m_ObjectState == FxObjectStateDisposed);
192 
193     result = MarkDeleteCalledLocked();
194     ASSERT(result);
195     UNREFERENCED_PARAMETER(result); //for fre build
196 
197     if (m_ParentObject != NULL) {
198         NTSTATUS status;
199         KIRQL irql;
200 
201         m_SpinLock.Acquire(&irql);
202 
203         if (m_ParentObject != NULL) {
204             status = m_ParentObject->RemoveChildObjectInternal(this);
205 
206             if (status == STATUS_DELETE_PENDING) {
207                 SetObjectStateLocked(FxObjectStateWaitingForParentDeleteAndDisposed);
208                 m_SpinLock.Release(irql);
209                 return;
210             }
211             else {
212                 //
213                 // We no longer have a parent object
214                 //
215                 m_ParentObject = NULL;
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
229 FxObject::Dispose(
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
251 FxObject::ProcessDestroy(
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         //
289         h = GetObjectHandleUnchecked();
290 
291 
292 
293 
294 
295 
296 
297 
298 
299 
300 
301 
302 
303         for (pHeader = GetContextHeader();
304              pHeader != NULL;
305              pHeader = pHeader->NextHeader) {
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) {
321                 pHeader->EvtCleanupCallback(h);
322                 pHeader->EvtCleanupCallback = NULL;
323             }
324 
325             if (pHeader->EvtDestroyCallback != NULL) {
326                 pHeader->EvtDestroyCallback(h);
327                 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) {
341                 FxPoolFree(pHeader);
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) {
364         GetDebugExtension()->TagTracker = NULL;
365         delete pTagTracker;
366     }
367 
368     //
369     // See NOTE above.
370     //
371     SelfDestruct();
372 }
373 
374 BOOLEAN
375 FxObject::EarlyDispose(
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 {
396     NTSTATUS status;
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) {
408     case FxObjectStateCreated:
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             //
417             status = m_ParentObject->RemoveChildObjectInternal(this);
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                 //
439                 SetObjectStateLocked(FxObjectStateWaitingForEarlyDispose);
440                 m_SpinLock.Release(oldIrql);
441 
442                 return FALSE;
443             }
444             else {
445                 //
446                 // We no longer have a parent object
447                 //
448                 m_ParentObject = NULL;
449             }
450         }
451 
452         //
453         // Mark that this object was early disposed externally wrt the
454         // state machine.
455         //
456         m_ObjectFlags |= FXOBJECT_FLAGS_EARLY_DISPOSED_EXT;
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
476 FxObject::PerformEarlyDispose(
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) {
517     case FxObjectStateCreated:
518         //
519         // Start dispose, move into Disposing state
520         // returns with m_SpinLock released
521         //
522         result = PerformEarlyDisposeWorkerAndUnlock(oldIrql, FALSE);
523         break;
524 
525     case FxObjectStateWaitingForEarlyDispose:
526         //
527         // Start the dispose path.
528         //
529         result = PerformEarlyDisposeWorkerAndUnlock(oldIrql, FALSE);
530         break;
531 
532     case FxObjectStateDeferedDisposing:
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
544     case FxObjectStateDeletedAndDisposed:
545         TraceDroppedEvent(FxObjectDroppedEventPerformEarlyDispose);
546         m_SpinLock.Release(oldIrql);
547         break;
548 
549     // These are bad states for this event
550     case FxObjectStateInvalid:
551     case FxObjectStateDestroyed:
552 
553     default:
554         TraceDroppedEvent(FxObjectDroppedEventPerformEarlyDispose);
555         // Bad state
556         ASSERT(FALSE);
557         m_SpinLock.Release(oldIrql);
558         break;
559     }
560 
561     return result;
562 }
563 
564 _Must_inspect_result_
565 NTSTATUS
566 FxObject::RemoveParentAssignment(
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;
583     NTSTATUS status;
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     //
591     if (m_ObjectState != FxObjectStateCreated) {
592         TraceDroppedEvent(FxObjectDroppedEventRemoveParentAssignment);
593         m_SpinLock.Release(oldIrql);
594         return STATUS_DELETE_PENDING;
595     }
596 
597     // We should have a parent
598     ASSERT(m_ParentObject != NULL);
599 
600     status = m_ParentObject->RemoveChildObjectInternal(this);
601     if (NT_SUCCESS(status)) {
602         m_ParentObject = NULL;
603     }
604 
605     m_SpinLock.Release(oldIrql);
606 
607     return status;
608 }
609 
610 VOID
611 FxObject::ParentDeleteEvent(
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     //
641     m_ParentObject = NULL;
642 
643     //
644     // Perform the right action based on the objects current state
645     //
646     switch(m_ObjectState) {
647     case FxObjectStateWaitingForParentDeleteAndDisposed:
648     case FxObjectStateDisposed:
649         //
650         // This will release the spinlock
651         //
652         DeletedAndDisposedWorkerLocked(oldIrql, TRUE);
653         break;
654 
655     case FxObjectStateDeletedDisposing:
656         //
657         // Do nothing, workitem will move into disposed
658         //
659         TraceDroppedEvent(FxObjectDroppedEventParentDeleteEvent);
660         m_SpinLock.Release(oldIrql);
661         break;
662 
663     case FxObjectStateDeletedAndDisposed:     // Do nothing, already deleted
664         m_SpinLock.Release(oldIrql);
665         break;
666 
667     case FxObjectStateDisposingDisposeChildren:
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 
680     case FxObjectStateCreated:
681     case FxObjectStateWaitingForEarlyDispose:
682 
683     case FxObjectStateInvalid:
684     case FxObjectStateDestroyed:
685     default:
686         //
687         // Bad state
688         //
689         ASSERT(FALSE);
690         m_SpinLock.Release(oldIrql);
691         break;
692     }
693 }
694 
695 VOID
696 FxObject::DeferredDisposeWorkItem(
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;
714     BOOLEAN result, destroy;
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) {
729     case FxObjectStateDeferedDisposing:
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 
744     case FxObjectStateDeferedDeleting:
745         SetObjectStateLocked(FxObjectStateDeletedDisposing);
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 
756     case FxObjectStateDeferedDestroy:
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
763     case FxObjectStateDisposed:
764     case FxObjectStateWaitingForParentDeleteAndDisposed: // Do nothing
765     case FxObjectStateCreated:
766     case FxObjectStateWaitingForEarlyDispose:
767     case FxObjectStateInvalid:
768     case FxObjectStateDestroyed:
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 
783 _Releases_lock_(this->m_SpinLock.m_Lock)
784 __drv_requiresIRQL(DISPATCH_LEVEL)
785 BOOLEAN
786 FxObject::PerformDisposingDisposeChildrenLocked(
787     __in __drv_restoresIRQL KIRQL OldIrql,
788     __in                    BOOLEAN CanDefer
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 |
811                                    FXOBJECT_FLAGS_EARLY_DISPOSED_EXT);
812 
813     SetObjectStateLocked(FxObjectStateDisposingDisposeChildren);
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             //
838             SetObjectStateLocked(FxObjectStateDisposed);
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 
854 _Releases_lock_(this->m_SpinLock.m_Lock)
855 __drv_requiresIRQL(DISPATCH_LEVEL)
856 BOOLEAN
857 FxObject::PerformEarlyDisposeWorkerAndUnlock(
858     __in __drv_restoresIRQL KIRQL OldIrql,
859     __in                    BOOLEAN CanDefer
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 {
881     ASSERT(m_ObjectState == FxObjectStateCreated ||
882            m_ObjectState == FxObjectStateWaitingForEarlyDispose);
883 
884     SetObjectStateLocked(FxObjectStateDisposingEarly);
885 
886     if (CanDefer && ShouldDeferDisposeLocked(&OldIrql))  {
887         QueueDeferredDisposeLocked(FxObjectStateDeferedDisposing);
888         m_SpinLock.Release(OldIrql);
889 
890         return FALSE;
891     }
892     else {
893         return PerformDisposingDisposeChildrenLocked(OldIrql, CanDefer);
894     }
895 }
896 
897 _Releases_lock_(this->m_SpinLock.m_Lock)
898 __drv_requiresIRQL(DISPATCH_LEVEL)
899 BOOLEAN
900 FxObject::DeleteWorkerAndUnlock(
901     __in __drv_restoresIRQL KIRQL OldIrql,
902     __in                    BOOLEAN CanDefer
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 {
925     ASSERT(m_ObjectState == FxObjectStateCreated);
926            // m_ObjectState == FxObjectStateWaitingForParentDelete);
927 
928     if (CanDefer && ShouldDeferDisposeLocked(&OldIrql)) {
929         QueueDeferredDisposeLocked(FxObjectStateDeferedDeleting);
930         m_SpinLock.Release(OldIrql);
931 
932         return FALSE;
933     }
934     else {
935         SetObjectStateLocked(FxObjectStateDeletedDisposing);
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
958 FxObject::QueueDeferredDisposeLocked(
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);
986     ASSERT(m_Globals->Driver != NULL);
987 
988     SetObjectStateLocked(NewDeferedState);
989 
990     //FxToObjectItf::FxAddToDisposeList(m_DeviceBase, m_Globals, this);
991     if (m_DeviceBase != NULL) {
992         m_DeviceBase->AddToDisposeList(this);
993     }
994     else {
995         m_Globals->Driver->GetDisposeList()->Add(this);
996     }
997 }
998 
999 _Releases_lock_(this->m_SpinLock.m_Lock)
1000 __drv_requiresIRQL(DISPATCH_LEVEL)
1001 BOOLEAN
1002 FxObject::DisposeChildrenWorker(
1003     __in                    FxObjectState NewDeferedState,
1004     __in __drv_restoresIRQL KIRQL OldIrql,
1005     __in                    BOOLEAN CanDefer
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 
1063     ASSERT((m_ObjectState == FxObjectStateDisposingDisposeChildren) ||
1064            (m_ObjectState == FxObjectStateDeletedDisposing));
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     //
1094     if (IsForceDisposeThreadLocked() && OldIrql != PASSIVE_LEVEL) {
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 //
1196 _When_(Unlock, _Releases_lock_(this->m_SpinLock.m_Lock))
1197 __drv_when(Unlock, __drv_requiresIRQL(DISPATCH_LEVEL))
1198 VOID
1199 FxObject::DeletedAndDisposedWorkerLocked(
1200     __in __drv_when(Unlock, __drv_restoresIRQL) KIRQL OldIrql,
1201     __in                                        BOOLEAN Unlock
1202     )
1203 {
1204     SetObjectStateLocked(FxObjectStateDeletedAndDisposed);
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 
1218