- KdDebuggerNotPresent should be FALSE by default.
[reactos.git] / reactos / ntoskrnl / ex / callback.c
1 /*
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)
7 */
8
9 /* INCLUDES ******************************************************************/
10
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <internal/debug.h>
14
15 /* TYPES *********************************************************************/
16
17 /* Mapping for Callback Object */
18 GENERIC_MAPPING ExpCallbackMapping =
19 {
20 CALLBACK_READ,
21 CALLBACK_WRITE,
22 CALLBACK_EXECUTE,
23 CALLBACK_ALL_ACCESS
24 };
25
26 /* Kernel Default Callbacks */
27 PCALLBACK_OBJECT SetSystemTimeCallback;
28 PCALLBACK_OBJECT SetSystemStateCallback;
29 PCALLBACK_OBJECT PowerStateCallback;
30 SYSTEM_CALLBACKS ExpInitializeCallback[] =
31 {
32 {&SetSystemTimeCallback, L"\\Callback\\SetSystemTime"},
33 {&SetSystemStateCallback, L"\\Callback\\SetSystemState"},
34 {&PowerStateCallback, L"\\Callback\\PowerState"},
35 {NULL, NULL}
36 };
37
38 POBJECT_TYPE ExCallbackObjectType;
39 KEVENT ExpCallbackEvent;
40 EX_PUSH_LOCK ExpCallBackFlush;
41
42 /* PRIVATE FUNCTIONS *********************************************************/
43
44 VOID
45 NTAPI
46 ExInitializeCallBack(IN OUT PEX_CALLBACK Callback)
47 {
48 /* Initialize the fast references */
49 Callback->RoutineBlock.Object = NULL;
50 }
51
52 PEX_CALLBACK_ROUTINE_BLOCK
53 NTAPI
54 ExAllocateCallBack(IN PEX_CALLBACK_FUNCTION Function,
55 IN PVOID Context)
56 {
57 PEX_CALLBACK_ROUTINE_BLOCK Callback;
58
59 /* Allocate a callback */
60 Callback = ExAllocatePoolWithTag(PagedPool,
61 sizeof(*Callback),
62 TAG('C', 'b', 'r', 'b'));
63 if (Callback)
64 {
65 /* Initialize it */
66 Callback->Function = Function;
67 Callback->Context = Context;
68 ExInitializeRundownProtection(&Callback->RundownProtect);
69 }
70
71 /* Return it */
72 return Callback;
73 }
74
75 VOID
76 NTAPI
77 ExFreeCallBack(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
78 {
79 /* Just free it from memory */
80 ExFreePool(Callback);
81 }
82
83 VOID
84 NTAPI
85 ExWaitForCallBacks(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
86 {
87 /* Wait on the rundown */
88 ExWaitForRundownProtectionRelease(&Callback->RundownProtect);
89 }
90
91 PEX_CALLBACK_FUNCTION
92 NTAPI
93 ExGetCallBackBlockRoutine(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
94 {
95 /* Return the function */
96 return Callback->Function;
97 }
98
99 PVOID
100 NTAPI
101 ExGetCallBackBlockContext(IN PEX_CALLBACK_ROUTINE_BLOCK Callback)
102 {
103 /* Return the context */
104 return Callback->Context;
105 }
106
107 VOID
108 NTAPI
109 ExDereferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack,
110 IN PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock)
111 {
112 PEX_FAST_REF FastRef = &CallBack->RoutineBlock;
113 EX_FAST_REF Value, NewValue;
114
115 /* Sanity checks */
116 ASSERT(CallbackRoutineBlock);
117 ASSERT(!(((ULONG_PTR)CallbackRoutineBlock) & MAX_FAST_REFS));
118
119 /* Start dereference loop */
120 for (;;)
121 {
122 /* Get the current count */
123 Value = *FastRef;
124 if ((Value.Value ^ (ULONG_PTR)CallbackRoutineBlock) < MAX_FAST_REFS)
125 {
126 /* Decrease the reference count */
127 NewValue.Value = Value.Value + 1;
128 NewValue.Object = InterlockedCompareExchangePointer(&FastRef->Object,
129 NewValue.Object,
130 Value.Object);
131 if (NewValue.Object != Value.Object) continue;
132
133 /* We're all done */
134 break;
135 }
136 else
137 {
138 /* Release rundown protection */
139 ExReleaseRundownProtection(&CallbackRoutineBlock->RundownProtect);
140 }
141 }
142 }
143
144 PEX_CALLBACK_ROUTINE_BLOCK
145 NTAPI
146 ExReferenceCallBackBlock(IN OUT PEX_CALLBACK CallBack)
147 {
148 PEX_FAST_REF FastRef = &CallBack->RoutineBlock;
149 EX_FAST_REF Value, NewValue;
150 PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock;
151
152 /* Start reference loop */
153 for (;;)
154 {
155 /* Get the current count */
156 Value = *FastRef;
157 if (Value.RefCnt != 0)
158 {
159 /* Increase the reference count */
160 NewValue.Value = Value.Value - 1;
161 NewValue.Object = InterlockedCompareExchangePointer(&FastRef->Object,
162 NewValue.Object,
163 Value.Object);
164 if (NewValue.Object != Value.Object) continue;
165 }
166
167 /* All done */
168 break;
169 }
170
171 /* Fail if there isn't any object */
172 if (!Value.Value) return NULL;
173
174 /* Check if we don't have a reference */
175 if (!Value.RefCnt)
176 {
177 /* FIXME: Race */
178 CallbackRoutineBlock = NULL;
179 DPRINT1("Unhandled callback race condition\n");
180 KEBUGCHECK(0);
181 }
182 else
183 {
184 /* Get the callback block */
185 CallbackRoutineBlock = (PVOID)(Value.Value &~ MAX_FAST_REFS);
186
187 /* Check if this is the last reference */
188 if (Value.RefCnt == 1)
189 {
190 /* Acquire rundown protection */
191 if (ExfAcquireRundownProtectionEx(&CallbackRoutineBlock->
192 RundownProtect,
193 MAX_FAST_REFS))
194 {
195 /* Sanity check */
196 ASSERT(!(((ULONG_PTR)CallbackRoutineBlock) & MAX_FAST_REFS));
197
198 /* Start reference loop */
199 for (;;)
200 {
201 /* Check if the current count is too high */
202 Value = *FastRef;
203 if (((Value.RefCnt + MAX_FAST_REFS) > MAX_FAST_REFS) ||
204 ((Value.Value &~ MAX_FAST_REFS) !=
205 (ULONG_PTR)CallbackRoutineBlock))
206 {
207 /* Backdown the rundown acquire */
208 ExfReleaseRundownProtectionEx(&CallbackRoutineBlock->
209 RundownProtect,
210 MAX_FAST_REFS);
211 break;
212 }
213
214 /* Increase the reference count */
215 NewValue.Value = Value.Value + MAX_FAST_REFS;
216 NewValue.Object =
217 InterlockedCompareExchangePointer(&FastRef->Object,
218 NewValue.Object,
219 Value.Object);
220 if (NewValue.Object != Value.Object) continue;
221
222 /* Break out if the change was OK */
223 break;
224 }
225 }
226 }
227 }
228
229 /* Return the callback block */
230 return CallbackRoutineBlock;
231 }
232
233 BOOLEAN
234 NTAPI
235 ExCompareExchangeCallBack(IN OUT PEX_CALLBACK CallBack,
236 IN PEX_CALLBACK_ROUTINE_BLOCK NewBlock,
237 IN PEX_CALLBACK_ROUTINE_BLOCK OldBlock)
238 {
239 EX_FAST_REF Value, NewValue;
240 PEX_CALLBACK_ROUTINE_BLOCK CallbackRoutineBlock;
241 PEX_FAST_REF FastRef = &CallBack->RoutineBlock;
242
243 /* Check that we have a new block */
244 if (NewBlock)
245 {
246 /* Acquire rundown */
247 if (!ExfAcquireRundownProtectionEx(&NewBlock->RundownProtect,
248 MAX_FAST_REFS + 1))
249 {
250 /* This should never happen */
251 ASSERTMSG("Callback block is already undergoing rundown", FALSE);
252 return FALSE;
253 }
254 }
255
256 /* Sanity check and start swap loop */
257 ASSERT(!(((ULONG_PTR)NewBlock) & MAX_FAST_REFS));
258 for (;;)
259 {
260 /* Get the current value */
261 Value = *FastRef;
262
263 /* Make sure there's enough references to swap */
264 if (!((Value.Value ^ (ULONG_PTR)OldBlock) <= MAX_FAST_REFS)) break;
265
266 /* Check if we have an object to swap */
267 if (NewBlock)
268 {
269 /* Set up the value with maximum fast references */
270 NewValue.Value = (ULONG_PTR)NewBlock | MAX_FAST_REFS;
271 }
272 else
273 {
274 /* Write the object address itself (which is empty) */
275 NewValue.Value = (ULONG_PTR)NewBlock;
276 }
277
278 /* Do the actual compare exchange */
279 NewValue.Object = InterlockedCompareExchangePointer(&FastRef->Object,
280 NewValue.Object,
281 Value.Object);
282 if (NewValue.Object != Value.Object) continue;
283
284 /* All done */
285 break;
286 }
287
288 /* Get the routine block */
289 CallbackRoutineBlock = (PVOID)(Value.Value & ~MAX_FAST_REFS);
290
291 /* Make sure the swap worked */
292 if (CallbackRoutineBlock == OldBlock)
293 {
294 /* Make sure we replaced a valid pointer */
295 if (CallbackRoutineBlock)
296 {
297 /* Acquire the flush lock and immediately release it */
298 KeEnterCriticalRegion();
299 ExWaitOnPushLock(&ExpCallBackFlush);
300
301 /* Release rundown protection */
302 KeLeaveCriticalRegion();
303 ExfReleaseRundownProtectionEx(&CallbackRoutineBlock->RundownProtect,
304 Value.RefCnt + 1);
305 }
306
307 /* Compare worked */
308 return TRUE;
309 }
310 else
311 {
312 /* It failed, check if we had a block */
313 if (NewBlock)
314 {
315 /* We did, remove the refernces that we had added */
316 ExfReleaseRundownProtectionEx(&NewBlock->RundownProtect,
317 MAX_FAST_REFS + 1);
318 }
319
320 /* Return failure */
321 return FALSE;
322 }
323 }
324
325 VOID
326 NTAPI
327 ExpDeleteCallback(IN PVOID Object)
328 {
329 /* Sanity check */
330 ASSERT(IsListEmpty(&((PCALLBACK_OBJECT)Object)->RegisteredCallbacks));
331 }
332
333 /*++
334 * @name ExpInitializeCallbacks
335 *
336 * Creates the Callback Object as a valid Object Type in the Kernel.
337 * Internal function, subject to further review
338 *
339 * @return TRUE if the Callback Object Type was successfully created.
340 *
341 * @remarks None
342 *
343 *--*/
344 BOOLEAN
345 INIT_FUNCTION
346 NTAPI
347 ExpInitializeCallbacks(VOID)
348 {
349 OBJECT_ATTRIBUTES ObjectAttributes;
350 NTSTATUS Status;
351 UNICODE_STRING DirName = RTL_CONSTANT_STRING(L"\\Callback");
352 UNICODE_STRING CallbackName;
353 UNICODE_STRING Name;
354 OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
355 HANDLE DirectoryHandle;
356 ULONG i;
357
358 /* Setup lightweight callback lock */
359 ExpCallBackFlush.Value = 0;
360
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,
372 NULL,
373 &ExCallbackObjectType);
374 if (!NT_SUCCESS(Status)) return FALSE;
375
376 /* Initialize the Object */
377 InitializeObjectAttributes(&ObjectAttributes,
378 &DirName,
379 OBJ_CASE_INSENSITIVE | OBJ_PERMANENT,
380 NULL,
381 SePublicDefaultSd);
382
383 /* Create the Object Directory */
384 Status = NtCreateDirectoryObject(&DirectoryHandle,
385 DIRECTORY_ALL_ACCESS,
386 &ObjectAttributes);
387 if (!NT_SUCCESS(Status)) return FALSE;
388
389 /* Close Handle... */
390 NtClose(DirectoryHandle);
391
392 /* Initialize Event used when unregistering */
393 KeInitializeEvent(&ExpCallbackEvent, NotificationEvent, 0);
394
395 /* Default NT Kernel Callbacks. */
396 for (i = 0; ExpInitializeCallback[i].CallbackObject; i++)
397 {
398 /* Create the name from the structure */
399 RtlInitUnicodeString(&CallbackName, ExpInitializeCallback[i].Name);
400
401 /* Initialize the Object Attributes Structure */
402 InitializeObjectAttributes(&ObjectAttributes,
403 &CallbackName,
404 OBJ_PERMANENT | OBJ_CASE_INSENSITIVE,
405 NULL,
406 NULL);
407
408 /* Create the Callback Object */
409 Status = ExCreateCallback(ExpInitializeCallback[i].CallbackObject,
410 &ObjectAttributes,
411 TRUE,
412 TRUE);
413 if (!NT_SUCCESS(Status)) return FALSE;
414 }
415
416 /* Everything successful */
417 return TRUE;
418 }
419
420 /* PUBLIC FUNCTIONS **********************************************************/
421
422 /*++
423 * @name ExCreateCallback
424 * @implemented
425 *
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
431 *
432 * @param CallbackObject
433 * Pointer that will receive the Callback Object.
434 *
435 * @param CallbackName
436 * Name of Callback
437 *
438 * @param Create
439 * Determines if the object will be created if it doesn't exit
440 *
441 * @param AllowMultipleCallbacks
442 * Determines if more then one registered callback function
443 * can be attached to this Callback Object.
444 *
445 * @return STATUS_SUCESS if not failed.
446 *
447 * @remarks Must be called at IRQL = PASSIVE_LEVEL
448 *
449 *--*/
450 NTSTATUS
451 NTAPI
452 ExCreateCallback(OUT PCALLBACK_OBJECT *CallbackObject,
453 IN POBJECT_ATTRIBUTES ObjectAttributes,
454 IN BOOLEAN Create,
455 IN BOOLEAN AllowMultipleCallbacks)
456 {
457 PCALLBACK_OBJECT Callback = NULL;
458 NTSTATUS Status;
459 HANDLE Handle = NULL;
460 PAGED_CODE();
461
462 /* Open a handle to the callback if it exists */
463 if (ObjectAttributes->ObjectName)
464 {
465 /* Open the handle */
466 Status = ObOpenObjectByName(ObjectAttributes,
467 ExCallbackObjectType,
468 KernelMode,
469 NULL,
470 0,
471 NULL,
472 &Handle);
473 }
474 else
475 {
476 /* Otherwise, fail */
477 Status = STATUS_UNSUCCESSFUL;
478 }
479
480 /* We weren't able to open it...should we create it? */
481 if (!(NT_SUCCESS(Status)) && (Create))
482 {
483 /* Create the object */
484 Status = ObCreateObject(KernelMode,
485 ExCallbackObjectType,
486 ObjectAttributes,
487 KernelMode,
488 NULL,
489 sizeof(CALLBACK_OBJECT),
490 0,
491 0,
492 (PVOID *)&Callback);
493 if (NT_SUCCESS(Status))
494 {
495 /* Set it up */
496 Callback->Signature = TAG('C', 'a', 'l', 'l');
497 KeInitializeSpinLock(&Callback->Lock);
498 InitializeListHead(&Callback->RegisteredCallbacks);
499 Callback->AllowMultipleCallbacks = AllowMultipleCallbacks;
500
501 /* Insert the object into the object namespace */
502 Status = ObInsertObject(Callback,
503 NULL,
504 FILE_READ_DATA,
505 0,
506 NULL,
507 &Handle);
508 }
509 }
510
511 /* Check if we have success until here */
512 if (NT_SUCCESS(Status))
513 {
514 /* Get a pointer to the new object from the handle we just got */
515 Status = ObReferenceObjectByHandle(Handle,
516 0,
517 ExCallbackObjectType,
518 KernelMode,
519 (PVOID)&Callback,
520 NULL);
521
522 /* Close the Handle, since we now have the pointer */
523 ZwClose(Handle);
524 }
525
526 /* Everything went fine, so return a pointer to the Object */
527 if (NT_SUCCESS(Status)) *CallbackObject = Callback;
528 return Status;
529 }
530
531 /*++
532 * @name ExNotifyCallback
533 * @implemented
534 *
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
538 *
539 * @param CallbackObject
540 * Which callback to call
541 *
542 * @param Argument1
543 * Pointer/data to send to callback function
544 *
545 * @param Argument2
546 * Pointer/data to send to callback function
547 *
548 * @return None
549 *
550 * @remarks None
551 *
552 *--*/
553 VOID
554 NTAPI
555 ExNotifyCallback(IN PCALLBACK_OBJECT CallbackObject,
556 IN PVOID Argument1,
557 IN PVOID Argument2)
558 {
559 PLIST_ENTRY RegisteredCallbacks;
560 PCALLBACK_REGISTRATION CallbackRegistration;
561 KIRQL OldIrql;
562
563 /* Check if we don't have an object or registrations */
564 if (!(CallbackObject) ||
565 (IsListEmpty(&CallbackObject->RegisteredCallbacks)))
566 {
567 /* Don't notify */
568 return;
569 }
570
571 /* Acquire the Lock */
572 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
573
574 /* Enumerate through all the registered functions */
575 for (RegisteredCallbacks = CallbackObject->RegisteredCallbacks.Flink;
576 RegisteredCallbacks != &CallbackObject->RegisteredCallbacks;
577 RegisteredCallbacks = RegisteredCallbacks->Flink)
578 {
579 /* Get a pointer to a Callback Registration from the List Entries */
580 CallbackRegistration = CONTAINING_RECORD(RegisteredCallbacks,
581 CALLBACK_REGISTRATION,
582 Link);
583
584 /* Don't bother doing notification if it's pending to be deleted */
585 if (!CallbackRegistration->UnregisterWaiting)
586 {
587 /* Mark the Callback in use, so it won't get deleted */
588 CallbackRegistration->Busy += 1;
589
590 /* Release the Spinlock before making the call */
591 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
592
593 /* Call the Registered Function */
594 CallbackRegistration->CallbackFunction(CallbackRegistration->
595 CallbackContext,
596 Argument1,
597 Argument2);
598
599 /* Get SpinLock back */
600 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
601
602 /* We are not in use anymore */
603 CallbackRegistration->Busy -= 1;
604
605 /* Check if removal is pending and we're not active */
606 if ((CallbackRegistration->UnregisterWaiting) &&
607 !(CallbackRegistration->Busy))
608 {
609 /* Signal the callback event */
610 KeSetEvent(&ExpCallbackEvent, 0, FALSE);
611 }
612 }
613 }
614
615 /* Release the Callback Object */
616 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
617 }
618
619 /*++
620 * @name ExRegisterCallback
621 * @implemented
622 *
623 * Allows a function to associate a callback pointer (Function) to
624 * a created Callback object
625 * See: DDK, OSR, links in ExNotifyCallback
626 *
627 * @param CallbackObject
628 * The Object Created with ExCreateCallBack
629 *
630 * @param CallBackFunction
631 * Pointer to the function to be called back
632 *
633 * @param CallBackContext
634 * Block of memory that can contain user-data which will be
635 * passed on to the callback
636 *
637 * @return A handle to a Callback Registration Structure (MSDN Documentation)
638 *
639 * @remarks None
640 *
641 *--*/
642 PVOID
643 NTAPI
644 ExRegisterCallback(IN PCALLBACK_OBJECT CallbackObject,
645 IN PCALLBACK_FUNCTION CallbackFunction,
646 IN PVOID CallbackContext)
647 {
648 PCALLBACK_REGISTRATION CallbackRegistration = NULL;
649 KIRQL OldIrql;
650
651 /* Sanity checks */
652 ASSERT(CallbackFunction);
653 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
654
655 /* Create reference to Callback Object */
656 ObReferenceObject(CallbackObject);
657
658 /* Allocate memory for the structure */
659 CallbackRegistration = ExAllocatePoolWithTag(NonPagedPool,
660 sizeof(CALLBACK_REGISTRATION),
661 CALLBACK_TAG);
662 if (!CallbackRegistration)
663 {
664 /* Dereference and fail */
665 ObDereferenceObject (CallbackObject);
666 return NULL;
667 }
668
669 /* Create Callback Registration */
670 CallbackRegistration->CallbackObject = CallbackObject;
671 CallbackRegistration->CallbackFunction = CallbackFunction;
672 CallbackRegistration->CallbackContext = CallbackContext;
673 CallbackRegistration->Busy = 0;
674 CallbackRegistration->UnregisterWaiting = FALSE;
675
676 /* Acquire SpinLock */
677 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
678
679 /* Check if 1) No Callbacks registered or 2) Multiple Callbacks allowed */
680 if ((CallbackObject->AllowMultipleCallbacks) ||
681 (IsListEmpty(&CallbackObject->RegisteredCallbacks)))
682 {
683 /* Register the callback */
684 InsertTailList(&CallbackObject->RegisteredCallbacks,
685 &CallbackRegistration->Link);
686
687 /* Release SpinLock */
688 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
689 }
690 else
691 {
692 /* Release SpinLock */
693 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
694
695 /* Free the registration */
696 ExFreePool(CallbackRegistration);
697 CallbackRegistration = NULL;
698
699 /* Dereference the object */
700 ObDereferenceObject(CallbackObject);
701 }
702
703 /* Return handle to Registration Object */
704 return (PVOID)CallbackRegistration;
705 }
706
707 /*++
708 * @name ExUnregisterCallback
709 * @implemented
710 *
711 * Deregisters a CallBack
712 * See: DDK, OSR, links in ExNotifyCallback
713 *
714 * @param CallbackRegistration
715 * Callback Registration Handle
716 *
717 * @return None
718 *
719 * @remarks None
720 *
721 *--*/
722 VOID
723 NTAPI
724 ExUnregisterCallback(IN PVOID CallbackRegistrationHandle)
725 {
726 PCALLBACK_REGISTRATION CallbackRegistration;
727 PCALLBACK_OBJECT CallbackObject;
728 KIRQL OldIrql;
729 ASSERT_IRQL_LESS_OR_EQUAL(DISPATCH_LEVEL);
730
731 /* Convert Handle to valid Structure Pointer */
732 CallbackRegistration = (PCALLBACK_REGISTRATION)CallbackRegistrationHandle;
733
734 /* Get the Callback Object */
735 CallbackObject = CallbackRegistration->CallbackObject;
736
737 /* Lock the Object */
738 KeAcquireSpinLock (&CallbackObject->Lock, &OldIrql);
739
740 /* We can't Delete the Callback if it's in use */
741 while (CallbackRegistration->Busy)
742 {
743 /* Let everyone else know we're unregistering */
744 CallbackRegistration->UnregisterWaiting = TRUE;
745
746 /* We are going to wait for the event, so the Lock isn't necessary */
747 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
748
749 /* Make sure the event is cleared */
750 KeClearEvent(&ExpCallbackEvent);
751
752 /* Wait for the Event */
753 KeWaitForSingleObject(&ExpCallbackEvent,
754 Executive,
755 KernelMode,
756 FALSE,
757 NULL);
758
759 /* We need the Lock again */
760 KeAcquireSpinLock(&CallbackObject->Lock, &OldIrql);
761 }
762
763 /* Remove the Callback */
764 RemoveEntryList(&CallbackRegistration->Link);
765
766 /* It's now safe to release the lock */
767 KeReleaseSpinLock(&CallbackObject->Lock, OldIrql);
768
769 /* Delete this registration */
770 ExFreePool(CallbackRegistration);
771
772 /* Remove the reference */
773 ObDereferenceObject(CallbackObject);
774 }
775
776 /* EOF */