Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1181 lines
27 KiB

  1. /*++
  2. Copyright (c) 1989-1995 Microsoft Corporation
  3. Module Name:
  4. callback.c
  5. Abstract:
  6. This module implements the executive callbaqck object. Functions are
  7. provided to open, register, unregister , and notify callback objects.
  8. Author:
  9. Ken Reneris (kenr) 7-March-1995
  10. Neill Clift (NeillC) 17-Feb-2001
  11. Added low overhead callbacks for critical components like thread/registry etc.
  12. These routines have a high probability of not requiring any locks for an
  13. individual call.
  14. Environment:
  15. Kernel mode only.
  16. Revision History:
  17. --*/
  18. #include "exp.h"
  19. //
  20. // Callback Specific Access Rights.
  21. //
  22. #define CALLBACK_MODIFY_STATE 0x0001
  23. #define CALLBACK_ALL_ACCESS (STANDARD_RIGHTS_REQUIRED|SYNCHRONIZE|\
  24. CALLBACK_MODIFY_STATE )
  25. //
  26. // Event to wait for registration to become idle
  27. //
  28. KEVENT ExpCallbackEvent;
  29. //
  30. // Lock used when fast referencing fails.
  31. //
  32. EX_PUSH_LOCK ExpCallBackFlush;
  33. //
  34. // Debug flag to force certain code paths. Let it get optimized away on free builds.
  35. //
  36. #if DBG
  37. BOOLEAN ExpCallBackReturnRefs = FALSE;
  38. #else
  39. const
  40. BOOLEAN ExpCallBackReturnRefs = FALSE;
  41. #endif
  42. //
  43. // Address of callback object type descriptor.
  44. //
  45. POBJECT_TYPE ExCallbackObjectType;
  46. //
  47. // Structure that describes the mapping of generic access rights to object
  48. // specific access rights for callback objects.
  49. //
  50. #ifdef ALLOC_DATA_PRAGMA
  51. #pragma const_seg("INITCONST")
  52. #endif
  53. const GENERIC_MAPPING ExpCallbackMapping = {
  54. STANDARD_RIGHTS_READ ,
  55. STANDARD_RIGHTS_WRITE | CALLBACK_MODIFY_STATE,
  56. STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
  57. CALLBACK_ALL_ACCESS
  58. };
  59. #ifdef ALLOC_DATA_PRAGMA
  60. #pragma const_seg()
  61. #endif
  62. //
  63. // Executive callback object structure definition.
  64. //
  65. typedef struct _CALLBACK_OBJECT {
  66. ULONG Signature;
  67. KSPIN_LOCK Lock;
  68. LIST_ENTRY RegisteredCallbacks;
  69. BOOLEAN AllowMultipleCallbacks;
  70. UCHAR reserved[3];
  71. } CALLBACK_OBJECT , *PCALLBACK_OBJECT;
  72. //
  73. // Executive callback registration structure definition.
  74. //
  75. typedef struct _CALLBACK_REGISTRATION {
  76. LIST_ENTRY Link;
  77. PCALLBACK_OBJECT CallbackObject;
  78. PCALLBACK_FUNCTION CallbackFunction;
  79. PVOID CallbackContext;
  80. ULONG Busy;
  81. BOOLEAN UnregisterWaiting;
  82. } CALLBACK_REGISTRATION , *PCALLBACK_REGISTRATION;
  83. VOID
  84. ExpDeleteCallback (
  85. IN PCALLBACK_OBJECT CallbackObject
  86. );
  87. #ifdef ALLOC_PRAGMA
  88. #pragma alloc_text(INIT, ExpInitializeCallbacks)
  89. #pragma alloc_text(PAGE, ExCreateCallback)
  90. #pragma alloc_text(PAGE, ExpDeleteCallback)
  91. #pragma alloc_text(PAGE, ExInitializeCallBack)
  92. #pragma alloc_text(PAGE, ExCompareExchangeCallBack)
  93. #pragma alloc_text(PAGE, ExCallCallBack)
  94. #pragma alloc_text(PAGE, ExFreeCallBack)
  95. #pragma alloc_text(PAGE, ExAllocateCallBack)
  96. #pragma alloc_text(PAGE, ExReferenceCallBackBlock)
  97. #pragma alloc_text(PAGE, ExGetCallBackBlockRoutine)
  98. #pragma alloc_text(PAGE, ExWaitForCallBacks)
  99. #pragma alloc_text(PAGE, ExGetCallBackBlockContext)
  100. #pragma alloc_text(PAGE, ExDereferenceCallBackBlock)
  101. #endif
  102. BOOLEAN
  103. ExpInitializeCallbacks (
  104. )
  105. /*++
  106. Routine Description:
  107. This function creates the callback object type descriptor at system
  108. initialization and stores the address of the object type descriptor
  109. in local static storage.
  110. Arguments:
  111. None.
  112. Return Value:
  113. A value of TRUE is returned if the timer object type descriptor is
  114. successfully initialized. Otherwise a value of FALSE is returned.
  115. --*/
  116. {
  117. OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
  118. OBJECT_ATTRIBUTES ObjectAttributes;
  119. NTSTATUS Status;
  120. UNICODE_STRING unicodeString;
  121. ULONG i;
  122. HANDLE handle;
  123. //
  124. // Initialize the slow referencing lock
  125. //
  126. ExInitializePushLock (&ExpCallBackFlush);
  127. //
  128. // Initialize string descriptor.
  129. //
  130. RtlInitUnicodeString(&unicodeString, L"Callback");
  131. //
  132. // Create timer object type descriptor.
  133. //
  134. RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
  135. ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
  136. ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
  137. ObjectTypeInitializer.GenericMapping = ExpCallbackMapping;
  138. ObjectTypeInitializer.DeleteProcedure = ExpDeleteCallback;
  139. ObjectTypeInitializer.PoolType = NonPagedPool;
  140. ObjectTypeInitializer.ValidAccessMask = CALLBACK_ALL_ACCESS;
  141. Status = ObCreateObjectType(&unicodeString,
  142. &ObjectTypeInitializer,
  143. (PSECURITY_DESCRIPTOR)NULL,
  144. &ExCallbackObjectType);
  145. if (!NT_SUCCESS(Status)) {
  146. return FALSE;
  147. }
  148. RtlInitUnicodeString( &unicodeString, ExpWstrCallback );
  149. InitializeObjectAttributes(
  150. &ObjectAttributes,
  151. &unicodeString,
  152. OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
  153. NULL,
  154. SePublicDefaultSd
  155. );
  156. Status = NtCreateDirectoryObject(
  157. &handle,
  158. DIRECTORY_ALL_ACCESS,
  159. &ObjectAttributes
  160. );
  161. if (!NT_SUCCESS(Status)) {
  162. return FALSE;
  163. }
  164. NtClose (handle);
  165. //
  166. // Initialize event to wait on for Unregisters which occur while
  167. // notifications are in progress
  168. //
  169. KeInitializeEvent (&ExpCallbackEvent, NotificationEvent, 0);
  170. //
  171. // Initialize NT global callbacks
  172. //
  173. for (i=0; ExpInitializeCallback[i].CallBackObject; i++) {
  174. //
  175. // Create named calledback
  176. //
  177. RtlInitUnicodeString(&unicodeString, ExpInitializeCallback[i].CallbackName);
  178. InitializeObjectAttributes(
  179. &ObjectAttributes,
  180. &unicodeString,
  181. OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
  182. NULL,
  183. NULL
  184. );
  185. Status = ExCreateCallback (
  186. ExpInitializeCallback[i].CallBackObject,
  187. &ObjectAttributes,
  188. TRUE,
  189. TRUE
  190. );
  191. if (!NT_SUCCESS(Status)) {
  192. return FALSE;
  193. }
  194. }
  195. return TRUE;
  196. }
  197. NTSTATUS
  198. ExCreateCallback (
  199. OUT PCALLBACK_OBJECT * CallbackObject,
  200. IN POBJECT_ATTRIBUTES ObjectAttributes,
  201. IN BOOLEAN Create,
  202. IN BOOLEAN AllowMultipleCallbacks
  203. )
  204. /*++
  205. Routine Description:
  206. This function opens a callback object with the specified callback
  207. object. If the callback object does not exist or it is a NULL then
  208. a callback object will be created if create is TRUE. If a callbackobject
  209. is created it will only support multiple registered callbacks if
  210. AllowMulitipleCallbacks is TRUE.
  211. Arguments:
  212. CallbackObject - Supplies a pointer to a variable that will receive the
  213. Callback object.
  214. CallbackName - Supplies a pointer to a object name that will receive the
  215. Create - Supplies a flag which indicates whether a callback object will
  216. be created or not .
  217. AllowMultipleCallbacks - Supplies a flag which indicates only support
  218. mulitiple registered callbacks.
  219. Return Value:
  220. NTSTATUS.
  221. --*/
  222. {
  223. PCALLBACK_OBJECT cbObject;
  224. NTSTATUS Status;
  225. HANDLE Handle;
  226. PAGED_CODE();
  227. //
  228. // Initializing cbObject & Handle is not needed for correctness but without
  229. // it the compiler cannot compile this code W4 to check for use of
  230. // uninitialized variables.
  231. //
  232. Handle = NULL;
  233. cbObject = NULL;
  234. //
  235. // If named callback, open handle to it
  236. //
  237. if (ObjectAttributes->ObjectName) {
  238. Status = ObOpenObjectByName(ObjectAttributes,
  239. ExCallbackObjectType,
  240. KernelMode,
  241. NULL,
  242. 0, // DesiredAccess,
  243. NULL,
  244. &Handle);
  245. } else {
  246. Status = STATUS_UNSUCCESSFUL;
  247. }
  248. //
  249. // If not opened, check if callback should be created
  250. //
  251. if (!NT_SUCCESS(Status) && Create ) {
  252. Status = ObCreateObject(KernelMode,
  253. ExCallbackObjectType,
  254. ObjectAttributes,
  255. KernelMode,
  256. NULL,
  257. sizeof(CALLBACK_OBJECT),
  258. 0,
  259. 0,
  260. (PVOID *)&cbObject );
  261. if(NT_SUCCESS(Status)){
  262. //
  263. // Fill in structure signature
  264. //
  265. cbObject->Signature = 'llaC';
  266. //
  267. // It will support multiple registered callbacks if
  268. // AllowMultipleCallbacks is TRUE.
  269. //
  270. cbObject->AllowMultipleCallbacks = AllowMultipleCallbacks;
  271. //
  272. // Initialize CallbackObject queue.
  273. //
  274. InitializeListHead( &cbObject->RegisteredCallbacks );
  275. //
  276. // Initialize spinlock
  277. //
  278. KeInitializeSpinLock (&cbObject->Lock);
  279. //
  280. // Put the object in the root directory
  281. //
  282. Status = ObInsertObject (
  283. cbObject,
  284. NULL,
  285. FILE_READ_DATA,
  286. 0,
  287. NULL,
  288. &Handle );
  289. }
  290. }
  291. if(NT_SUCCESS(Status)){
  292. //
  293. // Add one to callback object reference count.
  294. //
  295. Status = ObReferenceObjectByHandle (
  296. Handle,
  297. 0, // DesiredAccess
  298. ExCallbackObjectType,
  299. KernelMode,
  300. &cbObject,
  301. NULL
  302. );
  303. ZwClose (Handle);
  304. }
  305. //
  306. // If success, returns a referenced pointer to the CallbackObject.
  307. //
  308. if (NT_SUCCESS(Status)) {
  309. *CallbackObject = cbObject;
  310. }
  311. return Status;
  312. }
  313. VOID
  314. ExpDeleteCallback (
  315. IN PCALLBACK_OBJECT CallbackObject
  316. )
  317. {
  318. #if !DBG
  319. UNREFERENCED_PARAMETER (CallbackObject);
  320. #endif
  321. ASSERT (IsListEmpty(&CallbackObject->RegisteredCallbacks));
  322. }
  323. PVOID
  324. ExRegisterCallback (
  325. IN PCALLBACK_OBJECT CallbackObject,
  326. IN PCALLBACK_FUNCTION CallbackFunction,
  327. IN PVOID CallbackContext
  328. )
  329. /*++
  330. Routine Description:
  331. This routine allows a caller to register that it would like to have its
  332. callback Function invoked when the callback notification call occurs.
  333. Arguments:
  334. CallbackObject - Supplies a pointer to a CallbackObject.
  335. CallbackFunction - Supplies a pointer to a function which is to
  336. be executed when the Callback notification occures.
  337. CallbackContext - Supplies a pointer to an arbitrary data structure
  338. that will be passed to the function specified by the CallbackFunction
  339. parameter.
  340. Return Value:
  341. Returns handle to callback registration.
  342. --*/
  343. {
  344. PCALLBACK_REGISTRATION CallbackRegistration;
  345. BOOLEAN Inserted;
  346. KIRQL OldIrql;
  347. ASSERT (CallbackFunction);
  348. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  349. //
  350. // Add reference to object
  351. //
  352. ObReferenceObject (CallbackObject);
  353. //
  354. // Begin by attempting to allocate storage for the CallbackRegistration.
  355. // one cannot be allocated, return the error status.
  356. //
  357. CallbackRegistration = ExAllocatePoolWithTag(
  358. NonPagedPool,
  359. sizeof( CALLBACK_REGISTRATION ),
  360. 'eRBC'
  361. );
  362. if( !CallbackRegistration ) {
  363. ObDereferenceObject (CallbackObject);
  364. return NULL;
  365. }
  366. //
  367. // Initialize the callback packet
  368. //
  369. CallbackRegistration->CallbackObject = CallbackObject;
  370. CallbackRegistration->CallbackFunction = CallbackFunction;
  371. CallbackRegistration->CallbackContext = CallbackContext;
  372. CallbackRegistration->Busy = 0;
  373. CallbackRegistration->UnregisterWaiting = FALSE;
  374. Inserted = FALSE;
  375. KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
  376. if( CallbackObject->AllowMultipleCallbacks ||
  377. IsListEmpty( &CallbackObject->RegisteredCallbacks ) ) {
  378. //
  379. // add CallbackRegistration to tail
  380. //
  381. Inserted = TRUE;
  382. InsertTailList( &CallbackObject->RegisteredCallbacks,
  383. &CallbackRegistration->Link );
  384. }
  385. KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
  386. if (!Inserted) {
  387. ExFreePool (CallbackRegistration);
  388. CallbackRegistration = NULL;
  389. }
  390. return (PVOID) CallbackRegistration;
  391. }
  392. VOID
  393. ExUnregisterCallback (
  394. IN PVOID CbRegistration
  395. )
  396. /*++
  397. Routine Description:
  398. This function removes the callback registration for the callbacks
  399. from the list of callback object .
  400. Arguments:
  401. CallbackRegistration - Pointer to device object for the file system.
  402. Return Value:
  403. None.
  404. --*/
  405. {
  406. PCALLBACK_REGISTRATION CallbackRegistration;
  407. PCALLBACK_OBJECT CallbackObject;
  408. KIRQL OldIrql;
  409. ASSERT (KeGetCurrentIrql() < DISPATCH_LEVEL);
  410. CallbackRegistration = (PCALLBACK_REGISTRATION) CbRegistration;
  411. CallbackObject = CallbackRegistration->CallbackObject;
  412. KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
  413. //
  414. // Wait for registration
  415. //
  416. while (CallbackRegistration->Busy) {
  417. //
  418. // Set waiting flag, then wait. (not performance critical - use
  419. // single global event to wait for any and all unregister waits)
  420. //
  421. CallbackRegistration->UnregisterWaiting = TRUE;
  422. KeClearEvent (&ExpCallbackEvent);
  423. KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
  424. KeWaitForSingleObject (
  425. &ExpCallbackEvent,
  426. Executive,
  427. KernelMode,
  428. FALSE,
  429. NULL
  430. );
  431. //
  432. // Synchronize with callback object and recheck registration busy
  433. //
  434. KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
  435. }
  436. //
  437. // Registration not busy, remove it from the callback object
  438. //
  439. RemoveEntryList (&CallbackRegistration->Link);
  440. KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
  441. //
  442. // Free memory used for CallbackRegistration
  443. //
  444. ExFreePool (CallbackRegistration);
  445. //
  446. // Remove reference count on CallbackObject
  447. //
  448. ObDereferenceObject (CallbackObject);
  449. }
  450. VOID
  451. ExNotifyCallback (
  452. IN PCALLBACK_OBJECT CallbackObject,
  453. IN PVOID Argument1,
  454. IN PVOID Argument2
  455. )
  456. /*++
  457. Routine Description:
  458. This function notifies all registered callbacks .
  459. Arguments:
  460. CallbackObject - supplies a pointer to the callback object should be
  461. notified.
  462. SystemArgument1 - supplies a pointer will be passed to callback function.
  463. SystemArgument2 - supplies a pointer will be passed to callback function.
  464. Return Value:
  465. None.
  466. --*/
  467. {
  468. PLIST_ENTRY Link;
  469. PCALLBACK_REGISTRATION CallbackRegistration;
  470. KIRQL OldIrql;
  471. if (CallbackObject == NULL) {
  472. return ;
  473. }
  474. //
  475. // Synchronize with callback object
  476. //
  477. KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
  478. //
  479. // call registered callbacks at callers IRQL level
  480. // ( done if FIFO order of registration )
  481. //
  482. if (OldIrql == DISPATCH_LEVEL) {
  483. //
  484. // OldIrql is DISPATCH_LEVEL, just invoke all callbacks without
  485. // releasing the lock
  486. //
  487. for (Link = CallbackObject->RegisteredCallbacks.Flink;
  488. Link != &CallbackObject->RegisteredCallbacks;
  489. Link = Link->Flink) {
  490. //
  491. // Get current registration to notify
  492. //
  493. CallbackRegistration = CONTAINING_RECORD (Link,
  494. CALLBACK_REGISTRATION,
  495. Link);
  496. //
  497. // Notify reigstration
  498. //
  499. CallbackRegistration->CallbackFunction(
  500. CallbackRegistration->CallbackContext,
  501. Argument1,
  502. Argument2
  503. );
  504. } // next registration
  505. } else {
  506. //
  507. // OldIrql is < DISPATCH_LEVEL, the code being called may be pagable
  508. // and the callback object spinlock needs to be released around
  509. // each registration callback.
  510. //
  511. for (Link = CallbackObject->RegisteredCallbacks.Flink;
  512. Link != &CallbackObject->RegisteredCallbacks;
  513. Link = Link->Flink ) {
  514. //
  515. // Get current registration to notify
  516. //
  517. CallbackRegistration = CONTAINING_RECORD (Link,
  518. CALLBACK_REGISTRATION,
  519. Link);
  520. //
  521. // If registration is being removed, don't bothing calling it
  522. //
  523. if (!CallbackRegistration->UnregisterWaiting) {
  524. //
  525. // Set registration busy
  526. //
  527. CallbackRegistration->Busy += 1;
  528. //
  529. // Release SpinLock and notify this callback
  530. //
  531. KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
  532. CallbackRegistration->CallbackFunction(
  533. CallbackRegistration->CallbackContext,
  534. Argument1,
  535. Argument2
  536. );
  537. //
  538. // Synchronize with CallbackObject
  539. //
  540. KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
  541. //
  542. // Remove our busy count
  543. //
  544. CallbackRegistration->Busy -= 1;
  545. //
  546. // If the registriation removal is pending, kick global
  547. // event let unregister conitnue
  548. //
  549. if (CallbackRegistration->UnregisterWaiting &&
  550. CallbackRegistration->Busy == 0) {
  551. KeSetEvent (&ExpCallbackEvent, 0, FALSE);
  552. }
  553. }
  554. }
  555. }
  556. //
  557. // Release callback
  558. //
  559. KeReleaseSpinLock (&CallbackObject->Lock, OldIrql);
  560. }
  561. VOID
  562. ExInitializeCallBack (
  563. IN OUT PEX_CALLBACK CallBack
  564. )
  565. /*++
  566. Routine Description:
  567. This function initializes a low overhead callback.
  568. Arguments:
  569. CallBack - Pointer to the callback structure
  570. Return Value:
  571. None.
  572. --*/
  573. {
  574. ExFastRefInitialize (&CallBack->RoutineBlock, NULL);
  575. }
  576. PEX_CALLBACK_ROUTINE_BLOCK
  577. ExAllocateCallBack (
  578. IN PEX_CALLBACK_FUNCTION Function,
  579. IN PVOID Context
  580. )
  581. /*++
  582. Routine Description:
  583. This function allocates a low overhead callback.
  584. Arguments:
  585. Function - Routine to issue callbacks to
  586. Context - A context value to issue
  587. Return Value:
  588. PEX_CALLBACK_ROUTINE_BLOCK - Allocated block or NULL if allocation fails.
  589. --*/
  590. {
  591. PEX_CALLBACK_ROUTINE_BLOCK NewBlock;
  592. NewBlock = ExAllocatePoolWithTag (PagedPool,
  593. sizeof (EX_CALLBACK_ROUTINE_BLOCK),
  594. 'brbC');
  595. if (NewBlock != NULL) {
  596. NewBlock->Function = Function;
  597. NewBlock->Context = Context;
  598. ExInitializeRundownProtection (&NewBlock->RundownProtect);
  599. }
  600. return NewBlock;
  601. }
  602. VOID
  603. ExFreeCallBack (
  604. IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
  605. )
  606. /*++
  607. Routine Description:
  608. This function destroys a low overhead callback block.
  609. Arguments:
  610. CallBackBlock - Call back block to destroy
  611. Return Value:
  612. None.
  613. --*/
  614. {
  615. ExFreePool (CallBackBlock);
  616. }
  617. VOID
  618. ExWaitForCallBacks (
  619. IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
  620. )
  621. /*++
  622. Routine Description:
  623. This function waits for all outcalls on the specified
  624. callback block to complete
  625. Arguments:
  626. CallBackBlock - Call back block to wait for
  627. Return Value:
  628. None.
  629. --*/
  630. {
  631. //
  632. // Wait for all active callbacks to be finished.
  633. //
  634. ExWaitForRundownProtectionRelease (&CallBackBlock->RundownProtect);
  635. }
  636. BOOLEAN
  637. ExCompareExchangeCallBack (
  638. IN OUT PEX_CALLBACK CallBack,
  639. IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock,
  640. IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock
  641. )
  642. /*++
  643. Routine Description:
  644. This function assigns, removes or swaps a low overhead callback function.
  645. Arguments:
  646. CallBack - Callback structure to be modified
  647. NewBlock - New block to be installed in the callback
  648. OldBlock - The old block that must be there now to be replaced
  649. Return Value:
  650. BOOLEAN - TRUE: The swap occured, FALSE: The swap failed
  651. --*/
  652. {
  653. EX_FAST_REF OldRef;
  654. PEX_CALLBACK_ROUTINE_BLOCK ReplacedBlock;
  655. if (NewBlock != NULL) {
  656. //
  657. // Add the additional references to the routine block
  658. //
  659. if (!ExAcquireRundownProtectionEx (&NewBlock->RundownProtect,
  660. ExFastRefGetAdditionalReferenceCount () + 1)) {
  661. ASSERTMSG ("Callback block is already undergoing rundown", FALSE);
  662. return FALSE;
  663. }
  664. }
  665. //
  666. // Attempt to replace the existing object and balance all the reference counts
  667. //
  668. OldRef = ExFastRefCompareSwapObject (&CallBack->RoutineBlock,
  669. NewBlock,
  670. OldBlock);
  671. ReplacedBlock = ExFastRefGetObject (OldRef);
  672. //
  673. // See if the swap occured. If it didn't undo the original references we added.
  674. // If it did then release remaining references on the original
  675. //
  676. if (ReplacedBlock == OldBlock) {
  677. PKTHREAD CurrentThread;
  678. //
  679. // We need to flush out any slow referencers at this point. We do this by
  680. // acquiring and releasing a lock.
  681. //
  682. if (ReplacedBlock != NULL) {
  683. CurrentThread = KeGetCurrentThread ();
  684. KeEnterCriticalRegionThread (CurrentThread);
  685. ExAcquireReleasePushLockExclusive (&ExpCallBackFlush);
  686. KeLeaveCriticalRegionThread (CurrentThread);
  687. ExReleaseRundownProtectionEx (&ReplacedBlock->RundownProtect,
  688. ExFastRefGetUnusedReferences (OldRef) + 1);
  689. }
  690. return TRUE;
  691. } else {
  692. //
  693. // The swap failed. Remove the addition references if we had added any.
  694. //
  695. if (NewBlock != NULL) {
  696. ExReleaseRundownProtectionEx (&NewBlock->RundownProtect,
  697. ExFastRefGetAdditionalReferenceCount () + 1);
  698. }
  699. return FALSE;
  700. }
  701. }
  702. PEX_CALLBACK_ROUTINE_BLOCK
  703. ExReferenceCallBackBlock (
  704. IN OUT PEX_CALLBACK CallBack
  705. )
  706. /*++
  707. Routine Description:
  708. This function takes a reference on the call back block inside the
  709. callback structure.
  710. Arguments:
  711. CallBack - Call back to obtain the call back block from
  712. Return Value:
  713. PEX_CALLBACK_ROUTINE_BLOCK - Referenced structure or NULL if these wasn't one
  714. --*/
  715. {
  716. EX_FAST_REF OldRef;
  717. PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock;
  718. //
  719. // Get a reference to the callback block if we can.
  720. //
  721. OldRef = ExFastReference (&CallBack->RoutineBlock);
  722. //
  723. // If there is no callback then return
  724. //
  725. if (ExFastRefObjectNull (OldRef)) {
  726. return NULL;
  727. }
  728. //
  729. // If we didn't get a reference then use a lock to get one.
  730. //
  731. if (!ExFastRefCanBeReferenced (OldRef)) {
  732. PKTHREAD CurrentThread;
  733. CurrentThread = KeGetCurrentThread ();
  734. KeEnterCriticalRegionThread (CurrentThread);
  735. ExAcquirePushLockExclusive (&ExpCallBackFlush);
  736. CallBackBlock = ExFastRefGetObject (CallBack->RoutineBlock);
  737. if (CallBackBlock && !ExAcquireRundownProtection (&CallBackBlock->RundownProtect)) {
  738. CallBackBlock = NULL;
  739. }
  740. ExReleasePushLockExclusive (&ExpCallBackFlush);
  741. KeLeaveCriticalRegionThread (CurrentThread);
  742. if (CallBackBlock == NULL) {
  743. return NULL;
  744. }
  745. } else {
  746. CallBackBlock = ExFastRefGetObject (OldRef);
  747. //
  748. // If we just removed the last reference then attempt fix it up.
  749. //
  750. if (ExFastRefIsLastReference (OldRef) && !ExpCallBackReturnRefs) {
  751. ULONG RefsToAdd;
  752. RefsToAdd = ExFastRefGetAdditionalReferenceCount ();
  753. //
  754. // If we can't add the references then just give up
  755. //
  756. if (ExAcquireRundownProtectionEx (&CallBackBlock->RundownProtect,
  757. RefsToAdd)) {
  758. //
  759. // Repopulate the cached refs. If this fails we just give them back.
  760. //
  761. if (!ExFastRefAddAdditionalReferenceCounts (&CallBack->RoutineBlock,
  762. CallBackBlock,
  763. RefsToAdd)) {
  764. ExReleaseRundownProtectionEx (&CallBackBlock->RundownProtect,
  765. RefsToAdd);
  766. }
  767. }
  768. }
  769. }
  770. return CallBackBlock;
  771. }
  772. PEX_CALLBACK_FUNCTION
  773. ExGetCallBackBlockRoutine (
  774. IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
  775. )
  776. /*++
  777. Routine Description:
  778. This function gets the routine associated with a call back block
  779. Arguments:
  780. CallBackBlock - Call back block to obtain routine for
  781. Return Value:
  782. PEX_CALLBACK_FUNCTION - The function pointer associated with this block
  783. --*/
  784. {
  785. return CallBackBlock->Function;
  786. }
  787. PVOID
  788. ExGetCallBackBlockContext (
  789. IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
  790. )
  791. /*++
  792. Routine Description:
  793. This function gets the context associated with a call back block
  794. Arguments:
  795. CallBackBlock - Call back block to obtain context for
  796. Return Value:
  797. PVOID - The context associated with this block
  798. --*/
  799. {
  800. return CallBackBlock->Context;
  801. }
  802. VOID
  803. ExDereferenceCallBackBlock (
  804. IN OUT PEX_CALLBACK CallBack,
  805. IN PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock
  806. )
  807. /*++
  808. Routine Description:
  809. This returns a reference previous obtained on a call back block
  810. Arguments:
  811. CallBackBlock - Call back block to return reference to
  812. Return Value:
  813. None
  814. --*/
  815. {
  816. if (ExpCallBackReturnRefs || !ExFastRefDereference (&CallBack->RoutineBlock, CallBackBlock)) {
  817. ExReleaseRundownProtection (&CallBackBlock->RundownProtect);
  818. }
  819. }
  820. NTSTATUS
  821. ExCallCallBack (
  822. IN OUT PEX_CALLBACK CallBack,
  823. IN PVOID Argument1,
  824. IN PVOID Argument2
  825. )
  826. /*++
  827. Routine Description:
  828. This function calls the callback thats inside a callback structure
  829. Arguments:
  830. CallBack - Call back that needs to be called through
  831. Argument1 - Caller provided argument to pass on
  832. Argument2 - Caller provided argument to pass on
  833. Return Value:
  834. NTSTATUS - Status returned by callback or STATUS_SUCCESS if theres wasn't one
  835. --*/
  836. {
  837. PEX_CALLBACK_ROUTINE_BLOCK CallBackBlock;
  838. NTSTATUS Status;
  839. CallBackBlock = ExReferenceCallBackBlock (CallBack);
  840. if (CallBackBlock) {
  841. //
  842. // Call the function
  843. //
  844. Status = CallBackBlock->Function (CallBackBlock->Context, Argument1, Argument2);
  845. ExDereferenceCallBackBlock (CallBack, CallBackBlock);
  846. } else {
  847. Status = STATUS_SUCCESS;
  848. }
  849. return Status;
  850. }