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 ******************************************************************/
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 reference */
49 ExInitializeFastReference(&Callback
->RoutineBlock
, NULL
);
52 PEX_CALLBACK_ROUTINE_BLOCK
54 ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function
,
57 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
;
59 /* Allocate a callback */
60 CallbackBlock
= ExAllocatePoolWithTag(PagedPool
,
61 sizeof(EX_CALLBACK_ROUTINE_BLOCK
),
66 CallbackBlock
->Function
= Function
;
67 CallbackBlock
->Context
= Context
;
68 ExInitializeRundownProtection(&CallbackBlock
->RundownProtect
);
77 ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
79 /* Just free it from memory */
80 ExFreePool(CallbackBlock
);
85 ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
87 /* Wait on the rundown */
88 ExWaitForRundownProtectionRelease(&CallbackBlock
->RundownProtect
);
93 ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
95 /* Return the function */
96 return CallbackBlock
->Function
;
101 ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
103 /* Return the context */
104 return CallbackBlock
->Context
;
109 ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack
,
110 IN PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
)
112 /* Release a fast reference */
113 if (!ExReleaseFastReference(&CallBack
->RoutineBlock
, CallbackBlock
))
116 ExReleaseRundownProtection(&CallbackBlock
->RundownProtect
);
120 PEX_CALLBACK_ROUTINE_BLOCK
122 ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack
)
124 EX_FAST_REF OldValue
;
126 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
;
128 /* Acquire a reference */
129 OldValue
= ExAcquireFastReference(&CallBack
->RoutineBlock
);
130 Count
= ExGetCountFastReference(OldValue
);
132 /* Fail if there isn't any object */
133 if (!ExGetObjectFastReference(OldValue
)) return NULL
;
135 /* Check if we don't have a reference */
139 DPRINT1("Unhandled callback race condition\n");
144 /* Get the callback block */
145 CallbackBlock
= ExGetObjectFastReference(OldValue
);
147 /* Check if this is the last reference */
150 /* Acquire rundown protection */
151 if (ExfAcquireRundownProtectionEx(&CallbackBlock
->RundownProtect
,
154 /* Insert references */
155 if (!ExInsertFastReference(&CallBack
->RoutineBlock
, CallbackBlock
))
157 /* Backdown the rundown acquire */
158 ExfReleaseRundownProtectionEx(&CallbackBlock
->RundownProtect
,
164 /* Return the callback block */
165 return CallbackBlock
;
170 ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack
,
171 IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock
,
172 IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock
)
174 EX_FAST_REF OldValue
;
175 PEX_CALLBACK_ROUTINE_BLOCK CallbackBlock
;
178 /* Check that we have a new block */
181 /* Acquire rundown */
182 if (!ExfAcquireRundownProtectionEx(&NewBlock
->RundownProtect
,
185 /* This should never happen */
186 ASSERTMSG("Callback block is already undergoing rundown", FALSE
);
192 OldValue
= ExCompareSwapFastReference(&CallBack
->RoutineBlock
,
196 /* Get the routine block */
197 CallbackBlock
= ExGetObjectFastReference(OldValue
);
198 Count
= ExGetCountFastReference(OldValue
);
200 /* Make sure the swap worked */
201 if (CallbackBlock
== OldBlock
)
203 /* Make sure we replaced a valid pointer */
206 /* Acquire the flush lock and immediately release it */
207 KeEnterCriticalRegion();
208 ExWaitOnPushLock(&ExpCallBackFlush
);
210 /* Release rundown protection */
211 KeLeaveCriticalRegion();
212 ExfReleaseRundownProtectionEx(&CallbackBlock
->RundownProtect
,
221 /* It failed, check if we had a block */
224 /* We did, remove the refernces that we had added */
225 ExfReleaseRundownProtectionEx(&NewBlock
->RundownProtect
,
236 ExpDeleteCallback(IN PVOID Object
)
239 ASSERT(IsListEmpty(&((PCALLBACK_OBJECT
)Object
)->RegisteredCallbacks
));
243 * @name ExpInitializeCallbacks
245 * Creates the Callback Object as a valid Object Type in the Kernel.
246 * Internal function, subject to further review
248 * @return TRUE if the Callback Object Type was successfully created.
256 ExpInitializeCallbacks(VOID
)
258 OBJECT_ATTRIBUTES ObjectAttributes
;
260 UNICODE_STRING DirName
= RTL_CONSTANT_STRING(L
"\\Callback");
261 UNICODE_STRING CallbackName
;
263 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer
;
264 HANDLE DirectoryHandle
;
267 /* Setup lightweight callback lock */
268 ExpCallBackFlush
.Value
= 0;
270 /* Initialize the Callback Object type */
271 RtlZeroMemory(&ObjectTypeInitializer
, sizeof(ObjectTypeInitializer
));
272 RtlInitUnicodeString(&Name
, L
"Callback");
273 ObjectTypeInitializer
.Length
= sizeof(ObjectTypeInitializer
);
274 ObjectTypeInitializer
.InvalidAttributes
= OBJ_OPENLINK
;
275 ObjectTypeInitializer
.GenericMapping
= ExpCallbackMapping
;
276 ObjectTypeInitializer
.PoolType
= NonPagedPool
;
277 ObjectTypeInitializer
.DeleteProcedure
= ExpDeleteCallback
;
278 ObjectTypeInitializer
.ValidAccessMask
= CALLBACK_ALL_ACCESS
;
279 Status
= ObCreateObjectType(&Name
,
280 &ObjectTypeInitializer
,
282 &ExCallbackObjectType
);
283 if (!NT_SUCCESS(Status
)) return FALSE
;
285 /* Initialize the Object */
286 InitializeObjectAttributes(&ObjectAttributes
,
288 OBJ_CASE_INSENSITIVE
| OBJ_PERMANENT
,
292 /* Create the Object Directory */
293 Status
= NtCreateDirectoryObject(&DirectoryHandle
,
294 DIRECTORY_ALL_ACCESS
,
296 if (!NT_SUCCESS(Status
)) return FALSE
;
298 /* Close Handle... */
299 NtClose(DirectoryHandle
);
301 /* Initialize Event used when unregistering */
302 KeInitializeEvent(&ExpCallbackEvent
, NotificationEvent
, 0);
304 /* Default NT Kernel Callbacks. */
305 for (i
= 0; ExpInitializeCallback
[i
].CallbackObject
; i
++)
307 /* Create the name from the structure */
308 RtlInitUnicodeString(&CallbackName
, ExpInitializeCallback
[i
].Name
);
310 /* Initialize the Object Attributes Structure */
311 InitializeObjectAttributes(&ObjectAttributes
,
313 OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
317 /* Create the Callback Object */
318 Status
= ExCreateCallback(ExpInitializeCallback
[i
].CallbackObject
,
322 if (!NT_SUCCESS(Status
)) return FALSE
;
325 /* Everything successful */
329 /* PUBLIC FUNCTIONS **********************************************************/
332 * @name ExCreateCallback
335 * Opens or creates a Callback Object. Creates only if Create is true.
336 * Allows multiple Callback Functions to be registred only if
337 * AllowMultipleCallbacks is true.
338 * See: http://www.osronline.com/ddkx/kmarch/k102_967m.htm
339 * http://www.osronline.com/article.cfm?id=24
341 * @param CallbackObject
342 * Pointer that will receive the Callback Object.
344 * @param CallbackName
348 * Determines if the object will be created if it doesn't exit
350 * @param AllowMultipleCallbacks
351 * Determines if more then one registered callback function
352 * can be attached to this Callback Object.
354 * @return STATUS_SUCESS if not failed.
356 * @remarks Must be called at IRQL = PASSIVE_LEVEL
361 ExCreateCallback(OUT PCALLBACK_OBJECT
*CallbackObject
,
362 IN POBJECT_ATTRIBUTES ObjectAttributes
,
364 IN BOOLEAN AllowMultipleCallbacks
)
366 PCALLBACK_OBJECT Callback
= NULL
;
368 HANDLE Handle
= NULL
;
371 /* Open a handle to the callback if it exists */
372 if (ObjectAttributes
->ObjectName
)
374 /* Open the handle */
375 Status
= ObOpenObjectByName(ObjectAttributes
,
376 ExCallbackObjectType
,
385 /* Otherwise, fail */
386 Status
= STATUS_UNSUCCESSFUL
;
389 /* We weren't able to open it...should we create it? */
390 if (!(NT_SUCCESS(Status
)) && (Create
))
392 /* Create the object */
393 Status
= ObCreateObject(KernelMode
,
394 ExCallbackObjectType
,
398 sizeof(CALLBACK_OBJECT
),
402 if (NT_SUCCESS(Status
))
405 Callback
->Signature
= 'llaC';
406 KeInitializeSpinLock(&Callback
->Lock
);
407 InitializeListHead(&Callback
->RegisteredCallbacks
);
408 Callback
->AllowMultipleCallbacks
= AllowMultipleCallbacks
;
410 /* Insert the object into the object namespace */
411 Status
= ObInsertObject(Callback
,
420 /* Check if we have success until here */
421 if (NT_SUCCESS(Status
))
423 /* Get a pointer to the new object from the handle we just got */
424 Status
= ObReferenceObjectByHandle(Handle
,
426 ExCallbackObjectType
,
431 /* Close the Handle, since we now have the pointer */
435 /* Everything went fine, so return a pointer to the Object */
436 if (NT_SUCCESS(Status
)) *CallbackObject
= Callback
;
441 * @name ExNotifyCallback
444 * Calls a function pointer (a registered callback)
445 * See: http://www.osronline.com/ddkx/kmarch/k102_2f5e.htm
446 * http://vmsone.com/~decuslib/vmssig/vmslt99b/nt/wdm-callback.txt
448 * @param CallbackObject
449 * Which callback to call
452 * Pointer/data to send to callback function
455 * Pointer/data to send to callback function
464 ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject
,
468 PLIST_ENTRY RegisteredCallbacks
;
469 PCALLBACK_REGISTRATION CallbackRegistration
;
472 /* Check if we don't have an object or registrations */
473 if (!(CallbackObject
) ||
474 (IsListEmpty(&CallbackObject
->RegisteredCallbacks
)))
480 /* Acquire the Lock */
481 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
483 /* Enumerate through all the registered functions */
484 for (RegisteredCallbacks
= CallbackObject
->RegisteredCallbacks
.Flink
;
485 RegisteredCallbacks
!= &CallbackObject
->RegisteredCallbacks
;
486 RegisteredCallbacks
= RegisteredCallbacks
->Flink
)
488 /* Get a pointer to a Callback Registration from the List Entries */
489 CallbackRegistration
= CONTAINING_RECORD(RegisteredCallbacks
,
490 CALLBACK_REGISTRATION
,
493 /* Don't bother doing notification if it's pending to be deleted */
494 if (!CallbackRegistration
->UnregisterWaiting
)
496 /* Mark the Callback in use, so it won't get deleted */
497 CallbackRegistration
->Busy
+= 1;
499 /* Release the Spinlock before making the call */
500 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
502 /* Call the Registered Function */
503 CallbackRegistration
->CallbackFunction(CallbackRegistration
->
508 /* Get SpinLock back */
509 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
511 /* We are not in use anymore */
512 CallbackRegistration
->Busy
-= 1;
514 /* Check if removal is pending and we're not active */
515 if ((CallbackRegistration
->UnregisterWaiting
) &&
516 !(CallbackRegistration
->Busy
))
518 /* Signal the callback event */
519 KeSetEvent(&ExpCallbackEvent
, 0, FALSE
);
524 /* Release the Callback Object */
525 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
529 * @name ExRegisterCallback
532 * Allows a function to associate a callback pointer (Function) to
533 * a created Callback object
534 * See: DDK, OSR, links in ExNotifyCallback
536 * @param CallbackObject
537 * The Object Created with ExCreateCallBack
539 * @param CallBackFunction
540 * Pointer to the function to be called back
542 * @param CallBackContext
543 * Block of memory that can contain user-data which will be
544 * passed on to the callback
546 * @return A handle to a Callback Registration Structure (MSDN Documentation)
553 ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject
,
554 IN PCALLBACK_FUNCTION CallbackFunction
,
555 IN PVOID CallbackContext
)
557 PCALLBACK_REGISTRATION CallbackRegistration
= NULL
;
561 ASSERT(CallbackFunction
);
562 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
564 /* Create reference to Callback Object */
565 ObReferenceObject(CallbackObject
);
567 /* Allocate memory for the structure */
568 CallbackRegistration
= ExAllocatePoolWithTag(NonPagedPool
,
569 sizeof(CALLBACK_REGISTRATION
),
571 if (!CallbackRegistration
)
573 /* Dereference and fail */
574 ObDereferenceObject (CallbackObject
);
578 /* Create Callback Registration */
579 CallbackRegistration
->CallbackObject
= CallbackObject
;
580 CallbackRegistration
->CallbackFunction
= CallbackFunction
;
581 CallbackRegistration
->CallbackContext
= CallbackContext
;
582 CallbackRegistration
->Busy
= 0;
583 CallbackRegistration
->UnregisterWaiting
= FALSE
;
585 /* Acquire SpinLock */
586 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
588 /* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
589 if ((CallbackObject
->AllowMultipleCallbacks
) ||
590 (IsListEmpty(&CallbackObject
->RegisteredCallbacks
)))
592 /* Register the callback */
593 InsertTailList(&CallbackObject
->RegisteredCallbacks
,
594 &CallbackRegistration
->Link
);
596 /* Release SpinLock */
597 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
601 /* Release SpinLock */
602 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
604 /* Free the registration */
605 ExFreePool(CallbackRegistration
);
606 CallbackRegistration
= NULL
;
608 /* Dereference the object */
609 ObDereferenceObject(CallbackObject
);
612 /* Return handle to Registration Object */
613 return (PVOID
)CallbackRegistration
;
617 * @name ExUnregisterCallback
620 * Deregisters a CallBack
621 * See: DDK, OSR, links in ExNotifyCallback
623 * @param CallbackRegistration
624 * Callback Registration Handle
633 ExUnregisterCallback(IN PVOID CallbackRegistrationHandle
)
635 PCALLBACK_REGISTRATION CallbackRegistration
;
636 PCALLBACK_OBJECT CallbackObject
;
638 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL
);
640 /* Convert Handle to valid Structure Pointer */
641 CallbackRegistration
= (PCALLBACK_REGISTRATION
)CallbackRegistrationHandle
;
643 /* Get the Callback Object */
644 CallbackObject
= CallbackRegistration
->CallbackObject
;
646 /* Lock the Object */
647 KeAcquireSpinLock (&CallbackObject
->Lock
, &OldIrql
);
649 /* We can't Delete the Callback if it's in use */
650 while (CallbackRegistration
->Busy
)
652 /* Let everyone else know we're unregistering */
653 CallbackRegistration
->UnregisterWaiting
= TRUE
;
655 /* We are going to wait for the event, so the Lock isn't necessary */
656 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
658 /* Make sure the event is cleared */
659 KeClearEvent(&ExpCallbackEvent
);
661 /* Wait for the Event */
662 KeWaitForSingleObject(&ExpCallbackEvent
,
668 /* We need the Lock again */
669 KeAcquireSpinLock(&CallbackObject
->Lock
, &OldIrql
);
672 /* Remove the Callback */
673 RemoveEntryList(&CallbackRegistration
->Link
);
675 /* It's now safe to release the lock */
676 KeReleaseSpinLock(&CallbackObject
->Lock
, OldIrql
);
678 /* Delete this registration */
679 ExFreePool(CallbackRegistration
);
681 /* Remove the reference */
682 ObDereferenceObject(CallbackObject
);