2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ex/callback.c
5 * PURPOSE: Executive callbacks
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
9 /* INCLUDES ******************************************************************/
13 #include <internal/debug.h>
15 /* TYPES *********************************************************************/
17 /* Mapping for Callback Object */
18 GENERIC_MAPPING ExpCallbackMapping
=
26 /* Kernel Default Callbacks */
27 PCALLBACK_OBJECT SetSystemTimeCallback
;
28 PCALLBACK_OBJECT SetSystemStateCallback
;
29 PCALLBACK_OBJECT PowerStateCallback
;
30 SYSTEM_CALLBACKS ExpInitializeCallback
[] =
32 {&SetSystemTimeCallback
, L
"\\Callback\\SetSystemTime"},
33 {&SetSystemStateCallback
, L
"\\Callback\\SetSystemState"},
34 {&PowerStateCallback
, L
"\\Callback\\PowerState"},
38 POBJECT_TYPE ExCallbackObjectType
;
39 KEVENT ExpCallbackEvent
;
40 EX_PUSH_LOCK ExpCallBackFlush
;
42 /* PRIVATE FUNCTIONS *********************************************************/
46 ExInitializeCallBack(IN OUT PEX_CALLBACK Callback
)
48 /* Initialize the fast references */
49 Callback
->RoutineBlock
.Object
= NULL
;
52 PEX_CALLBACK_ROUTINE_BLOCK
54 ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function
,
57 PEX_CALLBACK_ROUTINE_BLOCK Callback
;
59 /* Allocate a callback */
60 Callback
= ExAllocatePoolWithTag(PagedPool
,
62 TAG('C', 'b', 'r', 'b'));
66 Callback
->Function
= Function
;
67 Callback
->Context
= Context
;
68 ExInitializeRundownProtection(&Callback
->RundownProtect
);
77 ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK Callback
)
79 /* Just free it from memory */
85 ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK Callback
)
87 /* Wait on the rundown */
88 ExWaitForRundownProtectionRelease(&Callback
->RundownProtect
);
93 ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK Callback
)
95 /* Return the function */
96 return Callback
->Function
;
101 ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK Callback
)
103 /* Return the context */
104 return Callback
->Context
;
109 ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack
,
110 IN PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock
)
112 PEX_FAST_REF FastRef
= &CallBack
->RoutineBlock
;
113 EX_FAST_REF Value
, NewValue
;
116 ASSERT(CallbackRoutineBlock
);
117 ASSERT(!(((ULONG_PTR
)CallbackRoutineBlock
) & MAX_FAST_REFS
));
119 /* Start dereference loop */
122 /* Get the current count */
124 if ((Value
.Value
^ (ULONG_PTR
)CallbackRoutineBlock
) < MAX_FAST_REFS
)
126 /* Decrease the reference count */
127 NewValue
.Value
= Value
.Value
+ 1;
128 NewValue
.Object
= InterlockedCompareExchangePointer(&FastRef
->Object
,
131 if (NewValue
.Object
!= Value
.Object
) continue;
138 /* Release rundown protection */
139 ExReleaseRundownProtection(&CallbackRoutineBlock
->RundownProtect
);
144 PEX_CALLBACK_ROUTINE_BLOCK
146 ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack
)
148 PEX_FAST_REF FastRef
= &CallBack
->RoutineBlock
;
149 EX_FAST_REF Value
, NewValue
;
150 PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock
;
152 /* Start reference loop */
155 /* Get the current count */
157 if (Value
.RefCnt
!= 0)
159 /* Increase the reference count */
160 NewValue
.Value
= Value
.Value
- 1;
161 NewValue
.Object
= InterlockedCompareExchangePointer(&FastRef
->Object
,
164 if (NewValue
.Object
!= Value
.Object
) continue;
171 /* Fail if there isn't any object */
172 if (!Value
.Value
) return NULL
;
174 /* Check if we don't have a reference */
178 CallbackRoutineBlock
= NULL
;
179 DPRINT1("Unhandled callback race condition\n");
184 /* Get the callback block */
185 CallbackRoutineBlock
= (PVOID
)(Value
.Value
&~ MAX_FAST_REFS
);
187 /* Check if this is the last reference */
188 if (Value
.RefCnt
== 1)
190 /* Acquire rundown protection */
191 if (ExfAcquireRundownProtectionEx(&CallbackRoutineBlock
->
196 ASSERT(!(((ULONG_PTR
)CallbackRoutineBlock
) & MAX_FAST_REFS
));
198 /* Start reference loop */
201 /* Check if the current count is too high */
203 if (((Value
.RefCnt
+ MAX_FAST_REFS
) > MAX_FAST_REFS
) ||
204 ((Value
.Value
&~ MAX_FAST_REFS
) !=
205 (ULONG_PTR
)CallbackRoutineBlock
))
207 /* Backdown the rundown acquire */
208 ExfReleaseRundownProtectionEx(&CallbackRoutineBlock
->
214 /* Increase the reference count */
215 NewValue
.Value
= Value
.Value
+ MAX_FAST_REFS
;
217 InterlockedCompareExchangePointer(&FastRef
->Object
,
220 if (NewValue
.Object
!= Value
.Object
) continue;
222 /* Break out if the change was OK */
229 /* Return the callback block */
230 return CallbackRoutineBlock
;
235 ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack
,
236 IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock
,
237 IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock
)
239 EX_FAST_REF Value
, NewValue
;
240 PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock
;
241 PEX_FAST_REF FastRef
= &CallBack
->RoutineBlock
;
243 /* Check that we have a new block */
246 /* Acquire rundown */
247 if (!ExfAcquireRundownProtectionEx(&NewBlock
->RundownProtect
,
250 /* This should never happen */
251 ASSERTMSG("Callback block is already undergoing rundown", FALSE
);
256 /* Sanity check and start swap loop */
257 ASSERT(!(((ULONG_PTR
)NewBlock
) & MAX_FAST_REFS
));
260 /* Get the current value */
263 /* Make sure there's enough references to swap */
264 if (!((Value
.Value
^ (ULONG_PTR
)OldBlock
) <= MAX_FAST_REFS
)) break;
266 /* Check if we have an object to swap */
269 /* Set up the value with maximum fast references */
270 NewValue
.Value
= (ULONG_PTR
)NewBlock
| MAX_FAST_REFS
;
274 /* Write the object address itself (which is empty) */
275 NewValue
.Value
= (ULONG_PTR
)NewBlock
;
278 /* Do the actual compare exchange */
279 NewValue
.Object
= InterlockedCompareExchangePointer(&FastRef
->Object
,
282 if (NewValue
.Object
!= Value
.Object
) continue;
288 /* Get the routine block */
289 CallbackRoutineBlock
= (PVOID
)(Value
.Value
& ~MAX_FAST_REFS
);
291 /* Make sure the swap worked */
292 if (CallbackRoutineBlock
== OldBlock
)
294 /* Make sure we replaced a valid pointer */
295 if (CallbackRoutineBlock
)
297 /* Acquire the flush lock and immediately release it */
298 KeEnterCriticalRegion();
299 ExWaitOnPushLock(&ExpCallBackFlush
);
301 /* Release rundown protection */
302 KeLeaveCriticalRegion();
303 ExfReleaseRundownProtectionEx(&CallbackRoutineBlock
->RundownProtect
,
312 /* It failed, check if we had a block */
315 /* We did, remove the refernces that we had added */
316 ExfReleaseRundownProtectionEx(&NewBlock
->RundownProtect
,
327 ExpDeleteCallback(IN PVOID Object
)
330 ASSERT(IsListEmpty(&((PCALLBACK_OBJECT
)Object
)->RegisteredCallbacks
));
334 * @name ExpInitializeCallbacks
336 * Creates the Callback Object as a valid Object Type in the Kernel.
337 * Internal function, subject to further review
339 * @return TRUE if the Callback Object Type was successfully created.
347 ExpInitializeCallbacks(VOID
)
349 OBJECT_ATTRIBUTES ObjectAttributes
;
351 UNICODE_STRING DirName
= RTL_CONSTANT_STRING(L
"\\Callback");
352 UNICODE_STRING CallbackName
;
354 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
355 HANDLE DirectoryHandle
;
358 /* Setup lightweight callback lock */
359 ExpCallBackFlush
.Value
= 0;
361 /* Initialize the Callback Object type */
362 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
363 RtlInitUnicodeString(&Name
, L
"Callback");
364 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
365 ObjectTypeInitializer
.InvalidAttributes
= OBJ_OPENLINK
;
366 ObjectTypeInitializer
.GenericMapping
= ExpCallbackMapping
;
367 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
368 ObjectTypeInitializer
.DeleteProcedure
= ExpDeleteCallback
;
369 ObjectTypeInitializer
.ValidAccessMask
= CALLBACK_ALL_ACCESS
;
370 Status
= ObCreateObjectType(&Name
,
371 &ObjectTypeInitializer
,
373 &ExCallbackObjectType
);
374 if (!NT_SUCCESS(Status
)) return FALSE
;
376 /* Initialize the Object */
377 InitializeObjectAttributes(&ObjectAttributes
,
379 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
383 /* Create the Object Directory */
384 Status
= NtCreateDirectoryObject(&DirectoryHandle
,
385 DIRECTORY_ALL_ACCESS
,
387 if (!NT_SUCCESS(Status
)) return FALSE
;
389 /* Close Handle... */
390 NtClose(DirectoryHandle
);
392 /* Initialize Event used when unregistering */
393 KeInitializeEvent(&ExpCallbackEvent
, NotificationEvent
, 0);
395 /* Default NT Kernel Callbacks. */
396 for (i
= 0; ExpInitializeCallback
[i
].CallbackObject
; i
++)
398 /* Create the name from the structure */
399 RtlInitUnicodeString(&CallbackName
, ExpInitializeCallback
[i
].Name
);
401 /* Initialize the Object Attributes Structure */
402 InitializeObjectAttributes(&ObjectAttributes
,
404 OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
408 /* Create the Callback Object */
409 Status
= ExCreateCallback(ExpInitializeCallback
[i
].CallbackObject
,
413 if (!NT_SUCCESS(Status
)) return FALSE
;
416 /* Everything successful */
420 /* PUBLIC FUNCTIONS **********************************************************/
423 * @name ExCreateCallback
426 * Opens or creates a Callback Object. Creates only if Create is true.
427 * Allows multiple Callback Functions to be registred only if
428 * AllowMultipleCallbacks is true.
429 * See: http://www.osronline.com/ddkx/kmarch/k102_967m.htm
430 * http://www.osronline.com/article.cfm?id=24
432 * @param CallbackObject
433 * Pointer that will receive the Callback Object.
435 * @param CallbackName
439 * Determines if the object will be created if it doesn't exit
441 * @param AllowMultipleCallbacks
442 * Determines if more then one registered callback function
443 * can be attached to this Callback Object.
445 * @return STATUS_SUCESS if not failed.
447 * @remarks Must be called at IRQL = PASSIVE_LEVEL
452 ExCreateCallback(OUT PCALLBACK_OBJECT
*CallbackObject
,
453 IN POBJECT_ATTRIBUTES ObjectAttributes
,
455 IN BOOLEAN AllowMultipleCallbacks
)
457 PCALLBACK_OBJECT Callback
= NULL
;
459 HANDLE Handle
= NULL
;
462 /* Open a handle to the callback if it exists */
463 if (ObjectAttributes
->ObjectName
)
465 /* Open the handle */
466 Status
= ObOpenObjectByName(ObjectAttributes
,
467 ExCallbackObjectType
,
476 /* Otherwise, fail */
477 Status
= STATUS_UNSUCCESSFUL
;
480 /* We weren't able to open it...should we create it? */
481 if (!(NT_SUCCESS(Status
)) && (Create
))
483 /* Create the object */
484 Status
= ObCreateObject(KernelMode
,
485 ExCallbackObjectType
,
489 sizeof(CALLBACK_OBJECT
),
493 if (NT_SUCCESS(Status
))
496 Callback
->Signature
= TAG('C', 'a', 'l', 'l');
497 KeInitializeSpinLock(&Callback
->Lock
);
498 InitializeListHead(&Callback
->RegisteredCallbacks
);
499 Callback
->AllowMultipleCallbacks
= AllowMultipleCallbacks
;
501 /* Insert the object into the object namespace */
502 Status
= ObInsertObject(Callback
,
511 /* Check if we have success until here */
512 if (NT_SUCCESS(Status
))
514 /* Get a pointer to the new object from the handle we just got */
515 Status
= ObReferenceObjectByHandle(Handle
,
517 ExCallbackObjectType
,
522 /* Close the Handle, since we now have the pointer */
526 /* Everything went fine, so return a pointer to the Object */
527 if (NT_SUCCESS(Status
)) *CallbackObject
= Callback
;
532 * @name ExNotifyCallback
535 * Calls a function pointer (a registered callback)
536 * See: http://www.osronline.com/ddkx/kmarch/k102_2f5e.htm
537 * http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt
539 * @param CallbackObject
540 * Which callback to call
543 * Pointer/data to send to callback function
546 * Pointer/data to send to callback function
555 ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject
,
559 PLIST_ENTRY RegisteredCallbacks
;
560 PCALLBACK_REGISTRATION CallbackRegistration
;
563 /* Check if we don't have an object or registrations */
564 if (!(CallbackObject
) ||
565 (IsListEmpty(&CallbackObject
->RegisteredCallbacks
)))
571 /* Acquire the Lock */
572 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
574 /* Enumerate through all the registered functions */
575 for (RegisteredCallbacks
= CallbackObject
->RegisteredCallbacks
.Flink
;
576 RegisteredCallbacks
!= &CallbackObject
->RegisteredCallbacks
;
577 RegisteredCallbacks
= RegisteredCallbacks
->Flink
)
579 /* Get a pointer to a Callback Registration from the List Entries */
580 CallbackRegistration
= CONTAINING_RECORD(RegisteredCallbacks
,
581 CALLBACK_REGISTRATION
,
584 /* Don't bother doing notification if it's pending to be deleted */
585 if (!CallbackRegistration
->UnregisterWaiting
)
587 /* Mark the Callback in use, so it won't get deleted */
588 CallbackRegistration
->Busy
+= 1;
590 /* Release the Spinlock before making the call */
591 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
593 /* Call the Registered Function */
594 CallbackRegistration
->CallbackFunction(CallbackRegistration
->
599 /* Get SpinLock back */
600 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
602 /* We are not in use anymore */
603 CallbackRegistration
->Busy
-= 1;
605 /* Check if removal is pending and we're not active */
606 if ((CallbackRegistration
->UnregisterWaiting
) &&
607 !(CallbackRegistration
->Busy
))
609 /* Signal the callback event */
610 KeSetEvent(&ExpCallbackEvent
, 0, FALSE
);
615 /* Release the Callback Object */
616 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
620 * @name ExRegisterCallback
623 * Allows a function to associate a callback pointer (Function) to
624 * a created Callback object
625 * See: DDK, OSR, links in ExNotifyCallback
627 * @param CallbackObject
628 * The Object Created with ExCreateCallBack
630 * @param CallBackFunction
631 * Pointer to the function to be called back
633 * @param CallBackContext
634 * Block of memory that can contain user-data which will be
635 * passed on to the callback
637 * @return A handle to a Callback Registration Structure (MSDN Documentation)
644 ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject
,
645 IN PCALLBACK_FUNCTION CallbackFunction
,
646 IN PVOID CallbackContext
)
648 PCALLBACK_REGISTRATION CallbackRegistration
= NULL
;
652 ASSERT(CallbackFunction
);
653 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
655 /* Create reference to Callback Object */
656 ObReferenceObject(CallbackObject
);
658 /* Allocate memory for the structure */
659 CallbackRegistration
= ExAllocatePoolWithTag(NonPagedPool
,
660 sizeof(CALLBACK_REGISTRATION
),
662 if (!CallbackRegistration
)
664 /* Dereference and fail */
665 ObDereferenceObject (CallbackObject
);
669 /* Create Callback Registration */
670 CallbackRegistration
->CallbackObject
= CallbackObject
;
671 CallbackRegistration
->CallbackFunction
= CallbackFunction
;
672 CallbackRegistration
->CallbackContext
= CallbackContext
;
673 CallbackRegistration
->Busy
= 0;
674 CallbackRegistration
->UnregisterWaiting
= FALSE
;
676 /* Acquire SpinLock */
677 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
679 /* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
680 if ((CallbackObject
->AllowMultipleCallbacks
) ||
681 (IsListEmpty(&CallbackObject
->RegisteredCallbacks
)))
683 /* Register the callback */
684 InsertTailList(&CallbackObject
->RegisteredCallbacks
,
685 &CallbackRegistration
->Link
);
687 /* Release SpinLock */
688 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
692 /* Release SpinLock */
693 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
695 /* Free the registration */
696 ExFreePool(CallbackRegistration
);
697 CallbackRegistration
= NULL
;
699 /* Dereference the object */
700 ObDereferenceObject(CallbackObject
);
703 /* Return handle to Registration Object */
704 return (PVOID
)CallbackRegistration
;
708 * @name ExUnregisterCallback
711 * Deregisters a CallBack
712 * See: DDK, OSR, links in ExNotifyCallback
714 * @param CallbackRegistration
715 * Callback Registration Handle
724 ExUnregisterCallback(IN PVOID CallbackRegistrationHandle
)
726 PCALLBACK_REGISTRATION CallbackRegistration
;
727 PCALLBACK_OBJECT CallbackObject
;
729 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
731 /* Convert Handle to valid Structure Pointer */
732 CallbackRegistration
= (PCALLBACK_REGISTRATION
)CallbackRegistrationHandle
;
734 /* Get the Callback Object */
735 CallbackObject
= CallbackRegistration
->CallbackObject
;
737 /* Lock the Object */
738 KeAcquireSpinLock (&CallbackObject
->Lock
, &OldIrql
);
740 /* We can't Delete the Callback if it's in use */
741 while (CallbackRegistration
->Busy
)
743 /* Let everyone else know we're unregistering */
744 CallbackRegistration
->UnregisterWaiting
= TRUE
;
746 /* We are going to wait for the event, so the Lock isn't necessary */
747 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
749 /* Make sure the event is cleared */
750 KeClearEvent(&ExpCallbackEvent
);
752 /* Wait for the Event */
753 KeWaitForSingleObject(&ExpCallbackEvent
,
759 /* We need the Lock again */
760 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
763 /* Remove the Callback */
764 RemoveEntryList(&CallbackRegistration
->Link
);
766 /* It's now safe to release the lock */
767 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
769 /* Delete this registration */
770 ExFreePool(CallbackRegistration
);
772 /* Remove the reference */
773 ObDereferenceObject(CallbackObject
);