2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Kernel
4 * FILE: ntoskrnl/ex/resource.c
5 * PURPOSE: ERESOURCE Implementation
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
10 * This implementation is the Windows NT 5.x one.
11 * NT 6.0 beta has optimized the OwnerThread entry array
12 * and the internals of ExpFindEntryForThread and ExpFindFreeEntry
13 * need to be modified accordingly in order to support the WDK.
14 * These changes will not be made here until NT 6.0 reaches RTM status since
15 * there is a possibility that they will be removed; as such, do NOT
16 * update ERESOURCE/relevant code to the WDK definition.
19 /* INCLUDES *****************************************************************/
23 #include <internal/debug.h>
25 #if defined (ALLOC_PRAGMA)
26 #pragma alloc_text(INIT, ExpResourceInitialization)
29 /* Macros for reading resource flags */
30 #define IsExclusiveWaiting(r) (r->NumberOfExclusiveWaiters)
31 #define IsSharedWaiting(r) (r->NumberOfSharedWaiters)
32 #define IsOwnedExclusive(r) (r->Flag & ResourceOwnedExclusive)
34 /* DATA***********************************************************************/
36 LARGE_INTEGER ExpTimeout
;
37 ULONG ExpResourceTimeoutCount
= 90 * 3600 / 2;
38 KSPIN_LOCK ExpResourceSpinLock
;
39 LIST_ENTRY ExpSystemResourcesList
;
40 BOOLEAN ExResourceStrict
= FALSE
; /* FIXME */
42 /* PRIVATE FUNCTIONS *********************************************************/
46 * @name ExpVerifyResource
48 * The ExpVerifyResource routine verifies the correctness of an ERESOURCE
51 * Pointer to the resource being verified.
55 * @remarks Only present on DBG builds.
60 ExpVerifyResource(IN PERESOURCE Resource
)
62 /* Verify the resource data */
63 ASSERT((((ULONG_PTR
)Resource
) & (sizeof(ULONG_PTR
) - 1)) == 0);
64 ASSERT(!Resource
->SharedWaiters
||
65 Resource
->SharedWaiters
->Header
.Type
== SemaphoreObject
);
66 ASSERT(!Resource
->SharedWaiters
||
67 Resource
->SharedWaiters
->Header
.Size
== (sizeof(KSEMAPHORE
) / sizeof(ULONG
)));
68 ASSERT(!Resource
->ExclusiveWaiters
||
69 Resource
->ExclusiveWaiters
->Header
.Type
== SynchronizationEvent
);
70 ASSERT(!Resource
->ExclusiveWaiters
||
71 Resource
->ExclusiveWaiters
->Header
.Size
== (sizeof(KEVENT
) / sizeof(ULONG
)));
75 * @name ExpCheckForApcsDisabled
77 * The ExpCheckForApcsDisabled routine checks if Kernel APCs are still
78 * enabled when they should be disabled, and optionally breakpoints.
81 * Specifies if we should break if an invalid APC State is detected.
84 * Pointer to the resource being checked.
87 * Pointer to the thread being checked.
91 * @remarks Only present on DBG builds. Depends on ExResourceStrict value.
96 ExpCheckForApcsDisabled(IN BOOLEAN BreakIfTrue
,
97 IN PERESOURCE Resource
,
100 /* Check if we should care and check if we should break */
101 if ((ExResourceStrict
) &&
103 !(Thread
->SystemThread
) &&
104 !(Thread
->Tcb
.CombinedApcDisable
))
107 DPRINT1("EX: resource: APCs still enabled before resource %p acquire "
113 #define ExpVerifyResource(r)
114 #define ExpCheckForApcsDisabled(b,r,t)
118 * @name ExpResourceInitialization
120 * The ExpResourceInitialization routine initializes resources for use.
126 * @remarks This routine should only be called once, during system startup.
132 ExpResourceInitialization(VOID
)
134 /* Setup the timeout */
135 ExpTimeout
.QuadPart
= Int32x32To64(4, -10000000);
136 InitializeListHead(&ExpSystemResourcesList
);
137 KeInitializeSpinLock(&ExpResourceSpinLock
);
141 * @name ExpAllocateExclusiveWaiterEvent
143 * The ExpAllocateExclusiveWaiterEvent routine creates the event that will
144 * be used by exclusive waiters on the resource.
147 * Pointer to the resource.
150 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
154 * @remarks The pointer to the event must be atomically set.
159 ExpAllocateExclusiveWaiterEvent(IN PERESOURCE Resource
,
164 /* Release the lock */
165 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
167 /* Allocate the event */
168 Event
= ExAllocatePoolWithTag(NonPagedPool
,
173 KeInitializeEvent(Event
, SynchronizationEvent
, FALSE
);
176 if (InterlockedCompareExchangePointer(&Resource
->ExclusiveWaiters
,
180 /* Someone already set it, free our event */
181 DPRINT1("WARNING: Handling race condition\n");
185 /* Re-acquire the lock */
186 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
190 * @name ExpAllocateSharedWaiterSemaphore
192 * The ExpAllocateSharedWaiterSemaphore routine creates the semaphore that
193 * will be used by shared waiters on the resource.
196 * Pointer to the resource.
199 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
203 * @remarks The pointer to the semaphore must be atomically set.
208 ExpAllocateSharedWaiterSemaphore(IN PERESOURCE Resource
,
211 PKSEMAPHORE Semaphore
;
213 /* Release the lock */
214 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
216 /* Allocate the semaphore */
217 Semaphore
= ExAllocatePoolWithTag(NonPagedPool
,
219 TAG_RESOURCE_SEMAPHORE
);
222 KeInitializeSemaphore(Semaphore
, 0, MAXLONG
);
225 if (InterlockedCompareExchangePointer(&Resource
->SharedWaiters
,
229 /* Someone already set it, free our semaphore */
230 DPRINT1("WARNING: Handling race condition\n");
231 ExFreePool(Semaphore
);
234 /* Re-acquire the lock */
235 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
239 * @name ExpExpandResourceOwnerTable
241 * The ExpExpandResourceOwnerTable routine expands the owner table of the
242 * specified resource.
245 * Pointer to the resource.
248 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
257 ExpExpandResourceOwnerTable(IN PERESOURCE Resource
,
260 POWNER_ENTRY Owner
, Table
;
261 ULONG NewSize
, OldSize
;
262 DPRINT("ExpExpandResourceOwnerTable: %p\n", Resource
);
264 /* Get the owner table */
265 Owner
= Resource
->OwnerTable
;
268 /* Start with the default size of 3 */
274 /* Add 4 more entries */
275 OldSize
= Owner
->TableSize
;
276 NewSize
= OldSize
+ 4;
279 /* Release the lock */
280 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
282 /* Allocate memory for the table */
283 Table
= ExAllocatePoolWithTag(NonPagedPool
,
284 NewSize
* sizeof(OWNER_ENTRY
),
288 RtlZeroMemory((PVOID
)(Table
+ OldSize
),
289 (NewSize
- OldSize
) * sizeof(OWNER_ENTRY
));
291 /* Lock the resource */
292 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
294 /* Make sure nothing has changed */
295 if ((Owner
!= Resource
->OwnerTable
) ||
296 ((Owner
) && (OldSize
!= Resource
->OwnerTable
->TableSize
)))
298 /* Resource changed while we weren't holding the lock; bail out */
299 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
305 RtlCopyMemory((PVOID
)Table
,
307 OldSize
* sizeof(OWNER_ENTRY
));
309 /* Acquire dispatcher lock to prevent thread boosting */
310 KeAcquireDispatcherDatabaseLockAtDpcLevel();
312 /* Set the new table data */
313 Table
->TableSize
= NewSize
;
314 Resource
->OwnerTable
= Table
;
317 ExpVerifyResource(Resource
);
320 KeReleaseDispatcherDatabaseLockFromDpcLevel();
321 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
323 /* Free the old table */
324 if (Owner
) ExFreePool(Owner
);
326 /* Set the resource index */
327 if (!OldSize
) OldSize
= 1;
330 /* Set the resource index */
331 KeGetCurrentThread()->ResourceIndex
= (UCHAR
)OldSize
;
333 /* Lock the resource again */
334 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
338 * @name ExpFindFreeEntry
340 * The ExpFindFreeEntry routine locates an empty owner entry in the
341 * specified resource. If none was found, then the owner table is
345 * Pointer to the resource.
348 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
350 * @return Pointer to an empty OWNER_ENTRY structure.
357 ExpFindFreeEntry(IN PERESOURCE Resource
,
360 POWNER_ENTRY Owner
, Limit
;
362 POWNER_ENTRY FreeEntry
= NULL
;
363 DPRINT("ExpFindFreeEntry: %p\n", Resource
);
366 ASSERT(OldIrql
!= 0);
367 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
!= 0);
369 /* Check if the next built-in entry is free */
370 if (!Resource
->OwnerThreads
[1].OwnerThread
)
373 FreeEntry
= &Resource
->OwnerThreads
[1];
377 /* Get the current table pointer */
378 Owner
= Resource
->OwnerTable
;
381 /* Loop every entry */
382 Size
= Owner
->TableSize
;
383 Limit
= &Owner
[Size
];
385 /* Go to the next entry and loop */
389 /* Check for a free entry */
390 if (!Owner
->OwnerThread
)
392 /* Found one, return it!*/
399 } while (Owner
!= Limit
);
401 /* If we found a free entry by now, return it */
404 /* Set the resource index */
405 KeGetCurrentThread()->ResourceIndex
=
406 (UCHAR
)(Owner
- Resource
->OwnerTable
);
411 /* No free entry, expand the table */
412 ExpExpandResourceOwnerTable(Resource
, OldIrql
);
416 /* Return the entry found */
421 * @name ExpFindEntryForThread
423 * The ExpFindEntryForThread routine locates the owner entry associated with
424 * the specified thread in the given resource. If none was found, then the
425 * owner table is expanded.
428 * Pointer to the resource.
431 * Pointer to the thread to find.
434 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
436 * @return Pointer to an empty OWNER_ENTRY structure.
443 ExpFindEntryForThread(IN PERESOURCE Resource
,
444 IN ERESOURCE_THREAD Thread
,
447 POWNER_ENTRY FreeEntry
, Owner
, Limit
;
449 DPRINT("ExpFindEntryForThread: %p\n", Resource
);
451 /* Start by looking in the static array */
452 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
454 /* Found it, return it! */
455 return &Resource
->OwnerThreads
[0];
457 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
460 return &Resource
->OwnerThreads
[1];
464 /* Check if the first array is empty for our use */
466 if (!Resource
->OwnerThreads
[1].OwnerThread
)
468 /* Use this as the first free entry */
469 FreeEntry
= &Resource
->OwnerThreads
[1];
472 /* Get the current table pointer */
473 Owner
= Resource
->OwnerTable
;
476 /* The current table is empty, so no size */
481 /* We have a table, get it's size and limit */
482 Size
= Owner
->TableSize
;
483 Limit
= &Owner
[Size
];
485 /* Go to the next entry and loop */
489 /* Check for a match */
490 if (Owner
->OwnerThread
== Thread
)
492 /* Match found! Set the resource index */
493 KeGetCurrentThread()->ResourceIndex
=
494 (UCHAR
)(Owner
- Resource
->OwnerTable
);
498 /* If we don't have a free entry yet, make this one free */
499 if (!(FreeEntry
) && !(Owner
->OwnerThread
)) FreeEntry
= Owner
;
503 } while (Owner
!= Limit
);
507 /* Check if it's OK to do an expansion */
508 if (!OldIrql
) return NULL
;
510 /* If we found a free entry by now, return it */
513 /* Set the resource index */
514 KeGetCurrentThread()->ResourceIndex
= (UCHAR
)
515 (Owner
- Resource
->OwnerTable
);
519 /* No free entry, expand the table */
520 ExpExpandResourceOwnerTable(Resource
, OldIrql
);
525 * @name ExpBoostOwnerThread
527 * The ExpBoostOwnerThread routine increases the priority of a waiting
528 * thread in an attempt to fight a possible deadlock.
531 * Pointer to the current thread.
534 * Pointer to thread that owns the resource.
544 * Disabled, read the comments bellow.
548 ExpBoostOwnerThread(IN PKTHREAD Thread
,
549 IN PKTHREAD OwnerThread
)
551 BOOLEAN Released
= FALSE
;
552 DPRINT("ExpBoostOwnerThread: %p\n", Thread
);
554 /* Make sure the owner thread is a pointer, not an ID */
555 if (!((ULONG_PTR
)OwnerThread
& 0x3))
557 /* Check if we can actually boost it */
558 if ((OwnerThread
->Priority
< Thread
->Priority
) &&
559 (OwnerThread
->Priority
< 14))
561 /* Make sure we're at dispatch */
562 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
564 /* Set the new priority */
565 OwnerThread
->PriorityDecrement
+= 14 - OwnerThread
->Priority
;
568 OwnerThread
->Quantum
= OwnerThread
->QuantumReset
;
570 /* Update the kernel state */
571 KiSetPriorityThread(OwnerThread
, 14, &Released
);
573 /* Reacquire lock if it got releases */
574 if (Released
) KeAcquireDispatcherDatabaseLockFromDpcLevel();
576 /* Make sure we're still at dispatch */
577 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
584 * @name ExpWaitForResource
586 * The ExpWaitForResource routine performs a wait on the specified resource.
589 * Pointer to the resource to wait on.
592 * Pointer to object (exclusive event or shared semaphore) to wait on.
601 ExpWaitForResource(IN PERESOURCE Resource
,
612 LARGE_INTEGER Timeout
;
614 /* Increase contention count and use a 5 second timeout */
615 Resource
->ContentionCount
++;
616 Timeout
.QuadPart
= 500 * -10000;
619 /* Wait for ownership */
620 Status
= KeWaitForSingleObject(Object
,
625 if (Status
!= STATUS_TIMEOUT
) break;
627 /* Increase wait count */
629 Timeout
= ExpTimeout
;
631 /* Check if we've exceeded the limit */
632 if (WaitCount
> ExpResourceTimeoutCount
)
634 /* Reset wait count */
637 /* Lock the resource */
638 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
640 /* Dump debug information */
641 DPRINT1("Resource @ %lx\n", Resource
);
642 DPRINT1(" ActiveCount = %04lx Flags = %s%s%s\n",
643 Resource
->ActiveCount
,
644 IsOwnedExclusive(Resource
) ? "IsOwnedExclusive " : "",
645 IsSharedWaiting(Resource
) ? "SharedWaiter " : "",
646 IsExclusiveWaiting(Resource
) ? "ExclusiveWaiter " : "");
647 DPRINT1(" NumberOfExclusiveWaiters = %04lx\n",
648 Resource
->NumberOfExclusiveWaiters
);
649 DPRINT1(" Thread = %08lx, Count = %02x\n",
650 Resource
->OwnerThreads
[0].OwnerThread
,
651 Resource
->OwnerThreads
[0].OwnerCount
);
652 DPRINT1(" Thread = %08lx, Count = %02x\n",
653 Resource
->OwnerThreads
[1].OwnerThread
,
654 Resource
->OwnerThreads
[1].OwnerCount
);
656 /* Dump out the table too */
657 Owner
= Resource
->OwnerTable
;
660 /* Loop every entry */
661 Size
= Owner
->TableSize
;
662 for (i
= 1; i
< Size
; i
++)
666 DPRINT1(" Thread = %08lx, Count = %02x\n",
674 DPRINT1("EX - Rewaiting\n");
675 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
681 * - We cannot access the OwnerTable without locking the resource.
682 * - The shared waiters may wait also on the semaphore. It makes no sense to boost a waiting thread.
683 * - The thread header is initialized like KeWaitForSingleObject (?, ?, ?, TRUE, ?).
684 * During the boost, possible the dispatcher lock is released but the thread block (WaitNext) isn't changed.
687 /* Check if we can boost */
688 if (!(Resource
->Flag
& ResourceHasDisabledPriorityBoost
))
690 PKTHREAD Thread
, OwnerThread
;
692 /* Get the current kernel thread and lock the dispatcher */
693 Thread
= KeGetCurrentThread();
694 Thread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
695 Thread
->WaitNext
= TRUE
;
697 /* Get the owner thread and boost it */
698 OwnerThread
= (PKTHREAD
)Resource
->OwnerThreads
[0].OwnerThread
;
699 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
701 /* If it's a shared resource */
702 if (!IsOwnedExclusive(Resource
))
704 /* Boost the other owner thread too */
705 OwnerThread
= (PKTHREAD
)Resource
->OwnerThreads
[1].OwnerThread
;
706 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
709 Owner
= Resource
->OwnerTable
;
712 /* Loop every entry */
713 Size
= Owner
->TableSize
;
714 for (i
= 1; i
< Size
; i
++)
716 /* Move to next entry */
720 OwnerThread
= (PKTHREAD
)Owner
->OwnerThread
;
723 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
732 /* FUNCTIONS *****************************************************************/
735 * @name ExAcquireResourceExclusiveLite
738 * The ExAcquireResourceExclusiveLite routine acquires the given resource
739 * for exclusive access by the calling thread.
742 * Pointer to the resource to acquire.
745 * Specifies the routine's behavior whenever the resource cannot be
746 * acquired immediately.
748 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
749 * and exclusive access cannot be granted immediately.
751 * @remarks The caller can release the resource by calling either
752 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
754 * Normal kernel APC delivery must be disabled before calling this
755 * routine. Disable normal kernel APC delivery by calling
756 * KeEnterCriticalRegion. Delivery must remain disabled until the
757 * resource is released, at which point it can be reenabled by calling
758 * KeLeaveCriticalRegion.
760 * For better performance, call ExTryToAcquireResourceExclusiveLite,
761 * rather than calling ExAcquireResourceExclusiveLite with Wait set
764 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
770 ExAcquireResourceExclusiveLite(PERESOURCE Resource
,
773 KIRQL OldIrql
= PASSIVE_LEVEL
;
774 ERESOURCE_THREAD Thread
;
778 ASSERT((Resource
->Flag
& ResourceNeverExclusive
) == 0);
781 Thread
= ExGetCurrentResourceThread();
783 /* Sanity check and validation */
784 ASSERT(KeIsExecutingDpc() == FALSE
);
785 ExpVerifyResource(Resource
);
787 /* Acquire the lock */
788 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
789 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
791 /* Check if there is a shared owner or exclusive owner */
793 DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n",
795 if (Resource
->ActiveCount
)
797 /* Check if it's exclusively owned, and we own it */
798 if ((IsOwnedExclusive(Resource
)) &&
799 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
801 /* Increase the owning count */
802 Resource
->OwnerThreads
[0].OwnerCount
++;
808 * If the caller doesn't want us to wait, we can't acquire the
809 * resource because someone else then us owns it. If we can wait,
818 /* Check if it has exclusive waiters */
819 if (!Resource
->ExclusiveWaiters
)
821 /* It doesn't, allocate the event and try acquiring again */
822 ExpAllocateExclusiveWaiterEvent(Resource
, &OldIrql
);
827 /* Has exclusive waiters, wait on it */
828 Resource
->NumberOfExclusiveWaiters
++;
829 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
830 ExpWaitForResource(Resource
, Resource
->ExclusiveWaiters
);
832 /* Set owner and return success */
833 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
841 /* Nobody owns it, so let's! */
842 Resource
->Flag
|= ResourceOwnedExclusive
;
843 Resource
->ActiveCount
= 1;
844 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
845 Resource
->OwnerThreads
[0].OwnerCount
= 1;
849 /* Release the lock and return */
850 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
855 * @name ExAcquireResourceSharedLite
858 * The ExAcquireResourceSharedLite routine acquires the given resource
859 * for shared access by the calling thread.
862 * Pointer to the resource to acquire.
865 * Specifies the routine's behavior whenever the resource cannot be
866 * acquired immediately.
868 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
869 * and exclusive access cannot be granted immediately.
871 * @remarks The caller can release the resource by calling either
872 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
874 * Normal kernel APC delivery must be disabled before calling this
875 * routine. Disable normal kernel APC delivery by calling
876 * KeEnterCriticalRegion. Delivery must remain disabled until the
877 * resource is released, at which point it can be reenabled by calling
878 * KeLeaveCriticalRegion.
880 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
886 ExAcquireResourceSharedLite(PERESOURCE Resource
,
890 ERESOURCE_THREAD Thread
;
894 Thread
= ExGetCurrentResourceThread();
896 /* Sanity check and validation */
897 ASSERT(KeIsExecutingDpc() == FALSE
);
898 ExpVerifyResource(Resource
);
900 /* Acquire the lock */
901 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
902 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
904 /* See if nobody owns us */
906 DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n",
908 if (!Resource
->ActiveCount
)
910 if (Resource
->NumberOfSharedWaiters
== 0)
912 Owner
= &Resource
->OwnerThreads
[1];
916 /* Find a free entry */
917 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
918 if (!Owner
) goto TryAcquire
;
921 Owner
->OwnerThread
= Thread
;
922 Owner
->OwnerCount
= 1;
923 Resource
->ActiveCount
= 1;
925 /* Release the lock and return */
926 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
930 /* Check if it's exclusively owned */
931 if (IsOwnedExclusive(Resource
))
933 /* Check if we own it */
934 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
936 /* Increase the owning count */
937 Resource
->OwnerThreads
[0].OwnerCount
++;
939 /* Release the lock and return */
940 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
944 /* Find a free entry */
945 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
946 if (!Owner
) goto TryAcquire
;
950 /* Resource is shared, find who owns it */
951 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
952 if (!Owner
) goto TryAcquire
;
955 if (Owner
->OwnerThread
== Thread
)
957 /* Increase acquire count and return */
959 ASSERT(Owner
->OwnerCount
!= 0);
961 /* Release the lock and return */
962 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
966 /* Try to find if there are exclusive waiters */
967 if (!IsExclusiveWaiting(Resource
))
969 /* There are none, so acquire it */
970 Owner
->OwnerThread
= Thread
;
971 Owner
->OwnerCount
= 1;
972 Resource
->ActiveCount
++;
974 /* Release the lock and return */
975 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
980 /* If we got here, then we need to wait. Are we allowed? */
983 /* Release the lock and return */
984 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
988 /* Check if we have a shared waiters semaphore */
989 if (!Resource
->SharedWaiters
)
991 /* Allocate it and try another acquire */
992 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
996 /* Now wait for the resource */
997 Owner
->OwnerThread
= Thread
;
998 Owner
->OwnerCount
= 1;
999 Resource
->NumberOfSharedWaiters
++;
1001 /* Release the lock and return */
1002 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1003 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1008 * @name ExAcquireSharedStarveExclusive
1011 * The ExAcquireSharedStarveExclusive routine acquires the given resource
1012 * shared access without waiting for any pending attempts to acquire
1013 * exclusive access to the same resource.
1016 * Pointer to the resource to acquire.
1019 * Specifies the routine's behavior whenever the resource cannot be
1020 * acquired immediately.
1022 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1023 * and exclusive access cannot be granted immediately.
1025 * @remarks The caller can release the resource by calling either
1026 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1028 * Normal kernel APC delivery must be disabled before calling this
1029 * routine. Disable normal kernel APC delivery by calling
1030 * KeEnterCriticalRegion. Delivery must remain disabled until the
1031 * resource is released, at which point it can be reenabled by calling
1032 * KeLeaveCriticalRegion.
1034 * Callers of ExAcquireSharedStarveExclusive usually need quick access
1035 * to a shared resource in order to save an exclusive accessor from
1036 * doing redundant work. For example, a file system might call this
1037 * routine to modify a cached resource, such as a BCB pinned in the
1038 * cache, before the Cache Manager can acquire exclusive access to the
1039 * resource and write the cache out to disk.
1041 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1047 ExAcquireSharedStarveExclusive(PERESOURCE Resource
,
1051 ERESOURCE_THREAD Thread
;
1054 /* Get the thread */
1055 Thread
= ExGetCurrentResourceThread();
1057 /* Sanity check and validation */
1058 ASSERT(KeIsExecutingDpc() == FALSE
);
1059 ExpVerifyResource(Resource
);
1061 /* Acquire the lock */
1062 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1064 /* See if nobody owns us */
1066 DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n",
1068 if (!Resource
->ActiveCount
)
1070 /* Nobody owns it, so let's take control */
1071 Resource
->ActiveCount
= 1;
1072 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
1073 Resource
->OwnerThreads
[1].OwnerCount
= 1;
1075 /* Release the lock and return */
1076 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1080 /* Check if it's exclusively owned */
1081 if (IsOwnedExclusive(Resource
))
1083 /* Check if we own it */
1084 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1086 /* Increase the owning count */
1087 Resource
->OwnerThreads
[0].OwnerCount
++;
1089 /* Release the lock and return */
1090 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1094 /* Find a free entry */
1095 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
1096 if (!Owner
) goto TryAcquire
;
1098 /* If we got here, then we need to wait. Are we allowed? */
1101 /* Release the lock and return */
1102 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1106 /* Check if we have a shared waiters semaphore */
1107 if (!Resource
->SharedWaiters
)
1109 /* Allocate one and try again */
1110 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1116 /* Resource is shared, find who owns it */
1117 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1118 if (!Owner
) goto TryAcquire
;
1121 if (Owner
->OwnerThread
== Thread
)
1123 /* Increase acquire count and return */
1124 Owner
->OwnerCount
++;
1125 ASSERT(Owner
->OwnerCount
!= 0);
1127 /* Release the lock and return */
1128 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1133 Owner
->OwnerThread
= Thread
;
1134 Owner
->OwnerCount
= 1;
1135 Resource
->ActiveCount
++;
1137 /* Release the lock and return */
1138 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1142 /* If we got here, then we need to wait. Are we allowed? */
1145 /* Release the lock and return */
1146 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1150 /* Now wait for the resource */
1151 Owner
->OwnerThread
= Thread
;
1152 Owner
->OwnerCount
= 1;
1153 Resource
->NumberOfSharedWaiters
++;
1155 /* Release the lock and return */
1156 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1157 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1162 * @name ExAcquireSharedWaitForExclusive
1165 * The ExAcquireSharedWaitForExclusive routine acquires the given resource
1166 * for shared access if shared access can be granted and there are no
1167 * exclusive waiters.
1170 * Pointer to the resource to acquire.
1173 * Specifies the routine's behavior whenever the resource cannot be
1174 * acquired immediately.
1176 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1177 * and exclusive access cannot be granted immediately.
1179 * @remarks The caller can release the resource by calling either
1180 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1182 * Normal kernel APC delivery must be disabled before calling this
1183 * routine. Disable normal kernel APC delivery by calling
1184 * KeEnterCriticalRegion. Delivery must remain disabled until the
1185 * resource is released, at which point it can be reenabled by calling
1186 * KeLeaveCriticalRegion.
1188 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1194 ExAcquireSharedWaitForExclusive(PERESOURCE Resource
,
1198 ERESOURCE_THREAD Thread
;
1201 /* Get the thread */
1202 Thread
= ExGetCurrentResourceThread();
1204 /* Sanity check and validation */
1205 ASSERT(KeIsExecutingDpc() == FALSE
);
1206 ExpVerifyResource(Resource
);
1208 /* Acquire the lock */
1209 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1211 /* See if nobody owns us */
1213 DPRINT("ExAcquireSharedWaitForExclusive(Resource 0x%p, Wait %d)\n",
1215 if (!Resource
->ActiveCount
)
1217 /* Nobody owns it, so let's take control */
1218 Resource
->ActiveCount
= 1;
1219 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
1220 Resource
->OwnerThreads
[1].OwnerCount
= 1;
1222 /* Release the lock and return */
1223 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1227 /* Check if it's exclusively owned */
1228 if (IsOwnedExclusive(Resource
))
1230 /* Check if we own it */
1231 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1233 /* Increase the owning count */
1234 Resource
->OwnerThreads
[0].OwnerCount
++;
1236 /* Release the lock and return */
1237 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1242 /* Find a free entry */
1243 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
1244 if (!Owner
) goto TryAcquire
;
1246 /* If we got here, then we need to wait. Are we allowed? */
1249 /* Release the lock and return */
1250 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1254 /* Check if we have a shared waiters semaphore */
1255 if (!Resource
->SharedWaiters
)
1257 /* Allocate one and try again */
1258 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1262 /* Now take control of the resource */
1263 Owner
->OwnerThread
= Thread
;
1264 Owner
->OwnerCount
= 1;
1265 Resource
->NumberOfSharedWaiters
++;
1267 /* Release the lock and wait for it to be ours */
1268 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1269 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1275 /* Try to find if there are exclusive waiters */
1276 if (IsExclusiveWaiting(Resource
))
1278 /* We have to wait for the exclusive waiter to be done */
1281 /* So bail out if we're not allowed */
1282 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1286 /* Check if we have a shared waiters semaphore */
1287 if (!Resource
->SharedWaiters
)
1289 /* Allocate one and try again */
1290 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1294 /* Now wait for the resource */
1295 Resource
->NumberOfSharedWaiters
++;
1296 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1297 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1299 /* Get the lock back */
1300 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1302 /* Find who owns it now */
1303 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1306 ASSERT(IsOwnedExclusive(Resource
) == FALSE
);
1307 ASSERT(Resource
->ActiveCount
> 0);
1308 ASSERT(Owner
->OwnerThread
!= Thread
);
1311 Owner
->OwnerThread
= Thread
;
1312 Owner
->OwnerCount
= 1;
1314 /* Release the lock and return */
1315 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1320 /* Resource is shared, find who owns it */
1321 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1322 if (!Owner
) goto TryAcquire
;
1325 if (Owner
->OwnerThread
== Thread
)
1327 /* Increase acquire count and return */
1328 Owner
->OwnerCount
++;
1329 ASSERT(Owner
->OwnerCount
!= 0);
1331 /* Release the lock and return */
1332 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1336 /* No exclusive waiters, so acquire it */
1337 Owner
->OwnerThread
= Thread
;
1338 Owner
->OwnerCount
= 1;
1339 Resource
->ActiveCount
++;
1341 /* Release the lock and return */
1342 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1349 * @name ExConvertExclusiveToSharedLite
1352 * The ExConvertExclusiveToSharedLite routine converts an exclusively
1353 * acquired resource into a resource that can be acquired shared.
1356 * Pointer to the resource to convert.
1360 * @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL <
1366 ExConvertExclusiveToSharedLite(PERESOURCE Resource
)
1370 DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource
);
1372 /* Lock the resource */
1373 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1376 ASSERT(KeIsExecutingDpc() == FALSE
);
1377 ExpVerifyResource(Resource
);
1378 ASSERT(IsOwnedExclusive(Resource
));
1379 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== (ERESOURCE_THREAD
)PsGetCurrentThread());
1381 /* Erase the exclusive flag */
1382 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1384 /* Check if we have shared waiters */
1385 OldWaiters
= Resource
->NumberOfSharedWaiters
;
1388 /* Make the waiters active owners */
1389 Resource
->ActiveCount
= Resource
->ActiveCount
+ (USHORT
)OldWaiters
;
1390 Resource
->NumberOfSharedWaiters
= 0;
1392 /* Release lock and wake the waiters */
1393 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1394 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, OldWaiters
, FALSE
);
1399 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1404 * @name ExDeleteResourceLite
1407 * The ExConvertExclusiveToSharedLite routine deletes a given resource
1408 * from the system\92s resource list.
1411 * Pointer to the resource to delete.
1413 * @return STATUS_SUCCESS if the resource was deleted.
1415 * @remarks Callers of ExDeleteResourceLite must be running at IRQL <
1421 ExDeleteResourceLite(PERESOURCE Resource
)
1424 DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource
);
1427 ASSERT(IsSharedWaiting(Resource
) == FALSE
);
1428 ASSERT(IsExclusiveWaiting(Resource
) == FALSE
);
1429 ASSERT(KeIsExecutingDpc() == FALSE
);
1430 ExpVerifyResource(Resource
);
1432 /* Lock the resource */
1433 KeAcquireSpinLock(&ExpResourceSpinLock
, &OldIrql
);
1435 /* Remove the resource */
1436 RemoveEntryList(&Resource
->SystemResourcesList
);
1438 /* Release the lock */
1439 KeReleaseSpinLock(&ExpResourceSpinLock
, OldIrql
);
1441 /* Free every structure */
1442 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
1443 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
1444 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
1446 /* Return success */
1447 return STATUS_SUCCESS
;
1451 * @name ExDisableResourceBoostLite
1454 * The ExDisableResourceBoostLite routine disables thread boosting for
1455 * the given resource.
1458 * Pointer to the resource whose thread boosting will be disabled.
1467 ExDisableResourceBoostLite(PERESOURCE Resource
)
1472 ExpVerifyResource(Resource
);
1474 /* Lock the resource */
1475 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1477 /* Remove the flag */
1478 Resource
->Flag
|= ResourceHasDisabledPriorityBoost
;
1480 /* Release the lock */
1481 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1485 * @name ExGetExclusiveWaiterCount
1488 * The ExGetExclusiveWaiterCount routine returns the number of exclusive
1489 * waiters for the given resource.
1492 * Pointer to the resource to check.
1494 * @return The number of exclusive waiters.
1501 ExGetExclusiveWaiterCount(PERESOURCE Resource
)
1503 /* Return the count */
1504 return Resource
->NumberOfExclusiveWaiters
;
1508 * @name ExGetSharedWaiterCount
1511 * The ExGetSharedWaiterCount routine returns the number of shared
1512 * waiters for the given resource.
1515 * Pointer to the resource to check.
1517 * @return The number of shared waiters.
1524 ExGetSharedWaiterCount(PERESOURCE Resource
)
1526 /* Return the count */
1527 return Resource
->NumberOfSharedWaiters
;
1531 * @name ExInitializeResourceLite
1534 * The ExInitializeResourceLite routine initializes a resource variable.
1537 * Pointer to the resource to check.
1539 * @return STATUS_SUCCESS.
1541 * @remarks The storage for ERESOURCE must not be allocated from paged pool.
1543 * The storage must be 8-byte aligned.
1548 ExInitializeResourceLite(PERESOURCE Resource
)
1550 DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource
);
1552 /* Clear the structure */
1553 RtlZeroMemory(Resource
, sizeof(ERESOURCE
));
1555 /* Initialize the lock */
1556 KeInitializeSpinLock(&Resource
->SpinLock
);
1558 /* Add it into the system list */
1559 ExInterlockedInsertTailList(&ExpSystemResourcesList
,
1560 &Resource
->SystemResourcesList
,
1561 &ExpResourceSpinLock
);
1563 /* Return success */
1564 return STATUS_SUCCESS
;
1568 * @name ExIsResourceAcquiredExclusiveLite
1571 * The ExIsResourceAcquiredExclusiveLite routine returns whether the
1572 * current thread has exclusive access to a given resource.
1575 * Pointer to the resource to check.
1577 * @return TRUE if the caller already has exclusive access to the given resource.
1579 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1580 * IRQL <= DISPATCH_LEVEL.
1585 ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource
)
1587 ERESOURCE_THREAD Thread
= ExGetCurrentResourceThread();
1588 BOOLEAN IsAcquired
= FALSE
;
1591 ExpVerifyResource(Resource
);
1593 /* Check if it's exclusively acquired */
1594 if ((IsOwnedExclusive(Resource
)) &&
1595 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
1597 /* It is acquired */
1601 /* Return if it's acquired */
1606 * @name ExIsResourceAcquiredSharedLite
1609 * The ExIsResourceAcquiredSharedLite routine returns whether the
1610 * current thread has has access (either shared or exclusive) to a
1614 * Pointer to the resource to check.
1616 * @return Number of times the caller has acquired the given resource for
1617 * shared or exclusive access.
1619 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1620 * IRQL <= DISPATCH_LEVEL.
1625 ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource
)
1627 ERESOURCE_THREAD Thread
;
1634 ExpVerifyResource(Resource
);
1636 /* Get the thread */
1637 Thread
= ExGetCurrentResourceThread();
1639 /* Check if we are in the thread list */
1640 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1642 /* Found it, return count */
1643 Count
= Resource
->OwnerThreads
[0].OwnerCount
;
1645 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
1647 /* Found it on the second list, return count */
1648 Count
= Resource
->OwnerThreads
[1].OwnerCount
;
1652 /* Not in the list, do a full table look up */
1653 Owner
= Resource
->OwnerTable
;
1656 /* Lock the resource */
1657 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1659 /* Get the resource index */
1660 i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
1661 Size
= Owner
->TableSize
;
1663 /* Check if the index is valid and check if we don't match */
1664 if ((i
>= Size
) || (Owner
[i
].OwnerThread
!= Thread
))
1666 /* Sh*t! We need to do a full search */
1667 for (i
= 1; i
< Size
; i
++)
1669 /* Move to next owner */
1672 /* Try to find a match */
1673 if (Owner
->OwnerThread
== Thread
)
1676 Count
= Owner
->OwnerCount
;
1683 /* We found the match directlry */
1684 Count
= Owner
[i
].OwnerCount
;
1687 /* Release the lock */
1688 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1697 * @name ExReinitializeResourceLite
1700 * The ExReinitializeResourceLite routine routine reinitializes
1701 * an existing resource variable.
1704 * Pointer to the resource to be reinitialized.
1706 * @return STATUS_SUCCESS.
1708 * @remarks With a single call to ExReinitializeResource, a driver writer can
1709 * replace three calls: one to ExDeleteResourceLite, another to
1710 * ExAllocatePool, and a third to ExInitializeResourceLite. As
1711 * contention for a resource variable increases, memory is dynamically
1712 * allocated and attached to the resource in order to track this
1713 * contention. As an optimization, ExReinitializeResourceLite retains
1714 * and zeroes this previously allocated memory.
1716 * Callers of ExReinitializeResourceLite must be running at
1717 * IRQL <= DISPATCH_LEVEL.
1722 ExReinitializeResourceLite(PERESOURCE Resource
)
1725 PKSEMAPHORE Semaphore
;
1729 /* Get the owner table */
1730 Owner
= Resource
->OwnerTable
;
1733 /* Get the size and loop it */
1734 Size
= Owner
->TableSize
;
1735 for (i
= 0; i
< Size
; i
++)
1737 /* Zero the table */
1738 Owner
[i
].OwnerThread
= 0;
1739 Owner
[i
].OwnerCount
= 0;
1743 /* Zero the flags and count */
1745 Resource
->ActiveCount
= 0;
1747 /* Reset the semaphore */
1748 Semaphore
= Resource
->SharedWaiters
;
1749 if (Semaphore
) KeInitializeSemaphore(Semaphore
, 0, MAXLONG
);
1751 /* Reset the event */
1752 Event
= Resource
->ExclusiveWaiters
;
1753 if (Event
) KeInitializeEvent(Event
, SynchronizationEvent
, FALSE
);
1755 /* Clear the resource data */
1756 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1757 Resource
->OwnerThreads
[1].OwnerThread
= 0;
1758 Resource
->OwnerThreads
[0].OwnerCount
= 0;
1759 Resource
->OwnerThreads
[1].OwnerCount
= 0;
1760 Resource
->ContentionCount
= 0;
1761 Resource
->NumberOfSharedWaiters
= 0;
1762 Resource
->NumberOfExclusiveWaiters
= 0;
1764 /* Reset the spinlock */
1765 KeInitializeSpinLock(&Resource
->SpinLock
);
1766 return STATUS_SUCCESS
;
1770 * @name ExReleaseResourceLite
1773 * The ExReleaseResourceLite routine routine releases
1774 * a specified executive resource owned by the current thread.
1777 * Pointer to the resource to be released.
1781 * @remarks Callers of ExReleaseResourceLite must be running at
1782 * IRQL <= DISPATCH_LEVEL.
1787 ExReleaseResourceLite(PERESOURCE Resource
)
1789 ERESOURCE_THREAD Thread
;
1792 POWNER_ENTRY Owner
, Limit
;
1793 DPRINT("ExReleaseResourceLite: %p\n", Resource
);
1796 ExpVerifyResource(Resource
);
1798 /* Get the thread and lock the resource */
1799 Thread
= ExGetCurrentResourceThread();
1800 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1801 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
1803 /* Check if it's exclusively owned */
1804 if (IsOwnedExclusive(Resource
))
1806 /* Decrement owner count and check if we're done */
1807 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
1808 ASSERT(Resource
->ActiveCount
== 1);
1809 if (--Resource
->OwnerThreads
[0].OwnerCount
)
1811 /* Done, release lock! */
1812 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1816 /* Clear the owner */
1817 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1819 /* Check if there are shared waiters */
1820 if (IsSharedWaiting(Resource
))
1822 /* Remove the exclusive flag */
1823 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1825 /* Give ownage to another thread */
1826 Count
= Resource
->NumberOfSharedWaiters
;
1827 Resource
->ActiveCount
= (USHORT
)Count
;
1828 Resource
->NumberOfSharedWaiters
= 0;
1830 /* Release lock and let someone else have it */
1831 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1832 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, Count
, FALSE
);
1835 else if (IsExclusiveWaiting(Resource
))
1837 /* Give exclusive access */
1838 Resource
->OwnerThreads
[0].OwnerThread
= 1;
1839 Resource
->OwnerThreads
[0].OwnerCount
= 1;
1840 Resource
->ActiveCount
= 1;
1841 Resource
->NumberOfExclusiveWaiters
--;
1843 /* Release the lock and give it away */
1844 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1845 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
1847 &Resource
->OwnerThreads
[0].OwnerThread
);
1851 /* Remove the exclusive flag */
1852 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1854 Resource
->ActiveCount
= 0;
1858 /* Check if we are in the thread list */
1859 if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
1861 /* Found it, get owner */
1862 Owner
= &Resource
->OwnerThreads
[1];
1866 /* Not in the list, do a full table look up */
1867 i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
1868 Owner
= Resource
->OwnerTable
;
1871 /* Nobody owns us, bugcheck! */
1872 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
1873 (ULONG_PTR
)Resource
,
1875 (ULONG_PTR
)Resource
->OwnerTable
,
1879 /* Check if we're out of the size and don't match */
1880 if ((i
>= Owner
->TableSize
) || (Owner
[i
].OwnerThread
!= Thread
))
1882 /* Get the last entry */
1883 Limit
= &Owner
[Owner
->TableSize
];
1886 /* Move to the next entry */
1889 /* Check if we don't match */
1892 /* Nobody owns us, bugcheck! */
1893 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
1894 (ULONG_PTR
)Resource
,
1896 (ULONG_PTR
)Resource
->OwnerTable
,
1900 /* Check for a match */
1901 if (Owner
->OwnerThread
== Thread
) break;
1906 /* Get the entry directly */
1912 ASSERT(Owner
->OwnerThread
== Thread
);
1913 ASSERT(Owner
->OwnerCount
> 0);
1915 /* Check if we are the last owner */
1916 if (--Owner
->OwnerCount
)
1919 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1924 Owner
->OwnerThread
= 0;
1926 /* See if the resource isn't being owned anymore */
1927 ASSERT(Resource
->ActiveCount
> 0);
1928 if (!(--Resource
->ActiveCount
))
1930 /* Check if there's an exclusive waiter */
1931 if (IsExclusiveWaiting(Resource
))
1933 /* Give exclusive access */
1934 Resource
->Flag
|= ResourceOwnedExclusive
;
1935 Resource
->OwnerThreads
[0].OwnerThread
= 1;
1936 Resource
->OwnerThreads
[0].OwnerCount
= 1;
1937 Resource
->ActiveCount
= 1;
1938 Resource
->NumberOfExclusiveWaiters
--;
1940 /* Release the lock and give it away */
1941 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1942 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
1944 &Resource
->OwnerThreads
[0].OwnerThread
);
1951 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1956 * @name ExReleaseResourceForThreadLite
1959 * The ExReleaseResourceForThreadLite routine routine releases
1960 * the input resource of the indicated thread.
1963 * Pointer to the resource to be released.
1966 * Identifies the thread that originally acquired the resource.
1970 * @remarks Callers of ExReleaseResourceForThreadLite must be running at
1971 * IRQL <= DISPATCH_LEVEL.
1976 ExReleaseResourceForThreadLite(PERESOURCE Resource
,
1977 ERESOURCE_THREAD Thread
)
1983 ASSERT(Thread
!= 0);
1984 DPRINT("ExReleaseResourceForThreadLite: %p\n", Resource
);
1986 /* Get the thread and lock the resource */
1987 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1990 ExpVerifyResource(Resource
);
1992 /* Check if it's exclusively owned */
1993 if (IsOwnedExclusive(Resource
))
1995 /* Decrement owner count and check if we're done */
1996 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== Thread
);
1997 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
1998 if (--Resource
->OwnerThreads
[0].OwnerCount
)
2000 /* Done, release lock! */
2001 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2005 /* Clear the owner */
2006 Resource
->OwnerThreads
[0].OwnerThread
= 0;
2008 /* See if the resource isn't being owned anymore */
2009 ASSERT(Resource
->ActiveCount
> 0);
2010 if (!(--Resource
->ActiveCount
))
2012 /* Check if there are shared waiters */
2013 if (IsSharedWaiting(Resource
))
2015 /* Remove the exclusive flag */
2016 Resource
->Flag
&= ~ResourceOwnedExclusive
;
2018 /* Give ownage to another thread */
2019 Count
= Resource
->NumberOfSharedWaiters
;
2020 Resource
->ActiveCount
= (USHORT
)Count
;
2021 Resource
->NumberOfSharedWaiters
= 0;
2023 /* Release lock and let someone else have it */
2024 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2025 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, Count
, FALSE
);
2028 else if (IsExclusiveWaiting(Resource
))
2030 /* Give exclusive access */
2031 Resource
->OwnerThreads
[0].OwnerThread
= 1;
2032 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2033 Resource
->ActiveCount
= 1;
2034 Resource
->NumberOfExclusiveWaiters
--;
2036 /* Release the lock and give it away */
2037 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2038 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
2040 &Resource
->OwnerThreads
[0].OwnerThread
);
2044 /* Remove the exclusive flag */
2045 Resource
->Flag
&= ~ResourceOwnedExclusive
;
2050 /* Check if we are in the thread list */
2051 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
2053 /* Found it, get owner */
2054 Owner
= &Resource
->OwnerThreads
[0];
2056 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
2058 /* Found it on the second list, get owner */
2059 Owner
= &Resource
->OwnerThreads
[1];
2063 /* Assume no valid index */
2066 /* If we got a valid pointer, try to get the resource index */
2067 if (!((ULONG
)Thread
& 3)) i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
2069 /* Do a table lookup */
2070 Owner
= Resource
->OwnerTable
;
2071 ASSERT(Owner
!= NULL
);
2073 /* Check if we're out of the size and don't match */
2074 if ((i
>= Owner
->TableSize
) || (Owner
[i
].OwnerThread
!= Thread
))
2076 /* Get the last entry */
2079 /* Move to the next entry */
2082 /* Check for a match */
2083 if (Owner
->OwnerThread
== Thread
) break;
2088 /* Get the entry directly */
2094 ASSERT(Owner
->OwnerThread
== Thread
);
2095 ASSERT(Owner
->OwnerCount
> 0);
2097 /* Check if we are the last owner */
2098 if (!(--Owner
->OwnerCount
))
2101 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2106 Owner
->OwnerThread
= 0;
2108 /* See if the resource isn't being owned anymore */
2109 ASSERT(Resource
->ActiveCount
> 0);
2110 if (!(--Resource
->ActiveCount
))
2112 /* Check if there's an exclusive waiter */
2113 if (IsExclusiveWaiting(Resource
))
2115 /* Give exclusive access */
2116 Resource
->OwnerThreads
[0].OwnerThread
= 1;
2117 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2118 Resource
->ActiveCount
= 1;
2119 Resource
->NumberOfExclusiveWaiters
--;
2121 /* Release the lock and give it away */
2122 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2123 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
2125 &Resource
->OwnerThreads
[0].OwnerThread
);
2132 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2137 * @name ExSetResourceOwnerPointer
2140 * The ExSetResourceOwnerPointer routine routine sets the owner thread
2141 * thread pointer for an executive resource.
2144 * Pointer to the resource whose owner to change.
2146 * @param OwnerPointer
2147 * Pointer to an owner thread pointer of type ERESOURCE_THREAD.
2151 * @remarks ExSetResourceOwnerPointer, used in conjunction with
2152 * ExReleaseResourceForThreadLite, provides a means for one thread
2153 * (acting as an resource manager thread) to acquire and release
2154 * resources for use by another thread (acting as a resource user
2157 * After calling ExSetResourceOwnerPointer for a specific resource,
2158 * the only other routine that can be called for that resource is
2159 * ExReleaseResourceForThreadLite.
2161 * Callers of ExSetResourceOwnerPointer must be running at
2162 * IRQL <= DISPATCH_LEVEL.
2167 ExSetResourceOwnerPointer(IN PERESOURCE Resource
,
2168 IN PVOID OwnerPointer
)
2170 ERESOURCE_THREAD Thread
;
2172 POWNER_ENTRY Owner
, ThisOwner
;
2175 ASSERT((OwnerPointer
!= 0) && (((ULONG_PTR
)OwnerPointer
& 3) == 3));
2177 /* Get the thread */
2178 Thread
= ExGetCurrentResourceThread();
2181 ExpVerifyResource(Resource
);
2183 /* Lock the resource */
2184 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
2186 /* Check if it's exclusive */
2187 if (IsOwnedExclusive(Resource
))
2189 /* If it's exclusive, set the first entry no matter what */
2190 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== Thread
);
2191 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
2192 Resource
->OwnerThreads
[0].OwnerThread
= (ULONG_PTR
)OwnerPointer
;
2196 /* Set the thread in both entries */
2197 ThisOwner
= ExpFindEntryForThread(Resource
,
2198 (ERESOURCE_THREAD
)OwnerPointer
,
2200 Owner
= ExpFindEntryForThread(Resource
, Thread
, 0);
2203 /* Nobody owns it, crash */
2204 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
2205 (ULONG_PTR
)Resource
,
2207 (ULONG_PTR
)Resource
->OwnerTable
,
2211 /* Set if we are the owner */
2215 ThisOwner
->OwnerCount
+= Owner
->OwnerCount
;
2216 Owner
->OwnerCount
= 0;
2217 Owner
->OwnerThread
= 0;
2218 ASSERT(Resource
->ActiveCount
>= 2);
2219 Resource
->ActiveCount
--;
2223 /* Release the resource */
2224 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2228 * @name ExTryToAcquireResourceExclusiveLite
2231 * The ExTryToAcquireResourceExclusiveLite routine routine attemps to
2232 * acquire the given resource for exclusive access.
2235 * Pointer to the resource to be acquired.
2237 * @return TRUE if the given resource has been acquired for the caller.
2239 * @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at
2240 * IRQL < DISPATCH_LEVEL.
2245 ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource
)
2247 ERESOURCE_THREAD Thread
;
2249 BOOLEAN Acquired
= FALSE
;
2250 DPRINT("ExTryToAcquireResourceExclusiveLite: %p\n", Resource
);
2253 ASSERT((Resource
->Flag
& ResourceNeverExclusive
) == 0);
2255 /* Get the thread */
2256 Thread
= ExGetCurrentResourceThread();
2258 /* Sanity check and validation */
2259 ASSERT(KeIsExecutingDpc() == FALSE
);
2260 ExpVerifyResource(Resource
);
2262 /* Acquire the lock */
2263 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
2265 /* Check if there is an owner */
2266 if (!Resource
->ActiveCount
)
2268 /* No owner, give exclusive access */
2269 Resource
->Flag
|= ResourceOwnedExclusive
;
2270 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
2271 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2272 Resource
->ActiveCount
= 1;
2275 else if ((IsOwnedExclusive(Resource
)) &&
2276 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
2278 /* Do a recursive acquire */
2279 Resource
->OwnerThreads
[0].OwnerCount
++;
2283 /* Release the resource */
2284 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2289 * @name ExEnterCriticalRegionAndAcquireResourceExclusive
2290 * @implemented NT5.1
2292 * The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical
2293 * region and then exclusively acquires a resource.
2296 * Pointer to the resource to acquire.
2298 * @return Pointer to the Win32K thread pointer of the current thread.
2300 * @remarks See ExAcquireResourceExclusiveLite.
2305 ExEnterCriticalRegionAndAcquireResourceExclusive(PERESOURCE Resource
)
2307 /* Enter critical region */
2308 KeEnterCriticalRegion();
2310 /* Acquire the resource */
2311 ExAcquireResourceExclusiveLite(Resource
, TRUE
);
2313 /* Return the Win32 Thread */
2314 return KeGetCurrentThread()->Win32Thread
;
2318 * @name ExReleaseResourceAndLeaveCriticalRegion
2319 * @implemented NT5.1
2321 * The ExReleaseResourceAndLeaveCriticalRegion release a resource and
2322 * then leaves a critical region.
2325 * Pointer to the resource to release.
2329 * @remarks See ExReleaseResourceLite.
2334 ExReleaseResourceAndLeaveCriticalRegion(PERESOURCE Resource
)
2336 /* Release the resource */
2337 ExReleaseResourceLite(Resource
);
2339 /* Leave critical region */
2340 KeLeaveCriticalRegion();