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