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 "
115 * @name ExpResourceInitialization
117 * The ExpResourceInitialization routine initializes resources for use.
123 * @remarks This routine should only be called once, during system startup.
129 ExpResourceInitialization(VOID
)
131 /* Setup the timeout */
132 ExpTimeout
.QuadPart
= Int32x32To64(4, -10000000);
133 InitializeListHead(&ExpSystemResourcesList
);
134 KeInitializeSpinLock(&ExpResourceSpinLock
);
138 * @name ExpAllocateExclusiveWaiterEvent
140 * The ExpAllocateExclusiveWaiterEvent routine creates the event that will
141 * be used by exclusive waiters on the resource.
144 * Pointer to the resource.
147 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
151 * @remarks The pointer to the event must be atomically set.
156 ExpAllocateExclusiveWaiterEvent(IN PERESOURCE Resource
,
161 /* Release the lock */
162 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
164 /* Allocate the event */
165 Event
= ExAllocatePoolWithTag(NonPagedPool
,
170 KeInitializeEvent(Event
, SynchronizationEvent
, FALSE
);
173 if (InterlockedCompareExchangePointer(&Resource
->ExclusiveWaiters
,
177 /* Someone already set it, free our event */
178 DPRINT1("WARNING: Handling race condition\n");
182 /* Re-acquire the lock */
183 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
187 * @name ExpAllocateSharedWaiterSemaphore
189 * The ExpAllocateSharedWaiterSemaphore routine creates the semaphore that
190 * will be used by shared waiters on the resource.
193 * Pointer to the resource.
196 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
200 * @remarks The pointer to the semaphore must be atomically set.
205 ExpAllocateSharedWaiterSemaphore(IN PERESOURCE Resource
,
208 PKSEMAPHORE Semaphore
;
210 /* Release the lock */
211 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
213 /* Allocate the semaphore */
214 Semaphore
= ExAllocatePoolWithTag(NonPagedPool
,
216 TAG_RESOURCE_SEMAPHORE
);
219 KeInitializeSemaphore(Semaphore
, 0, MAXLONG
);
222 if (InterlockedCompareExchangePointer(&Resource
->SharedWaiters
,
226 /* Someone already set it, free our semaphore */
227 DPRINT1("WARNING: Handling race condition\n");
228 ExFreePool(Semaphore
);
231 /* Re-acquire the lock */
232 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
236 * @name ExpExpandResourceOwnerTable
238 * The ExpExpandResourceOwnerTable routine expands the owner table of the
239 * specified resource.
242 * Pointer to the resource.
245 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
254 ExpExpandResourceOwnerTable(IN PERESOURCE Resource
,
257 POWNER_ENTRY Owner
, Table
;
258 ULONG NewSize
, OldSize
;
259 DPRINT("ExpExpandResourceOwnerTable: %p\n", Resource
);
261 /* Get the owner table */
262 Owner
= Resource
->OwnerTable
;
265 /* Start with the default size of 3 */
271 /* Add 4 more entries */
272 OldSize
= Owner
->TableSize
;
273 NewSize
= OldSize
+ 4;
276 /* Release the lock */
277 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
279 /* Allocate memory for the table */
280 Table
= ExAllocatePoolWithTag(NonPagedPool
,
281 NewSize
* sizeof(OWNER_ENTRY
),
285 RtlZeroMemory((PVOID
)(Table
+ OldSize
),
286 (NewSize
- OldSize
) * sizeof(OWNER_ENTRY
));
288 /* Lock the resource */
289 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
291 /* Make sure nothing has changed */
292 if ((Owner
!= Resource
->OwnerTable
) ||
293 ((Owner
) && (OldSize
!= Resource
->OwnerTable
->TableSize
)))
295 /* Resource changed while we weren't holding the lock; bail out */
296 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
302 RtlCopyMemory((PVOID
)Table
,
304 OldSize
* sizeof(OWNER_ENTRY
));
306 /* Acquire dispatcher lock to prevent thread boosting */
307 //KeAcquireDispatcherDatabaseLockAtDpcLevel();
309 /* Set the new table data */
310 Table
->TableSize
= NewSize
;
311 Resource
->OwnerTable
= Table
;
314 ExpVerifyResource(Resource
);
317 //KeReleaseDispatcherDatabaseLockFromDpcLevel();
318 ExReleaseResourceLock(&Resource
->SpinLock
, *OldIrql
);
320 /* Free the old table */
321 if (Owner
) ExFreePool(Owner
);
323 /* Set the resource index */
324 if (!OldSize
) OldSize
= 1;
327 /* Set the resource index */
328 KeGetCurrentThread()->ResourceIndex
= (UCHAR
)OldSize
;
330 /* Lock the resource again */
331 ExAcquireResourceLock(&Resource
->SpinLock
, OldIrql
);
335 * @name ExpFindFreeEntry
337 * The ExpFindFreeEntry routine locates an empty owner entry in the
338 * specified resource. If none was found, then the owner table is
342 * Pointer to the resource.
345 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
347 * @return Pointer to an empty OWNER_ENTRY structure.
354 ExpFindFreeEntry(IN PERESOURCE Resource
,
357 POWNER_ENTRY Owner
, Limit
;
359 POWNER_ENTRY FreeEntry
= NULL
;
360 DPRINT("ExpFindFreeEntry: %p\n", Resource
);
363 ASSERT(OldIrql
!= 0);
364 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
!= 0);
366 /* Check if the next built-in entry is free */
367 if (!Resource
->OwnerThreads
[1].OwnerThread
)
370 FreeEntry
= &Resource
->OwnerThreads
[1];
374 /* Get the current table pointer */
375 Owner
= Resource
->OwnerTable
;
378 /* Loop every entry */
379 Size
= Owner
->TableSize
;
380 Limit
= &Owner
[Size
];
382 /* Go to the next entry and loop */
386 /* Check for a free entry */
387 if (!Owner
->OwnerThread
)
389 /* Found one, return it!*/
396 } while (Owner
!= Limit
);
398 /* If we found a free entry by now, return it */
401 /* Set the resource index */
402 KeGetCurrentThread()->ResourceIndex
=
403 (UCHAR
)(Owner
- Resource
->OwnerTable
);
408 /* No free entry, expand the table */
409 ExpExpandResourceOwnerTable(Resource
, OldIrql
);
413 /* Return the entry found */
418 * @name ExpFindEntryForThread
420 * The ExpFindEntryForThread routine locates the owner entry associated with
421 * the specified thread in the given resource. If none was found, then the
422 * owner table is expanded.
425 * Pointer to the resource.
428 * Pointer to the thread to find.
431 * Pointer to current IRQL. TBC: Pointer to in-stack queued spinlock.
433 * @return Pointer to an empty OWNER_ENTRY structure.
440 ExpFindEntryForThread(IN PERESOURCE Resource
,
441 IN ERESOURCE_THREAD Thread
,
444 POWNER_ENTRY FreeEntry
, Owner
, Limit
;
446 DPRINT("ExpFindEntryForThread: %p\n", Resource
);
448 /* Start by looking in the static array */
449 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
451 /* Found it, return it! */
452 return &Resource
->OwnerThreads
[0];
454 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
457 return &Resource
->OwnerThreads
[1];
461 /* Check if the first array is empty for our use */
463 if (!Resource
->OwnerThreads
[1].OwnerThread
)
465 /* Use this as the first free entry */
466 FreeEntry
= &Resource
->OwnerThreads
[1];
469 /* Get the current table pointer */
470 Owner
= Resource
->OwnerTable
;
473 /* The current table is empty, so no size */
478 /* We have a table, get it's size and limit */
479 Size
= Owner
->TableSize
;
480 Limit
= &Owner
[Size
];
482 /* Go to the next entry and loop */
486 /* Check for a match */
487 if (Owner
->OwnerThread
== Thread
)
489 /* Match found! Set the resource index */
490 KeGetCurrentThread()->ResourceIndex
=
491 (UCHAR
)(Owner
- Resource
->OwnerTable
);
495 /* If we don't have a free entry yet, make this one free */
496 if (!(FreeEntry
) && !(Owner
->OwnerThread
)) FreeEntry
= Owner
;
500 } while (Owner
!= Limit
);
504 /* Check if it's OK to do an expansion */
505 if (!OldIrql
) return NULL
;
507 /* If we found a free entry by now, return it */
510 /* Set the resource index */
511 KeGetCurrentThread()->ResourceIndex
= (UCHAR
)
512 (Owner
- Resource
->OwnerTable
);
516 /* No free entry, expand the table */
517 ExpExpandResourceOwnerTable(Resource
, OldIrql
);
522 * @name ExpBoostOwnerThread
524 * The ExpBoostOwnerThread routine increases the priority of a waiting
525 * thread in an attempt to fight a possible deadlock.
528 * Pointer to the current thread.
531 * Pointer to thread that owns the resource.
540 ExpBoostOwnerThread(IN PKTHREAD Thread
,
541 IN PKTHREAD OwnerThread
)
543 BOOLEAN Released
= FALSE
;
544 DPRINT("ExpBoostOwnerThread: %p\n", Thread
);
546 /* Make sure the owner thread is a pointer, not an ID */
547 if (!((ULONG_PTR
)OwnerThread
& 0x3))
549 /* Check if we can actually boost it */
550 if ((OwnerThread
->Priority
< Thread
->Priority
) &&
551 (OwnerThread
->Priority
< 14))
553 /* Make sure we're at dispatch */
554 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
556 /* Set the new priority */
557 OwnerThread
->PriorityDecrement
+= 14 - OwnerThread
->Priority
;
560 OwnerThread
->Quantum
= OwnerThread
->QuantumReset
;
562 /* Update the kernel state */
563 KiSetPriorityThread(OwnerThread
, 14, &Released
);
565 /* Release Lock if needed */
566 if (!Released
) KeReleaseDispatcherDatabaseLockFromDpcLevel();
568 /* Make sure we're still at dispatch */
569 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
575 * @name ExpWaitForResource
577 * The ExpWaitForResource routine performs a wait on the specified resource.
580 * Pointer to the resource to wait on.
583 * Pointer to object (exclusive event or shared semaphore) to wait on.
592 ExpWaitForResource(IN PERESOURCE Resource
,
596 ULONG Size
, WaitCount
= 0;
599 PKTHREAD OwnerThread
, Thread
;
601 LARGE_INTEGER Timeout
;
603 /* Increase contention count and use a 5 second timeout */
604 Resource
->ContentionCount
++;
605 Timeout
.QuadPart
= 500 * -10000;
608 /* Wait for ownership */
609 Status
= KeWaitForSingleObject(Object
,
614 if (Status
!= STATUS_TIMEOUT
) break;
616 /* Increase wait count */
618 Timeout
= ExpTimeout
;
620 /* Check if we've exceeded the limit */
621 if (WaitCount
> ExpResourceTimeoutCount
)
623 /* Reset wait count */
626 /* Lock the resource */
627 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
629 /* Dump debug information */
630 DPRINT1("Resource @ %lx\n", Resource
);
631 DPRINT1(" ActiveCount = %04lx Flags = %s%s%s\n",
632 Resource
->ActiveCount
,
633 IsOwnedExclusive(Resource
) ? "IsOwnedExclusive " : "",
634 IsSharedWaiting(Resource
) ? "SharedWaiter " : "",
635 IsExclusiveWaiting(Resource
) ? "ExclusiveWaiter " : "");
636 DPRINT1(" NumberOfExclusiveWaiters = %04lx\n",
637 Resource
->NumberOfExclusiveWaiters
);
638 DPRINT1(" Thread = %08lx, Count = %02x\n",
639 Resource
->OwnerThreads
[0].OwnerThread
,
640 Resource
->OwnerThreads
[0].OwnerCount
);
641 DPRINT1(" Thread = %08lx, Count = %02x\n",
642 Resource
->OwnerThreads
[1].OwnerThread
,
643 Resource
->OwnerThreads
[1].OwnerCount
);
645 /* Dump out the table too */
646 Owner
= Resource
->OwnerTable
;
649 /* Loop every entry */
650 Size
= Owner
->TableSize
;
651 for (i
= 1; i
< Size
; i
++)
655 DPRINT1(" Thread = %08lx, Count = %02x\n",
663 DPRINT1("EX - Rewaiting\n");
664 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
668 /* Check if we can boost */
669 if (!(Resource
->Flag
& ResourceHasDisabledPriorityBoost
))
671 /* Get the current kernel thread and lock the dispatcher */
672 Thread
= KeGetCurrentThread();
673 Thread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
674 Thread
->WaitNext
= TRUE
;
676 /* Get the owner thread and boost it */
677 OwnerThread
= (PKTHREAD
)Resource
->OwnerThreads
[0].OwnerThread
;
678 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
680 /* If it's a shared resource */
681 if (!IsOwnedExclusive(Resource
))
683 /* Boost the other owner thread too */
684 OwnerThread
= (PKTHREAD
)Resource
->OwnerThreads
[1].OwnerThread
;
685 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
688 Owner
= Resource
->OwnerTable
;
691 /* Loop every entry */
692 Size
= Owner
->TableSize
;
693 for (i
= 1; i
< Size
; i
++)
695 /* Boot every thread in the table */
696 OwnerThread
= (PKTHREAD
)
697 Resource
->OwnerThreads
[1].OwnerThread
;
698 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
706 /* FUNCTIONS *****************************************************************/
709 * @name ExAcquireResourceExclusiveLite
712 * The ExAcquireResourceExclusiveLite routine acquires the given resource
713 * for exclusive access by the calling thread.
716 * Pointer to the resource to acquire.
719 * Specifies the routine's behavior whenever the resource cannot be
720 * acquired immediately.
722 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
723 * and exclusive access cannot be granted immediately.
725 * @remarks The caller can release the resource by calling either
726 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
728 * Normal kernel APC delivery must be disabled before calling this
729 * routine. Disable normal kernel APC delivery by calling
730 * KeEnterCriticalRegion. Delivery must remain disabled until the
731 * resource is released, at which point it can be reenabled by calling
732 * KeLeaveCriticalRegion.
734 * For better performance, call ExTryToAcquireResourceExclusiveLite,
735 * rather than calling ExAcquireResourceExclusiveLite with Wait set
738 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
744 ExAcquireResourceExclusiveLite(PERESOURCE Resource
,
747 KIRQL OldIrql
= PASSIVE_LEVEL
;
748 ERESOURCE_THREAD Thread
;
752 ASSERT((Resource
->Flag
& ResourceNeverExclusive
) == 0);
755 Thread
= ExGetCurrentResourceThread();
757 /* Sanity check and validation */
758 ASSERT(KeIsExecutingDpc() == FALSE
);
759 ExpVerifyResource(Resource
);
761 /* Acquire the lock */
762 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
763 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
765 /* Check if there is a shared owner or exclusive owner */
767 DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n",
769 if (Resource
->ActiveCount
)
771 /* Check if it's exclusively owned, and we own it */
772 if ((IsOwnedExclusive(Resource
)) &&
773 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
775 /* Increase the owning count */
776 Resource
->OwnerThreads
[0].OwnerCount
++;
782 * If the caller doesn't want us to wait, we can't acquire the
783 * resource because someone else then us owns it. If we can wait,
792 /* Check if it has exclusive waiters */
793 if (!Resource
->ExclusiveWaiters
)
795 /* It doesn't, allocate the event and try acquiring again */
796 ExpAllocateExclusiveWaiterEvent(Resource
, &OldIrql
);
801 /* Has exclusive waiters, wait on it */
802 Resource
->NumberOfExclusiveWaiters
++;
803 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
804 ExpWaitForResource(Resource
, Resource
->ExclusiveWaiters
);
806 /* Set owner and return success */
807 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
815 /* Nobody owns it, so let's! */
816 Resource
->Flag
|= ResourceOwnedExclusive
;
817 Resource
->ActiveCount
= 1;
818 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
819 Resource
->OwnerThreads
[0].OwnerCount
= 1;
823 /* Release the lock and return */
824 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
829 * @name ExAcquireResourceSharedLite
832 * The ExAcquireResourceSharedLite routine acquires the given resource
833 * for shared access by the calling thread.
836 * Pointer to the resource to acquire.
839 * Specifies the routine's behavior whenever the resource cannot be
840 * acquired immediately.
842 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
843 * and exclusive access cannot be granted immediately.
845 * @remarks The caller can release the resource by calling either
846 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
848 * Normal kernel APC delivery must be disabled before calling this
849 * routine. Disable normal kernel APC delivery by calling
850 * KeEnterCriticalRegion. Delivery must remain disabled until the
851 * resource is released, at which point it can be reenabled by calling
852 * KeLeaveCriticalRegion.
854 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
860 ExAcquireResourceSharedLite(PERESOURCE Resource
,
864 ERESOURCE_THREAD Thread
;
868 Thread
= ExGetCurrentResourceThread();
870 /* Sanity check and validation */
871 ASSERT(KeIsExecutingDpc() == FALSE
);
872 ExpVerifyResource(Resource
);
874 /* Acquire the lock */
875 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
876 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
878 /* See if nobody owns us */
880 DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n",
882 if (!Resource
->ActiveCount
)
884 /* Nobody owns it, so let's take control */
885 Resource
->ActiveCount
= 1;
886 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
887 Resource
->OwnerThreads
[1].OwnerCount
= 1;
889 /* Release the lock and return */
890 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
894 /* Check if it's exclusively owned */
895 if (IsOwnedExclusive(Resource
))
897 /* Check if we own it */
898 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
900 /* Increase the owning count */
901 Resource
->OwnerThreads
[0].OwnerCount
++;
903 /* Release the lock and return */
904 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
908 /* Find a free entry */
909 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
910 if (!Owner
) goto TryAcquire
;
914 /* Resource is shared, find who owns it */
915 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
916 if (!Owner
) goto TryAcquire
;
919 if (Owner
->OwnerThread
== Thread
)
921 /* Increase acquire count and return */
923 ASSERT(Owner
->OwnerCount
!= 0);
925 /* Release the lock and return */
926 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
930 /* Try to find if there are exclusive waiters */
931 if (!IsExclusiveWaiting(Resource
))
933 /* There are none, so acquire it */
934 Owner
->OwnerThread
= Thread
;
935 Owner
->OwnerCount
= 1;
936 Resource
->ActiveCount
++;
938 /* Release the lock and return */
939 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
944 /* If we got here, then we need to wait. Are we allowed? */
947 /* Release the lock and return */
948 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
952 /* Check if we have a shared waiters semaphore */
953 if (!Resource
->SharedWaiters
)
955 /* Allocate it and try another acquire */
956 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
960 /* Now wait for the resource */
961 Owner
->OwnerThread
= Thread
;
962 Owner
->OwnerCount
= 1;
963 Resource
->NumberOfSharedWaiters
++;
965 /* Release the lock and return */
966 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
967 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
972 * @name ExAcquireSharedStarveExclusive
975 * The ExAcquireSharedStarveExclusive routine acquires the given resource
976 * shared access without waiting for any pending attempts to acquire
977 * exclusive access to the same resource.
980 * Pointer to the resource to acquire.
983 * Specifies the routine's behavior whenever the resource cannot be
984 * acquired immediately.
986 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
987 * and exclusive access cannot be granted immediately.
989 * @remarks The caller can release the resource by calling either
990 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
992 * Normal kernel APC delivery must be disabled before calling this
993 * routine. Disable normal kernel APC delivery by calling
994 * KeEnterCriticalRegion. Delivery must remain disabled until the
995 * resource is released, at which point it can be reenabled by calling
996 * KeLeaveCriticalRegion.
998 * Callers of ExAcquireSharedStarveExclusive usually need quick access
999 * to a shared resource in order to save an exclusive accessor from
1000 * doing redundant work. For example, a file system might call this
1001 * routine to modify a cached resource, such as a BCB pinned in the
1002 * cache, before the Cache Manager can acquire exclusive access to the
1003 * resource and write the cache out to disk.
1005 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1011 ExAcquireSharedStarveExclusive(PERESOURCE Resource
,
1015 ERESOURCE_THREAD Thread
;
1018 /* Get the thread */
1019 Thread
= ExGetCurrentResourceThread();
1021 /* Sanity check and validation */
1022 ASSERT(KeIsExecutingDpc() == FALSE
);
1023 ExpVerifyResource(Resource
);
1025 /* Acquire the lock */
1026 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1028 /* See if nobody owns us */
1030 DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n",
1032 if (!Resource
->ActiveCount
)
1034 /* Nobody owns it, so let's take control */
1035 Resource
->ActiveCount
= 1;
1036 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
1037 Resource
->OwnerThreads
[1].OwnerCount
= 1;
1039 /* Release the lock and return */
1040 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1044 /* Check if it's exclusively owned */
1045 if (IsOwnedExclusive(Resource
))
1047 /* Check if we own it */
1048 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1050 /* Increase the owning count */
1051 Resource
->OwnerThreads
[0].OwnerCount
++;
1053 /* Release the lock and return */
1054 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1058 /* Find a free entry */
1059 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
1060 if (!Owner
) goto TryAcquire
;
1062 /* If we got here, then we need to wait. Are we allowed? */
1065 /* Release the lock and return */
1066 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1070 /* Check if we have a shared waiters semaphore */
1071 if (!Resource
->SharedWaiters
)
1073 /* Allocate one and try again */
1074 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1080 /* Resource is shared, find who owns it */
1081 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1082 if (!Owner
) goto TryAcquire
;
1085 if (Owner
->OwnerThread
== Thread
)
1087 /* Increase acquire count and return */
1088 Owner
->OwnerCount
++;
1089 ASSERT(Owner
->OwnerCount
!= 0);
1091 /* Release the lock and return */
1092 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1097 Owner
->OwnerThread
= Thread
;
1098 Owner
->OwnerCount
= 1;
1099 Resource
->ActiveCount
++;
1101 /* Release the lock and return */
1102 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1106 /* If we got here, then we need to wait. Are we allowed? */
1109 /* Release the lock and return */
1110 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1114 /* Now wait for the resource */
1115 Owner
->OwnerThread
= Thread
;
1116 Owner
->OwnerCount
= 1;
1117 Resource
->NumberOfSharedWaiters
++;
1119 /* Release the lock and return */
1120 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1121 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1126 * @name ExAcquireSharedWaitForExclusive
1129 * The ExAcquireSharedWaitForExclusive routine acquires the given resource
1130 * for shared access if shared access can be granted and there are no
1131 * exclusive waiters.
1134 * Pointer to the resource to acquire.
1137 * Specifies the routine's behavior whenever the resource cannot be
1138 * acquired immediately.
1140 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1141 * and exclusive access cannot be granted immediately.
1143 * @remarks The caller can release the resource by calling either
1144 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1146 * Normal kernel APC delivery must be disabled before calling this
1147 * routine. Disable normal kernel APC delivery by calling
1148 * KeEnterCriticalRegion. Delivery must remain disabled until the
1149 * resource is released, at which point it can be reenabled by calling
1150 * KeLeaveCriticalRegion.
1152 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1158 ExAcquireSharedWaitForExclusive(PERESOURCE Resource
,
1162 ERESOURCE_THREAD Thread
;
1165 /* Get the thread */
1166 Thread
= ExGetCurrentResourceThread();
1168 /* Sanity check and validation */
1169 ASSERT(KeIsExecutingDpc() == FALSE
);
1170 ExpVerifyResource(Resource
);
1172 /* Acquire the lock */
1173 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1175 /* See if nobody owns us */
1177 DPRINT("ExAcquireSharedWaitForExclusive(Resource 0x%p, Wait %d)\n",
1179 if (!Resource
->ActiveCount
)
1181 /* Nobody owns it, so let's take control */
1182 Resource
->ActiveCount
= 1;
1183 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
1184 Resource
->OwnerThreads
[1].OwnerCount
= 1;
1186 /* Release the lock and return */
1187 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1191 /* Check if it's exclusively owned */
1192 if (IsOwnedExclusive(Resource
))
1194 /* Check if we own it */
1195 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1197 /* Increase the owning count */
1198 Resource
->OwnerThreads
[0].OwnerCount
++;
1200 /* Release the lock and return */
1201 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1206 /* Find a free entry */
1207 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
1208 if (!Owner
) goto TryAcquire
;
1210 /* If we got here, then we need to wait. Are we allowed? */
1213 /* Release the lock and return */
1214 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1218 /* Check if we have a shared waiters semaphore */
1219 if (!Resource
->SharedWaiters
)
1221 /* Allocate one and try again */
1222 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1226 /* Now take control of the resource */
1227 Owner
->OwnerThread
= Thread
;
1228 Owner
->OwnerCount
= 1;
1229 Resource
->NumberOfSharedWaiters
++;
1231 /* Release the lock and wait for it to be ours */
1232 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1233 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1239 /* Try to find if there are exclusive waiters */
1240 if (IsExclusiveWaiting(Resource
))
1242 /* We have to wait for the exclusive waiter to be done */
1245 /* So bail out if we're not allowed */
1246 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1250 /* Check if we have a shared waiters semaphore */
1251 if (!Resource
->SharedWaiters
)
1253 /* Allocate one and try again */
1254 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1258 /* Now wait for the resource */
1259 Resource
->NumberOfSharedWaiters
++;
1260 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1261 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1263 /* Get the lock back */
1264 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1266 /* Find who owns it now */
1267 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1270 ASSERT(IsOwnedExclusive(Resource
) == FALSE
);
1271 ASSERT(Resource
->ActiveCount
> 0);
1272 ASSERT(Owner
->OwnerThread
!= Thread
);
1275 Owner
->OwnerThread
= Thread
;
1276 Owner
->OwnerCount
= 1;
1278 /* Release the lock and return */
1279 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1284 /* Resource is shared, find who owns it */
1285 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1286 if (!Owner
) goto TryAcquire
;
1289 if (Owner
->OwnerThread
== Thread
)
1291 /* Increase acquire count and return */
1292 Owner
->OwnerCount
++;
1293 ASSERT(Owner
->OwnerCount
!= 0);
1295 /* Release the lock and return */
1296 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1300 /* No exclusive waiters, so acquire it */
1301 Owner
->OwnerThread
= Thread
;
1302 Owner
->OwnerCount
= 1;
1303 Resource
->ActiveCount
++;
1305 /* Release the lock and return */
1306 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1313 * @name ExConvertExclusiveToSharedLite
1316 * The ExConvertExclusiveToSharedLite routine converts an exclusively
1317 * acquired resource into a resource that can be acquired shared.
1320 * Pointer to the resource to convert.
1324 * @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL <
1330 ExConvertExclusiveToSharedLite(PERESOURCE Resource
)
1334 DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource
);
1336 /* Lock the resource */
1337 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1340 ASSERT(KeIsExecutingDpc() == FALSE
);
1341 ExpVerifyResource(Resource
);
1342 ASSERT(IsOwnedExclusive(Resource
));
1343 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== (ERESOURCE_THREAD
)PsGetCurrentThread());
1345 /* Erase the exclusive flag */
1346 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1348 /* Check if we have shared waiters */
1349 OldWaiters
= Resource
->NumberOfSharedWaiters
;
1352 /* Make the waiters active owners */
1353 Resource
->ActiveCount
= Resource
->ActiveCount
+ (USHORT
)OldWaiters
;
1354 Resource
->NumberOfSharedWaiters
= 0;
1356 /* Release lock and wake the waiters */
1357 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1358 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, OldWaiters
, FALSE
);
1363 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1368 * @name ExDeleteResourceLite
1371 * The ExConvertExclusiveToSharedLite routine deletes a given resource
1372 * from the system\92s resource list.
1375 * Pointer to the resource to delete.
1377 * @return STATUS_SUCCESS if the resource was deleted.
1379 * @remarks Callers of ExDeleteResourceLite must be running at IRQL <
1385 ExDeleteResourceLite(PERESOURCE Resource
)
1388 DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource
);
1391 ASSERT(IsSharedWaiting(Resource
) == FALSE
);
1392 ASSERT(IsExclusiveWaiting(Resource
) == FALSE
);
1393 ASSERT(KeIsExecutingDpc() == FALSE
);
1394 ExpVerifyResource(Resource
);
1396 /* Lock the resource */
1397 KeAcquireSpinLock(&ExpResourceSpinLock
, &OldIrql
);
1399 /* Remove the resource */
1400 RemoveEntryList(&Resource
->SystemResourcesList
);
1402 /* Release the lock */
1403 KeReleaseSpinLock(&ExpResourceSpinLock
, OldIrql
);
1405 /* Free every structure */
1406 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
1407 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
1408 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
1410 /* Return success */
1411 return STATUS_SUCCESS
;
1415 * @name ExDisableResourceBoostLite
1418 * The ExDisableResourceBoostLite routine disables thread boosting for
1419 * the given resource.
1422 * Pointer to the resource whose thread boosting will be disabled.
1431 ExDisableResourceBoostLite(PERESOURCE Resource
)
1436 ExpVerifyResource(Resource
);
1438 /* Lock the resource */
1439 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1441 /* Remove the flag */
1442 Resource
->Flag
|= ResourceHasDisabledPriorityBoost
;
1444 /* Release the lock */
1445 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1449 * @name ExGetExclusiveWaiterCount
1452 * The ExGetExclusiveWaiterCount routine returns the number of exclusive
1453 * waiters for the given resource.
1456 * Pointer to the resource to check.
1458 * @return The number of exclusive waiters.
1465 ExGetExclusiveWaiterCount(PERESOURCE Resource
)
1467 /* Return the count */
1468 return Resource
->NumberOfExclusiveWaiters
;
1472 * @name ExGetSharedWaiterCount
1475 * The ExGetSharedWaiterCount routine returns the number of shared
1476 * waiters for the given resource.
1479 * Pointer to the resource to check.
1481 * @return The number of shared waiters.
1488 ExGetSharedWaiterCount(PERESOURCE Resource
)
1490 /* Return the count */
1491 return Resource
->NumberOfSharedWaiters
;
1495 * @name ExInitializeResourceLite
1498 * The ExInitializeResourceLite routine initializes a resource variable.
1501 * Pointer to the resource to check.
1503 * @return STATUS_SUCCESS.
1505 * @remarks The storage for ERESOURCE must not be allocated from paged pool.
1507 * The storage must be 8-byte aligned.
1512 ExInitializeResourceLite(PERESOURCE Resource
)
1514 DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource
);
1516 /* Clear the structure */
1517 RtlZeroMemory(Resource
, sizeof(ERESOURCE
));
1519 /* Initialize the lock */
1520 KeInitializeSpinLock(&Resource
->SpinLock
);
1522 /* Add it into the system list */
1523 ExInterlockedInsertTailList(&ExpSystemResourcesList
,
1524 &Resource
->SystemResourcesList
,
1525 &ExpResourceSpinLock
);
1527 /* Return success */
1528 return STATUS_SUCCESS
;
1532 * @name ExIsResourceAcquiredExclusiveLite
1535 * The ExIsResourceAcquiredExclusiveLite routine returns whether the
1536 * current thread has exclusive access to a given resource.
1539 * Pointer to the resource to check.
1541 * @return TRUE if the caller already has exclusive access to the given resource.
1543 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1544 * IRQL <= DISPATCH_LEVEL.
1549 ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource
)
1551 ERESOURCE_THREAD Thread
= ExGetCurrentResourceThread();
1552 BOOLEAN IsAcquired
= FALSE
;
1555 ExpVerifyResource(Resource
);
1557 /* Check if it's exclusively acquired */
1558 if ((IsOwnedExclusive(Resource
)) &&
1559 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
1561 /* It is acquired */
1565 /* Return if it's acquired */
1570 * @name ExIsResourceAcquiredSharedLite
1573 * The ExIsResourceAcquiredSharedLite routine returns whether the
1574 * current thread has has access (either shared or exclusive) to a
1578 * Pointer to the resource to check.
1580 * @return Number of times the caller has acquired the given resource for
1581 * shared or exclusive access.
1583 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1584 * IRQL <= DISPATCH_LEVEL.
1589 ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource
)
1591 ERESOURCE_THREAD Thread
;
1598 ExpVerifyResource(Resource
);
1600 /* Get the thread */
1601 Thread
= ExGetCurrentResourceThread();
1603 /* Check if we are in the thread list */
1604 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1606 /* Found it, return count */
1607 Count
= Resource
->OwnerThreads
[0].OwnerCount
;
1609 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
1611 /* Found it on the second list, return count */
1612 Count
= Resource
->OwnerThreads
[1].OwnerCount
;
1616 /* Not in the list, do a full table look up */
1617 Owner
= Resource
->OwnerTable
;
1620 /* Lock the resource */
1621 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1623 /* Get the resource index */
1624 i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
1625 Size
= Owner
->TableSize
;
1627 /* Check if the index is valid and check if we don't match */
1628 if ((i
>= Size
) || (Owner
[i
].OwnerThread
!= Thread
))
1630 /* Sh*t! We need to do a full search */
1631 for (i
= 1; i
< Size
; i
++)
1633 /* Move to next owner */
1636 /* Try to find a match */
1637 if (Owner
->OwnerThread
== Thread
)
1640 Count
= Owner
->OwnerCount
;
1647 /* We found the match directlry */
1648 Count
= Owner
[i
].OwnerCount
;
1651 /* Release the lock */
1652 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1661 * @name ExReinitializeResourceLite
1664 * The ExReinitializeResourceLite routine routine reinitializes
1665 * an existing resource variable.
1668 * Pointer to the resource to be reinitialized.
1670 * @return STATUS_SUCCESS.
1672 * @remarks With a single call to ExReinitializeResource, a driver writer can
1673 * replace three calls: one to ExDeleteResourceLite, another to
1674 * ExAllocatePool, and a third to ExInitializeResourceLite. As
1675 * contention for a resource variable increases, memory is dynamically
1676 * allocated and attached to the resource in order to track this
1677 * contention. As an optimization, ExReinitializeResourceLite retains
1678 * and zeroes this previously allocated memory.
1680 * Callers of ExReinitializeResourceLite must be running at
1681 * IRQL <= DISPATCH_LEVEL.
1686 ExReinitializeResourceLite(PERESOURCE Resource
)
1689 PKSEMAPHORE Semaphore
;
1693 /* Get the owner table */
1694 Owner
= Resource
->OwnerTable
;
1697 /* Get the size and loop it */
1698 Size
= Owner
->TableSize
;
1699 for (i
= 0; i
< Size
; i
++)
1701 /* Zero the table */
1702 Owner
[i
].OwnerThread
= 0;
1703 Owner
[i
].OwnerCount
= 0;
1707 /* Zero the flags and count */
1709 Resource
->ActiveCount
= 0;
1711 /* Reset the semaphore */
1712 Semaphore
= Resource
->SharedWaiters
;
1713 if (Semaphore
) KeInitializeSemaphore(Semaphore
, 0, MAXLONG
);
1715 /* Reset the event */
1716 Event
= Resource
->ExclusiveWaiters
;
1717 if (Event
) KeInitializeEvent(Event
, SynchronizationEvent
, FALSE
);
1719 /* Clear the resource data */
1720 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1721 Resource
->OwnerThreads
[1].OwnerThread
= 0;
1722 Resource
->OwnerThreads
[0].OwnerCount
= 0;
1723 Resource
->OwnerThreads
[1].OwnerCount
= 0;
1724 Resource
->ContentionCount
= 0;
1725 Resource
->NumberOfSharedWaiters
= 0;
1726 Resource
->NumberOfExclusiveWaiters
= 0;
1728 /* Reset the spinlock */
1729 KeInitializeSpinLock(&Resource
->SpinLock
);
1730 return STATUS_SUCCESS
;
1734 * @name ExReleaseResourceLite
1737 * The ExReleaseResourceLite routine routine releases
1738 * a specified executive resource owned by the current thread.
1741 * Pointer to the resource to be released.
1745 * @remarks Callers of ExReleaseResourceLite must be running at
1746 * IRQL <= DISPATCH_LEVEL.
1751 ExReleaseResourceLite(PERESOURCE Resource
)
1753 ERESOURCE_THREAD Thread
;
1756 POWNER_ENTRY Owner
, Limit
;
1757 DPRINT("ExReleaseResourceLite: %p\n", Resource
);
1760 ExpVerifyResource(Resource
);
1762 /* Get the thread and lock the resource */
1763 Thread
= ExGetCurrentResourceThread();
1764 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1765 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
1767 /* Check if it's exclusively owned */
1768 if (IsOwnedExclusive(Resource
))
1770 /* Decrement owner count and check if we're done */
1771 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
1772 if (--Resource
->OwnerThreads
[0].OwnerCount
)
1774 /* Done, release lock! */
1775 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1779 /* Clear the owner */
1780 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1782 /* See if the resource isn't being owned anymore */
1783 ASSERT(Resource
->ActiveCount
> 0);
1784 if (!(--Resource
->ActiveCount
))
1786 /* Check if there are shared waiters */
1787 if (IsSharedWaiting(Resource
))
1789 /* Remove the exclusive flag */
1790 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1792 /* Give ownage to another thread */
1793 Count
= Resource
->NumberOfSharedWaiters
;
1794 Resource
->ActiveCount
= (USHORT
)Count
;
1795 Resource
->NumberOfSharedWaiters
= 0;
1797 /* Release lock and let someone else have it */
1798 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1799 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, Count
, FALSE
);
1802 else if (IsExclusiveWaiting(Resource
))
1804 /* Give exclusive access */
1805 Resource
->OwnerThreads
[0].OwnerThread
= 1;
1806 Resource
->OwnerThreads
[0].OwnerCount
= 1;
1807 Resource
->ActiveCount
= 1;
1808 Resource
->NumberOfExclusiveWaiters
--;
1810 /* Release the lock and give it away */
1811 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1812 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
1814 &Resource
->OwnerThreads
[0].OwnerThread
);
1818 /* Remove the exclusive flag */
1819 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1824 /* Check if we are in the thread list */
1825 if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
1827 /* Found it, get owner */
1828 Owner
= &Resource
->OwnerThreads
[1];
1830 else if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1832 /* Found it on the second list, get owner */
1833 Owner
= &Resource
->OwnerThreads
[0];
1837 /* Not in the list, do a full table look up */
1838 i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
1839 Owner
= Resource
->OwnerTable
;
1842 /* Nobody owns us, bugcheck! */
1843 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
1844 (ULONG_PTR
)Resource
,
1846 (ULONG_PTR
)Resource
->OwnerTable
,
1850 /* Check if we're out of the size and don't match */
1851 if ((i
>= Owner
->TableSize
) || (Owner
[i
].OwnerThread
!= Thread
))
1853 /* Get the last entry */
1854 Limit
= &Owner
[Owner
->TableSize
];
1857 /* Move to the next entry */
1860 /* Check if we don't match */
1863 /* Nobody owns us, bugcheck! */
1864 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
1865 (ULONG_PTR
)Resource
,
1867 (ULONG_PTR
)Resource
->OwnerTable
,
1871 /* Check for a match */
1872 if (Owner
->OwnerThread
== Thread
) break;
1877 /* Get the entry directly */
1883 ASSERT(Owner
->OwnerThread
== Thread
);
1884 ASSERT(Owner
->OwnerCount
> 0);
1886 /* Check if we are the last owner */
1887 if (--Owner
->OwnerCount
)
1890 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1895 Owner
->OwnerThread
= 0;
1897 /* See if the resource isn't being owned anymore */
1898 ASSERT(Resource
->ActiveCount
> 0);
1899 if (!(--Resource
->ActiveCount
))
1901 /* Check if there's an exclusive waiter */
1902 if (IsExclusiveWaiting(Resource
))
1904 /* Give exclusive access */
1905 Resource
->Flag
|= ResourceOwnedExclusive
;
1906 Resource
->OwnerThreads
[0].OwnerThread
= 1;
1907 Resource
->OwnerThreads
[0].OwnerCount
= 1;
1908 Resource
->ActiveCount
= 1;
1909 Resource
->NumberOfExclusiveWaiters
--;
1911 /* Release the lock and give it away */
1912 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1913 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
1915 &Resource
->OwnerThreads
[0].OwnerThread
);
1922 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1927 * @name ExReleaseResourceForThreadLite
1930 * The ExReleaseResourceForThreadLite routine routine releases
1931 * the input resource of the indicated thread.
1934 * Pointer to the resource to be released.
1937 * Identifies the thread that originally acquired the resource.
1941 * @remarks Callers of ExReleaseResourceForThreadLite must be running at
1942 * IRQL <= DISPATCH_LEVEL.
1947 ExReleaseResourceForThreadLite(PERESOURCE Resource
,
1948 ERESOURCE_THREAD Thread
)
1954 ASSERT(Thread
!= 0);
1955 DPRINT("ExReleaseResourceForThreadLite: %p\n", Resource
);
1957 /* Get the thread and lock the resource */
1958 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1961 ExpVerifyResource(Resource
);
1963 /* Check if it's exclusively owned */
1964 if (IsOwnedExclusive(Resource
))
1966 /* Decrement owner count and check if we're done */
1967 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== Thread
);
1968 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
1969 if (--Resource
->OwnerThreads
[0].OwnerCount
)
1971 /* Done, release lock! */
1972 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1976 /* Clear the owner */
1977 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1979 /* See if the resource isn't being owned anymore */
1980 ASSERT(Resource
->ActiveCount
> 0);
1981 if (!(--Resource
->ActiveCount
))
1983 /* Check if there are shared waiters */
1984 if (IsSharedWaiting(Resource
))
1986 /* Remove the exclusive flag */
1987 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1989 /* Give ownage to another thread */
1990 Count
= Resource
->NumberOfSharedWaiters
;
1991 Resource
->ActiveCount
= (USHORT
)Count
;
1992 Resource
->NumberOfSharedWaiters
= 0;
1994 /* Release lock and let someone else have it */
1995 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1996 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, Count
, FALSE
);
1999 else if (IsExclusiveWaiting(Resource
))
2001 /* Give exclusive access */
2002 Resource
->OwnerThreads
[0].OwnerThread
= 1;
2003 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2004 Resource
->ActiveCount
= 1;
2005 Resource
->NumberOfExclusiveWaiters
--;
2007 /* Release the lock and give it away */
2008 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2009 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
2011 &Resource
->OwnerThreads
[0].OwnerThread
);
2015 /* Remove the exclusive flag */
2016 Resource
->Flag
&= ~ResourceOwnedExclusive
;
2021 /* Check if we are in the thread list */
2022 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
2024 /* Found it, get owner */
2025 Owner
= &Resource
->OwnerThreads
[0];
2027 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
2029 /* Found it on the second list, get owner */
2030 Owner
= &Resource
->OwnerThreads
[1];
2034 /* Assume no valid index */
2037 /* If we got a valid pointer, try to get the resource index */
2038 if (!((ULONG
)Thread
& 3)) i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
2040 /* Do a table lookup */
2041 Owner
= Resource
->OwnerTable
;
2042 ASSERT(Owner
!= NULL
);
2044 /* Check if we're out of the size and don't match */
2045 if ((i
>= Owner
->TableSize
) || (Owner
[i
].OwnerThread
!= Thread
))
2047 /* Get the last entry */
2050 /* Move to the next entry */
2053 /* Check for a match */
2054 if (Owner
->OwnerThread
== Thread
) break;
2059 /* Get the entry directly */
2065 ASSERT(Owner
->OwnerThread
== Thread
);
2066 ASSERT(Owner
->OwnerCount
> 0);
2068 /* Check if we are the last owner */
2069 if (!(--Owner
->OwnerCount
))
2072 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2077 Owner
->OwnerThread
= 0;
2079 /* See if the resource isn't being owned anymore */
2080 ASSERT(Resource
->ActiveCount
> 0);
2081 if (!(--Resource
->ActiveCount
))
2083 /* Check if there's an exclusive waiter */
2084 if (IsExclusiveWaiting(Resource
))
2086 /* Give exclusive access */
2087 Resource
->OwnerThreads
[0].OwnerThread
= 1;
2088 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2089 Resource
->ActiveCount
= 1;
2090 Resource
->NumberOfExclusiveWaiters
--;
2092 /* Release the lock and give it away */
2093 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2094 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
2096 &Resource
->OwnerThreads
[0].OwnerThread
);
2103 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2108 * @name ExSetResourceOwnerPointer
2111 * The ExSetResourceOwnerPointer routine routine sets the owner thread
2112 * thread pointer for an executive resource.
2115 * Pointer to the resource whose owner to change.
2117 * @param OwnerPointer
2118 * Pointer to an owner thread pointer of type ERESOURCE_THREAD.
2122 * @remarks ExSetResourceOwnerPointer, used in conjunction with
2123 * ExReleaseResourceForThreadLite, provides a means for one thread
2124 * (acting as an resource manager thread) to acquire and release
2125 * resources for use by another thread (acting as a resource user
2128 * After calling ExSetResourceOwnerPointer for a specific resource,
2129 * the only other routine that can be called for that resource is
2130 * ExReleaseResourceForThreadLite.
2132 * Callers of ExSetResourceOwnerPointer must be running at
2133 * IRQL <= DISPATCH_LEVEL.
2138 ExSetResourceOwnerPointer(IN PERESOURCE Resource
,
2139 IN PVOID OwnerPointer
)
2141 ERESOURCE_THREAD Thread
;
2143 POWNER_ENTRY Owner
, ThisOwner
;
2146 ASSERT((OwnerPointer
!= 0) && (((ULONG_PTR
)OwnerPointer
& 3) == 3));
2148 /* Get the thread */
2149 Thread
= ExGetCurrentResourceThread();
2152 ExpVerifyResource(Resource
);
2154 /* Lock the resource */
2155 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
2157 /* Check if it's exclusive */
2158 if (IsOwnedExclusive(Resource
))
2160 /* If it's exclusive, set the first entry no matter what */
2161 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== Thread
);
2162 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
2163 Resource
->OwnerThreads
[0].OwnerThread
= (ULONG_PTR
)OwnerPointer
;
2167 /* Set the thread in both entries */
2168 ThisOwner
= ExpFindEntryForThread(Resource
,
2169 (ERESOURCE_THREAD
)OwnerPointer
,
2171 Owner
= ExpFindEntryForThread(Resource
, Thread
, 0);
2174 /* Nobody owns it, crash */
2175 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
2176 (ULONG_PTR
)Resource
,
2178 (ULONG_PTR
)Resource
->OwnerTable
,
2182 /* Set if we are the owner */
2186 ThisOwner
->OwnerCount
+= Owner
->OwnerCount
;
2187 Owner
->OwnerCount
= 0;
2188 Owner
->OwnerThread
= 0;
2189 ASSERT(Resource
->ActiveCount
>= 2);
2190 Resource
->ActiveCount
--;
2194 /* Release the resource */
2195 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2199 * @name ExTryToAcquireResourceExclusiveLite
2202 * The ExTryToAcquireResourceExclusiveLite routine routine attemps to
2203 * acquire the given resource for exclusive access.
2206 * Pointer to the resource to be acquired.
2208 * @return TRUE if the given resource has been acquired for the caller.
2210 * @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at
2211 * IRQL < DISPATCH_LEVEL.
2216 ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource
)
2218 ERESOURCE_THREAD Thread
;
2220 BOOLEAN Acquired
= FALSE
;
2221 DPRINT("ExTryToAcquireResourceExclusiveLite: %p\n", Resource
);
2224 ASSERT((Resource
->Flag
& ResourceNeverExclusive
) == 0);
2226 /* Get the thread */
2227 Thread
= ExGetCurrentResourceThread();
2229 /* Sanity check and validation */
2230 ASSERT(KeIsExecutingDpc() == FALSE
);
2231 ExpVerifyResource(Resource
);
2233 /* Acquire the lock */
2234 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
2236 /* Check if there is an owner */
2237 if (!Resource
->ActiveCount
)
2239 /* No owner, give exclusive access */
2240 Resource
->Flag
|= ResourceOwnedExclusive
;
2241 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
2242 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2243 Resource
->ActiveCount
= 1;
2246 else if ((IsOwnedExclusive(Resource
)) &&
2247 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
2249 /* Do a recursive acquire */
2250 Resource
->OwnerThreads
[0].OwnerCount
++;
2254 /* Release the resource */
2255 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2260 * @name ExEnterCriticalRegionAndAcquireResourceExclusive
2261 * @implemented NT5.1
2263 * The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical
2264 * region and then exclusively acquires a resource.
2267 * Pointer to the resource to acquire.
2269 * @return Pointer to the Win32K thread pointer of the current thread.
2271 * @remarks See ExAcquireResourceExclusiveLite.
2276 ExEnterCriticalRegionAndAcquireResourceExclusive(PERESOURCE Resource
)
2278 /* Enter critical region */
2279 KeEnterCriticalRegion();
2281 /* Acquire the resource */
2282 ExAcquireResourceExclusiveLite(Resource
, TRUE
);
2284 /* Return the Win32 Thread */
2285 return KeGetCurrentThread()->Win32Thread
;
2289 * @name ExReleaseResourceAndLeaveCriticalRegion
2290 * @implemented NT5.1
2292 * The ExReleaseResourceAndLeaveCriticalRegion release a resource and
2293 * then leaves a critical region.
2296 * Pointer to the resource to release.
2300 * @remarks See ExReleaseResourceLite.
2305 ExReleaseResourceAndLeaveCriticalRegion(PERESOURCE Resource
)
2307 /* Release the resource */
2308 ExReleaseResourceLite(Resource
);
2310 /* Leave critical region */
2311 KeLeaveCriticalRegion();