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
DeleteObject(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
DeleteEarlyDisposedObject(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
Dispose(VOID)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
ProcessDestroy(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
EarlyDispose(VOID)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
PerformEarlyDispose(VOID)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
RemoveParentAssignment(VOID)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
ParentDeleteEvent(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
DeferredDisposeWorkItem(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)
__drv_requiresIRQL(DISPATCH_LEVEL)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)
__drv_requiresIRQL(DISPATCH_LEVEL)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)
__drv_requiresIRQL(DISPATCH_LEVEL)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
QueueDeferredDisposeLocked(__in FxObjectState NewDeferedState)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)
__drv_requiresIRQL(DISPATCH_LEVEL)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))
__drv_when(Unlock,__drv_requiresIRQL (DISPATCH_LEVEL))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