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.
541 * Disabled, read the comments bellow.
545 ExpBoostOwnerThread(IN PKTHREAD Thread
,
546 IN PKTHREAD OwnerThread
)
548 BOOLEAN Released
= FALSE
;
549 DPRINT("ExpBoostOwnerThread: %p\n", Thread
);
551 /* Make sure the owner thread is a pointer, not an ID */
552 if (!((ULONG_PTR
)OwnerThread
& 0x3))
554 /* Check if we can actually boost it */
555 if ((OwnerThread
->Priority
< Thread
->Priority
) &&
556 (OwnerThread
->Priority
< 14))
558 /* Make sure we're at dispatch */
559 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
561 /* Set the new priority */
562 OwnerThread
->PriorityDecrement
+= 14 - OwnerThread
->Priority
;
565 OwnerThread
->Quantum
= OwnerThread
->QuantumReset
;
567 /* Update the kernel state */
568 KiSetPriorityThread(OwnerThread
, 14, &Released
);
570 /* Reacquire lock if it got releases */
571 if (Released
) KeAcquireDispatcherDatabaseLockFromDpcLevel();
573 /* Make sure we're still at dispatch */
574 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL
);
581 * @name ExpWaitForResource
583 * The ExpWaitForResource routine performs a wait on the specified resource.
586 * Pointer to the resource to wait on.
589 * Pointer to object (exclusive event or shared semaphore) to wait on.
598 ExpWaitForResource(IN PERESOURCE Resource
,
602 ULONG Size
, WaitCount
= 0;
606 LARGE_INTEGER Timeout
;
608 /* Increase contention count and use a 5 second timeout */
609 Resource
->ContentionCount
++;
610 Timeout
.QuadPart
= 500 * -10000;
613 /* Wait for ownership */
614 Status
= KeWaitForSingleObject(Object
,
619 if (Status
!= STATUS_TIMEOUT
) break;
621 /* Increase wait count */
623 Timeout
= ExpTimeout
;
625 /* Check if we've exceeded the limit */
626 if (WaitCount
> ExpResourceTimeoutCount
)
628 /* Reset wait count */
631 /* Lock the resource */
632 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
634 /* Dump debug information */
635 DPRINT1("Resource @ %lx\n", Resource
);
636 DPRINT1(" ActiveCount = %04lx Flags = %s%s%s\n",
637 Resource
->ActiveCount
,
638 IsOwnedExclusive(Resource
) ? "IsOwnedExclusive " : "",
639 IsSharedWaiting(Resource
) ? "SharedWaiter " : "",
640 IsExclusiveWaiting(Resource
) ? "ExclusiveWaiter " : "");
641 DPRINT1(" NumberOfExclusiveWaiters = %04lx\n",
642 Resource
->NumberOfExclusiveWaiters
);
643 DPRINT1(" Thread = %08lx, Count = %02x\n",
644 Resource
->OwnerThreads
[0].OwnerThread
,
645 Resource
->OwnerThreads
[0].OwnerCount
);
646 DPRINT1(" Thread = %08lx, Count = %02x\n",
647 Resource
->OwnerThreads
[1].OwnerThread
,
648 Resource
->OwnerThreads
[1].OwnerCount
);
650 /* Dump out the table too */
651 Owner
= Resource
->OwnerTable
;
654 /* Loop every entry */
655 Size
= Owner
->TableSize
;
656 for (i
= 1; i
< Size
; i
++)
660 DPRINT1(" Thread = %08lx, Count = %02x\n",
668 DPRINT1("EX - Rewaiting\n");
669 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
675 * - We cannot access the OwnerTable without locking the resource.
676 * - The shared waiters may wait also on the semaphore. It makes no sense to boost a waiting thread.
677 * - The thread header is initialized like KeWaitForSingleObject (?, ?, ?, TRUE, ?).
678 * During the boost, possible the dispatcher lock is released but the thread block (WaitNext) isn't changed.
681 /* Check if we can boost */
682 if (!(Resource
->Flag
& ResourceHasDisabledPriorityBoost
))
684 PKTHREAD Thread
, OwnerThread
;
686 /* Get the current kernel thread and lock the dispatcher */
687 Thread
= KeGetCurrentThread();
688 Thread
->WaitIrql
= KeAcquireDispatcherDatabaseLock();
689 Thread
->WaitNext
= TRUE
;
691 /* Get the owner thread and boost it */
692 OwnerThread
= (PKTHREAD
)Resource
->OwnerThreads
[0].OwnerThread
;
693 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
695 /* If it's a shared resource */
696 if (!IsOwnedExclusive(Resource
))
698 /* Boost the other owner thread too */
699 OwnerThread
= (PKTHREAD
)Resource
->OwnerThreads
[1].OwnerThread
;
700 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
703 Owner
= Resource
->OwnerTable
;
706 /* Loop every entry */
707 Size
= Owner
->TableSize
;
708 for (i
= 1; i
< Size
; i
++)
710 /* Move to next entry */
714 OwnerThread
= (PKTHREAD
)OwnerEntry
->OwnerThread
;
717 if (OwnerThread
) ExpBoostOwnerThread(Thread
, OwnerThread
);
726 /* FUNCTIONS *****************************************************************/
729 * @name ExAcquireResourceExclusiveLite
732 * The ExAcquireResourceExclusiveLite routine acquires the given resource
733 * for exclusive access by the calling thread.
736 * Pointer to the resource to acquire.
739 * Specifies the routine's behavior whenever the resource cannot be
740 * acquired immediately.
742 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
743 * and exclusive access cannot be granted immediately.
745 * @remarks The caller can release the resource by calling either
746 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
748 * Normal kernel APC delivery must be disabled before calling this
749 * routine. Disable normal kernel APC delivery by calling
750 * KeEnterCriticalRegion. Delivery must remain disabled until the
751 * resource is released, at which point it can be reenabled by calling
752 * KeLeaveCriticalRegion.
754 * For better performance, call ExTryToAcquireResourceExclusiveLite,
755 * rather than calling ExAcquireResourceExclusiveLite with Wait set
758 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
764 ExAcquireResourceExclusiveLite(PERESOURCE Resource
,
767 KIRQL OldIrql
= PASSIVE_LEVEL
;
768 ERESOURCE_THREAD Thread
;
772 ASSERT((Resource
->Flag
& ResourceNeverExclusive
) == 0);
775 Thread
= ExGetCurrentResourceThread();
777 /* Sanity check and validation */
778 ASSERT(KeIsExecutingDpc() == FALSE
);
779 ExpVerifyResource(Resource
);
781 /* Acquire the lock */
782 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
783 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
785 /* Check if there is a shared owner or exclusive owner */
787 DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n",
789 if (Resource
->ActiveCount
)
791 /* Check if it's exclusively owned, and we own it */
792 if ((IsOwnedExclusive(Resource
)) &&
793 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
795 /* Increase the owning count */
796 Resource
->OwnerThreads
[0].OwnerCount
++;
802 * If the caller doesn't want us to wait, we can't acquire the
803 * resource because someone else then us owns it. If we can wait,
812 /* Check if it has exclusive waiters */
813 if (!Resource
->ExclusiveWaiters
)
815 /* It doesn't, allocate the event and try acquiring again */
816 ExpAllocateExclusiveWaiterEvent(Resource
, &OldIrql
);
821 /* Has exclusive waiters, wait on it */
822 Resource
->NumberOfExclusiveWaiters
++;
823 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
824 ExpWaitForResource(Resource
, Resource
->ExclusiveWaiters
);
826 /* Set owner and return success */
827 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
835 /* Nobody owns it, so let's! */
836 Resource
->Flag
|= ResourceOwnedExclusive
;
837 Resource
->ActiveCount
= 1;
838 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
839 Resource
->OwnerThreads
[0].OwnerCount
= 1;
843 /* Release the lock and return */
844 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
849 * @name ExAcquireResourceSharedLite
852 * The ExAcquireResourceSharedLite routine acquires the given resource
853 * for shared access by the calling thread.
856 * Pointer to the resource to acquire.
859 * Specifies the routine's behavior whenever the resource cannot be
860 * acquired immediately.
862 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
863 * and exclusive access cannot be granted immediately.
865 * @remarks The caller can release the resource by calling either
866 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
868 * Normal kernel APC delivery must be disabled before calling this
869 * routine. Disable normal kernel APC delivery by calling
870 * KeEnterCriticalRegion. Delivery must remain disabled until the
871 * resource is released, at which point it can be reenabled by calling
872 * KeLeaveCriticalRegion.
874 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
880 ExAcquireResourceSharedLite(PERESOURCE Resource
,
884 ERESOURCE_THREAD Thread
;
888 Thread
= ExGetCurrentResourceThread();
890 /* Sanity check and validation */
891 ASSERT(KeIsExecutingDpc() == FALSE
);
892 ExpVerifyResource(Resource
);
894 /* Acquire the lock */
895 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
896 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
898 /* See if nobody owns us */
900 DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n",
902 if (!Resource
->ActiveCount
)
904 if (Resource
->NumberOfSharedWaiters
== 0)
906 Owner
= &Resource
->OwnerThreads
[1];
910 /* Find a free entry */
911 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
912 if (!Owner
) goto TryAcquire
;
915 Owner
->OwnerThread
= Thread
;
916 Owner
->OwnerCount
= 1;
917 Resource
->ActiveCount
= 1;
919 /* Release the lock and return */
920 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
924 /* Check if it's exclusively owned */
925 if (IsOwnedExclusive(Resource
))
927 /* Check if we own it */
928 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
930 /* Increase the owning count */
931 Resource
->OwnerThreads
[0].OwnerCount
++;
933 /* Release the lock and return */
934 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
938 /* Find a free entry */
939 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
940 if (!Owner
) goto TryAcquire
;
944 /* Resource is shared, find who owns it */
945 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
946 if (!Owner
) goto TryAcquire
;
949 if (Owner
->OwnerThread
== Thread
)
951 /* Increase acquire count and return */
953 ASSERT(Owner
->OwnerCount
!= 0);
955 /* Release the lock and return */
956 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
960 /* Try to find if there are exclusive waiters */
961 if (!IsExclusiveWaiting(Resource
))
963 /* There are none, so acquire it */
964 Owner
->OwnerThread
= Thread
;
965 Owner
->OwnerCount
= 1;
966 Resource
->ActiveCount
++;
968 /* Release the lock and return */
969 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
974 /* If we got here, then we need to wait. Are we allowed? */
977 /* Release the lock and return */
978 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
982 /* Check if we have a shared waiters semaphore */
983 if (!Resource
->SharedWaiters
)
985 /* Allocate it and try another acquire */
986 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
990 /* Now wait for the resource */
991 Owner
->OwnerThread
= Thread
;
992 Owner
->OwnerCount
= 1;
993 Resource
->NumberOfSharedWaiters
++;
995 /* Release the lock and return */
996 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
997 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1002 * @name ExAcquireSharedStarveExclusive
1005 * The ExAcquireSharedStarveExclusive routine acquires the given resource
1006 * shared access without waiting for any pending attempts to acquire
1007 * exclusive access to the same resource.
1010 * Pointer to the resource to acquire.
1013 * Specifies the routine's behavior whenever the resource cannot be
1014 * acquired immediately.
1016 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1017 * and exclusive access cannot be granted immediately.
1019 * @remarks The caller can release the resource by calling either
1020 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1022 * Normal kernel APC delivery must be disabled before calling this
1023 * routine. Disable normal kernel APC delivery by calling
1024 * KeEnterCriticalRegion. Delivery must remain disabled until the
1025 * resource is released, at which point it can be reenabled by calling
1026 * KeLeaveCriticalRegion.
1028 * Callers of ExAcquireSharedStarveExclusive usually need quick access
1029 * to a shared resource in order to save an exclusive accessor from
1030 * doing redundant work. For example, a file system might call this
1031 * routine to modify a cached resource, such as a BCB pinned in the
1032 * cache, before the Cache Manager can acquire exclusive access to the
1033 * resource and write the cache out to disk.
1035 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1041 ExAcquireSharedStarveExclusive(PERESOURCE Resource
,
1045 ERESOURCE_THREAD Thread
;
1048 /* Get the thread */
1049 Thread
= ExGetCurrentResourceThread();
1051 /* Sanity check and validation */
1052 ASSERT(KeIsExecutingDpc() == FALSE
);
1053 ExpVerifyResource(Resource
);
1055 /* Acquire the lock */
1056 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1058 /* See if nobody owns us */
1060 DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n",
1062 if (!Resource
->ActiveCount
)
1064 /* Nobody owns it, so let's take control */
1065 Resource
->ActiveCount
= 1;
1066 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
1067 Resource
->OwnerThreads
[1].OwnerCount
= 1;
1069 /* Release the lock and return */
1070 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1074 /* Check if it's exclusively owned */
1075 if (IsOwnedExclusive(Resource
))
1077 /* Check if we own it */
1078 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1080 /* Increase the owning count */
1081 Resource
->OwnerThreads
[0].OwnerCount
++;
1083 /* Release the lock and return */
1084 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1088 /* Find a free entry */
1089 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
1090 if (!Owner
) goto TryAcquire
;
1092 /* If we got here, then we need to wait. Are we allowed? */
1095 /* Release the lock and return */
1096 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1100 /* Check if we have a shared waiters semaphore */
1101 if (!Resource
->SharedWaiters
)
1103 /* Allocate one and try again */
1104 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1110 /* Resource is shared, find who owns it */
1111 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1112 if (!Owner
) goto TryAcquire
;
1115 if (Owner
->OwnerThread
== Thread
)
1117 /* Increase acquire count and return */
1118 Owner
->OwnerCount
++;
1119 ASSERT(Owner
->OwnerCount
!= 0);
1121 /* Release the lock and return */
1122 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1127 Owner
->OwnerThread
= Thread
;
1128 Owner
->OwnerCount
= 1;
1129 Resource
->ActiveCount
++;
1131 /* Release the lock and return */
1132 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1136 /* If we got here, then we need to wait. Are we allowed? */
1139 /* Release the lock and return */
1140 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1144 /* Now wait for the resource */
1145 Owner
->OwnerThread
= Thread
;
1146 Owner
->OwnerCount
= 1;
1147 Resource
->NumberOfSharedWaiters
++;
1149 /* Release the lock and return */
1150 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1151 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1156 * @name ExAcquireSharedWaitForExclusive
1159 * The ExAcquireSharedWaitForExclusive routine acquires the given resource
1160 * for shared access if shared access can be granted and there are no
1161 * exclusive waiters.
1164 * Pointer to the resource to acquire.
1167 * Specifies the routine's behavior whenever the resource cannot be
1168 * acquired immediately.
1170 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1171 * and exclusive access cannot be granted immediately.
1173 * @remarks The caller can release the resource by calling either
1174 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1176 * Normal kernel APC delivery must be disabled before calling this
1177 * routine. Disable normal kernel APC delivery by calling
1178 * KeEnterCriticalRegion. Delivery must remain disabled until the
1179 * resource is released, at which point it can be reenabled by calling
1180 * KeLeaveCriticalRegion.
1182 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1188 ExAcquireSharedWaitForExclusive(PERESOURCE Resource
,
1192 ERESOURCE_THREAD Thread
;
1195 /* Get the thread */
1196 Thread
= ExGetCurrentResourceThread();
1198 /* Sanity check and validation */
1199 ASSERT(KeIsExecutingDpc() == FALSE
);
1200 ExpVerifyResource(Resource
);
1202 /* Acquire the lock */
1203 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1205 /* See if nobody owns us */
1207 DPRINT("ExAcquireSharedWaitForExclusive(Resource 0x%p, Wait %d)\n",
1209 if (!Resource
->ActiveCount
)
1211 /* Nobody owns it, so let's take control */
1212 Resource
->ActiveCount
= 1;
1213 Resource
->OwnerThreads
[1].OwnerThread
= Thread
;
1214 Resource
->OwnerThreads
[1].OwnerCount
= 1;
1216 /* Release the lock and return */
1217 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1221 /* Check if it's exclusively owned */
1222 if (IsOwnedExclusive(Resource
))
1224 /* Check if we own it */
1225 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1227 /* Increase the owning count */
1228 Resource
->OwnerThreads
[0].OwnerCount
++;
1230 /* Release the lock and return */
1231 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1236 /* Find a free entry */
1237 Owner
= ExpFindFreeEntry(Resource
, &OldIrql
);
1238 if (!Owner
) goto TryAcquire
;
1240 /* If we got here, then we need to wait. Are we allowed? */
1243 /* Release the lock and return */
1244 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1248 /* Check if we have a shared waiters semaphore */
1249 if (!Resource
->SharedWaiters
)
1251 /* Allocate one and try again */
1252 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1256 /* Now take control of the resource */
1257 Owner
->OwnerThread
= Thread
;
1258 Owner
->OwnerCount
= 1;
1259 Resource
->NumberOfSharedWaiters
++;
1261 /* Release the lock and wait for it to be ours */
1262 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1263 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1269 /* Try to find if there are exclusive waiters */
1270 if (IsExclusiveWaiting(Resource
))
1272 /* We have to wait for the exclusive waiter to be done */
1275 /* So bail out if we're not allowed */
1276 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1280 /* Check if we have a shared waiters semaphore */
1281 if (!Resource
->SharedWaiters
)
1283 /* Allocate one and try again */
1284 ExpAllocateSharedWaiterSemaphore(Resource
, &OldIrql
);
1288 /* Now wait for the resource */
1289 Resource
->NumberOfSharedWaiters
++;
1290 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1291 ExpWaitForResource(Resource
, Resource
->SharedWaiters
);
1293 /* Get the lock back */
1294 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1296 /* Find who owns it now */
1297 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1300 ASSERT(IsOwnedExclusive(Resource
) == FALSE
);
1301 ASSERT(Resource
->ActiveCount
> 0);
1302 ASSERT(Owner
->OwnerThread
!= Thread
);
1305 Owner
->OwnerThread
= Thread
;
1306 Owner
->OwnerCount
= 1;
1308 /* Release the lock and return */
1309 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1314 /* Resource is shared, find who owns it */
1315 Owner
= ExpFindEntryForThread(Resource
, Thread
, &OldIrql
);
1316 if (!Owner
) goto TryAcquire
;
1319 if (Owner
->OwnerThread
== Thread
)
1321 /* Increase acquire count and return */
1322 Owner
->OwnerCount
++;
1323 ASSERT(Owner
->OwnerCount
!= 0);
1325 /* Release the lock and return */
1326 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1330 /* No exclusive waiters, so acquire it */
1331 Owner
->OwnerThread
= Thread
;
1332 Owner
->OwnerCount
= 1;
1333 Resource
->ActiveCount
++;
1335 /* Release the lock and return */
1336 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1343 * @name ExConvertExclusiveToSharedLite
1346 * The ExConvertExclusiveToSharedLite routine converts an exclusively
1347 * acquired resource into a resource that can be acquired shared.
1350 * Pointer to the resource to convert.
1354 * @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL <
1360 ExConvertExclusiveToSharedLite(PERESOURCE Resource
)
1364 DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource
);
1366 /* Lock the resource */
1367 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1370 ASSERT(KeIsExecutingDpc() == FALSE
);
1371 ExpVerifyResource(Resource
);
1372 ASSERT(IsOwnedExclusive(Resource
));
1373 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== (ERESOURCE_THREAD
)PsGetCurrentThread());
1375 /* Erase the exclusive flag */
1376 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1378 /* Check if we have shared waiters */
1379 OldWaiters
= Resource
->NumberOfSharedWaiters
;
1382 /* Make the waiters active owners */
1383 Resource
->ActiveCount
= Resource
->ActiveCount
+ (USHORT
)OldWaiters
;
1384 Resource
->NumberOfSharedWaiters
= 0;
1386 /* Release lock and wake the waiters */
1387 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1388 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, OldWaiters
, FALSE
);
1393 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1398 * @name ExDeleteResourceLite
1401 * The ExConvertExclusiveToSharedLite routine deletes a given resource
1402 * from the system\92s resource list.
1405 * Pointer to the resource to delete.
1407 * @return STATUS_SUCCESS if the resource was deleted.
1409 * @remarks Callers of ExDeleteResourceLite must be running at IRQL <
1415 ExDeleteResourceLite(PERESOURCE Resource
)
1418 DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource
);
1421 ASSERT(IsSharedWaiting(Resource
) == FALSE
);
1422 ASSERT(IsExclusiveWaiting(Resource
) == FALSE
);
1423 ASSERT(KeIsExecutingDpc() == FALSE
);
1424 ExpVerifyResource(Resource
);
1426 /* Lock the resource */
1427 KeAcquireSpinLock(&ExpResourceSpinLock
, &OldIrql
);
1429 /* Remove the resource */
1430 RemoveEntryList(&Resource
->SystemResourcesList
);
1432 /* Release the lock */
1433 KeReleaseSpinLock(&ExpResourceSpinLock
, OldIrql
);
1435 /* Free every structure */
1436 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
1437 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
1438 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
1440 /* Return success */
1441 return STATUS_SUCCESS
;
1445 * @name ExDisableResourceBoostLite
1448 * The ExDisableResourceBoostLite routine disables thread boosting for
1449 * the given resource.
1452 * Pointer to the resource whose thread boosting will be disabled.
1461 ExDisableResourceBoostLite(PERESOURCE Resource
)
1466 ExpVerifyResource(Resource
);
1468 /* Lock the resource */
1469 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1471 /* Remove the flag */
1472 Resource
->Flag
|= ResourceHasDisabledPriorityBoost
;
1474 /* Release the lock */
1475 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1479 * @name ExGetExclusiveWaiterCount
1482 * The ExGetExclusiveWaiterCount routine returns the number of exclusive
1483 * waiters for the given resource.
1486 * Pointer to the resource to check.
1488 * @return The number of exclusive waiters.
1495 ExGetExclusiveWaiterCount(PERESOURCE Resource
)
1497 /* Return the count */
1498 return Resource
->NumberOfExclusiveWaiters
;
1502 * @name ExGetSharedWaiterCount
1505 * The ExGetSharedWaiterCount routine returns the number of shared
1506 * waiters for the given resource.
1509 * Pointer to the resource to check.
1511 * @return The number of shared waiters.
1518 ExGetSharedWaiterCount(PERESOURCE Resource
)
1520 /* Return the count */
1521 return Resource
->NumberOfSharedWaiters
;
1525 * @name ExInitializeResourceLite
1528 * The ExInitializeResourceLite routine initializes a resource variable.
1531 * Pointer to the resource to check.
1533 * @return STATUS_SUCCESS.
1535 * @remarks The storage for ERESOURCE must not be allocated from paged pool.
1537 * The storage must be 8-byte aligned.
1542 ExInitializeResourceLite(PERESOURCE Resource
)
1544 DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource
);
1546 /* Clear the structure */
1547 RtlZeroMemory(Resource
, sizeof(ERESOURCE
));
1549 /* Initialize the lock */
1550 KeInitializeSpinLock(&Resource
->SpinLock
);
1552 /* Add it into the system list */
1553 ExInterlockedInsertTailList(&ExpSystemResourcesList
,
1554 &Resource
->SystemResourcesList
,
1555 &ExpResourceSpinLock
);
1557 /* Return success */
1558 return STATUS_SUCCESS
;
1562 * @name ExIsResourceAcquiredExclusiveLite
1565 * The ExIsResourceAcquiredExclusiveLite routine returns whether the
1566 * current thread has exclusive access to a given resource.
1569 * Pointer to the resource to check.
1571 * @return TRUE if the caller already has exclusive access to the given resource.
1573 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1574 * IRQL <= DISPATCH_LEVEL.
1579 ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource
)
1581 ERESOURCE_THREAD Thread
= ExGetCurrentResourceThread();
1582 BOOLEAN IsAcquired
= FALSE
;
1585 ExpVerifyResource(Resource
);
1587 /* Check if it's exclusively acquired */
1588 if ((IsOwnedExclusive(Resource
)) &&
1589 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
1591 /* It is acquired */
1595 /* Return if it's acquired */
1600 * @name ExIsResourceAcquiredSharedLite
1603 * The ExIsResourceAcquiredSharedLite routine returns whether the
1604 * current thread has has access (either shared or exclusive) to a
1608 * Pointer to the resource to check.
1610 * @return Number of times the caller has acquired the given resource for
1611 * shared or exclusive access.
1613 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1614 * IRQL <= DISPATCH_LEVEL.
1619 ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource
)
1621 ERESOURCE_THREAD Thread
;
1628 ExpVerifyResource(Resource
);
1630 /* Get the thread */
1631 Thread
= ExGetCurrentResourceThread();
1633 /* Check if we are in the thread list */
1634 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
1636 /* Found it, return count */
1637 Count
= Resource
->OwnerThreads
[0].OwnerCount
;
1639 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
1641 /* Found it on the second list, return count */
1642 Count
= Resource
->OwnerThreads
[1].OwnerCount
;
1646 /* Not in the list, do a full table look up */
1647 Owner
= Resource
->OwnerTable
;
1650 /* Lock the resource */
1651 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1653 /* Get the resource index */
1654 i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
1655 Size
= Owner
->TableSize
;
1657 /* Check if the index is valid and check if we don't match */
1658 if ((i
>= Size
) || (Owner
[i
].OwnerThread
!= Thread
))
1660 /* Sh*t! We need to do a full search */
1661 for (i
= 1; i
< Size
; i
++)
1663 /* Move to next owner */
1666 /* Try to find a match */
1667 if (Owner
->OwnerThread
== Thread
)
1670 Count
= Owner
->OwnerCount
;
1677 /* We found the match directlry */
1678 Count
= Owner
[i
].OwnerCount
;
1681 /* Release the lock */
1682 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1691 * @name ExReinitializeResourceLite
1694 * The ExReinitializeResourceLite routine routine reinitializes
1695 * an existing resource variable.
1698 * Pointer to the resource to be reinitialized.
1700 * @return STATUS_SUCCESS.
1702 * @remarks With a single call to ExReinitializeResource, a driver writer can
1703 * replace three calls: one to ExDeleteResourceLite, another to
1704 * ExAllocatePool, and a third to ExInitializeResourceLite. As
1705 * contention for a resource variable increases, memory is dynamically
1706 * allocated and attached to the resource in order to track this
1707 * contention. As an optimization, ExReinitializeResourceLite retains
1708 * and zeroes this previously allocated memory.
1710 * Callers of ExReinitializeResourceLite must be running at
1711 * IRQL <= DISPATCH_LEVEL.
1716 ExReinitializeResourceLite(PERESOURCE Resource
)
1719 PKSEMAPHORE Semaphore
;
1723 /* Get the owner table */
1724 Owner
= Resource
->OwnerTable
;
1727 /* Get the size and loop it */
1728 Size
= Owner
->TableSize
;
1729 for (i
= 0; i
< Size
; i
++)
1731 /* Zero the table */
1732 Owner
[i
].OwnerThread
= 0;
1733 Owner
[i
].OwnerCount
= 0;
1737 /* Zero the flags and count */
1739 Resource
->ActiveCount
= 0;
1741 /* Reset the semaphore */
1742 Semaphore
= Resource
->SharedWaiters
;
1743 if (Semaphore
) KeInitializeSemaphore(Semaphore
, 0, MAXLONG
);
1745 /* Reset the event */
1746 Event
= Resource
->ExclusiveWaiters
;
1747 if (Event
) KeInitializeEvent(Event
, SynchronizationEvent
, FALSE
);
1749 /* Clear the resource data */
1750 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1751 Resource
->OwnerThreads
[1].OwnerThread
= 0;
1752 Resource
->OwnerThreads
[0].OwnerCount
= 0;
1753 Resource
->OwnerThreads
[1].OwnerCount
= 0;
1754 Resource
->ContentionCount
= 0;
1755 Resource
->NumberOfSharedWaiters
= 0;
1756 Resource
->NumberOfExclusiveWaiters
= 0;
1758 /* Reset the spinlock */
1759 KeInitializeSpinLock(&Resource
->SpinLock
);
1760 return STATUS_SUCCESS
;
1764 * @name ExReleaseResourceLite
1767 * The ExReleaseResourceLite routine routine releases
1768 * a specified executive resource owned by the current thread.
1771 * Pointer to the resource to be released.
1775 * @remarks Callers of ExReleaseResourceLite must be running at
1776 * IRQL <= DISPATCH_LEVEL.
1781 ExReleaseResourceLite(PERESOURCE Resource
)
1783 ERESOURCE_THREAD Thread
;
1786 POWNER_ENTRY Owner
, Limit
;
1787 DPRINT("ExReleaseResourceLite: %p\n", Resource
);
1790 ExpVerifyResource(Resource
);
1792 /* Get the thread and lock the resource */
1793 Thread
= ExGetCurrentResourceThread();
1794 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1795 ExpCheckForApcsDisabled(TRUE
, Resource
, (PETHREAD
)Thread
);
1797 /* Check if it's exclusively owned */
1798 if (IsOwnedExclusive(Resource
))
1800 /* Decrement owner count and check if we're done */
1801 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
1802 ASSERT(Resource
->ActiveCount
== 1);
1803 if (--Resource
->OwnerThreads
[0].OwnerCount
)
1805 /* Done, release lock! */
1806 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1810 /* Clear the owner */
1811 Resource
->OwnerThreads
[0].OwnerThread
= 0;
1813 /* Check if there are shared waiters */
1814 if (IsSharedWaiting(Resource
))
1816 /* Remove the exclusive flag */
1817 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1819 /* Give ownage to another thread */
1820 Count
= Resource
->NumberOfSharedWaiters
;
1821 Resource
->ActiveCount
= (USHORT
)Count
;
1822 Resource
->NumberOfSharedWaiters
= 0;
1824 /* Release lock and let someone else have it */
1825 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1826 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, Count
, FALSE
);
1829 else if (IsExclusiveWaiting(Resource
))
1831 /* Give exclusive access */
1832 Resource
->OwnerThreads
[0].OwnerThread
= 1;
1833 Resource
->OwnerThreads
[0].OwnerCount
= 1;
1834 Resource
->ActiveCount
= 1;
1835 Resource
->NumberOfExclusiveWaiters
--;
1837 /* Release the lock and give it away */
1838 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1839 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
1841 &Resource
->OwnerThreads
[0].OwnerThread
);
1845 /* Remove the exclusive flag */
1846 Resource
->Flag
&= ~ResourceOwnedExclusive
;
1848 Resource
->ActiveCount
= 0;
1852 /* Check if we are in the thread list */
1853 if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
1855 /* Found it, get owner */
1856 Owner
= &Resource
->OwnerThreads
[1];
1860 /* Not in the list, do a full table look up */
1861 i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
1862 Owner
= Resource
->OwnerTable
;
1865 /* Nobody owns us, bugcheck! */
1866 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
1867 (ULONG_PTR
)Resource
,
1869 (ULONG_PTR
)Resource
->OwnerTable
,
1873 /* Check if we're out of the size and don't match */
1874 if ((i
>= Owner
->TableSize
) || (Owner
[i
].OwnerThread
!= Thread
))
1876 /* Get the last entry */
1877 Limit
= &Owner
[Owner
->TableSize
];
1880 /* Move to the next entry */
1883 /* Check if we don't match */
1886 /* Nobody owns us, bugcheck! */
1887 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
1888 (ULONG_PTR
)Resource
,
1890 (ULONG_PTR
)Resource
->OwnerTable
,
1894 /* Check for a match */
1895 if (Owner
->OwnerThread
== Thread
) break;
1900 /* Get the entry directly */
1906 ASSERT(Owner
->OwnerThread
== Thread
);
1907 ASSERT(Owner
->OwnerCount
> 0);
1909 /* Check if we are the last owner */
1910 if (--Owner
->OwnerCount
)
1913 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1918 Owner
->OwnerThread
= 0;
1920 /* See if the resource isn't being owned anymore */
1921 ASSERT(Resource
->ActiveCount
> 0);
1922 if (!(--Resource
->ActiveCount
))
1924 /* Check if there's an exclusive waiter */
1925 if (IsExclusiveWaiting(Resource
))
1927 /* Give exclusive access */
1928 Resource
->Flag
|= ResourceOwnedExclusive
;
1929 Resource
->OwnerThreads
[0].OwnerThread
= 1;
1930 Resource
->OwnerThreads
[0].OwnerCount
= 1;
1931 Resource
->ActiveCount
= 1;
1932 Resource
->NumberOfExclusiveWaiters
--;
1934 /* Release the lock and give it away */
1935 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1936 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
1938 &Resource
->OwnerThreads
[0].OwnerThread
);
1945 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1950 * @name ExReleaseResourceForThreadLite
1953 * The ExReleaseResourceForThreadLite routine routine releases
1954 * the input resource of the indicated thread.
1957 * Pointer to the resource to be released.
1960 * Identifies the thread that originally acquired the resource.
1964 * @remarks Callers of ExReleaseResourceForThreadLite must be running at
1965 * IRQL <= DISPATCH_LEVEL.
1970 ExReleaseResourceForThreadLite(PERESOURCE Resource
,
1971 ERESOURCE_THREAD Thread
)
1977 ASSERT(Thread
!= 0);
1978 DPRINT("ExReleaseResourceForThreadLite: %p\n", Resource
);
1980 /* Get the thread and lock the resource */
1981 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
1984 ExpVerifyResource(Resource
);
1986 /* Check if it's exclusively owned */
1987 if (IsOwnedExclusive(Resource
))
1989 /* Decrement owner count and check if we're done */
1990 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== Thread
);
1991 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
1992 if (--Resource
->OwnerThreads
[0].OwnerCount
)
1994 /* Done, release lock! */
1995 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
1999 /* Clear the owner */
2000 Resource
->OwnerThreads
[0].OwnerThread
= 0;
2002 /* See if the resource isn't being owned anymore */
2003 ASSERT(Resource
->ActiveCount
> 0);
2004 if (!(--Resource
->ActiveCount
))
2006 /* Check if there are shared waiters */
2007 if (IsSharedWaiting(Resource
))
2009 /* Remove the exclusive flag */
2010 Resource
->Flag
&= ~ResourceOwnedExclusive
;
2012 /* Give ownage to another thread */
2013 Count
= Resource
->NumberOfSharedWaiters
;
2014 Resource
->ActiveCount
= (USHORT
)Count
;
2015 Resource
->NumberOfSharedWaiters
= 0;
2017 /* Release lock and let someone else have it */
2018 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2019 KeReleaseSemaphore(Resource
->SharedWaiters
, 0, Count
, FALSE
);
2022 else if (IsExclusiveWaiting(Resource
))
2024 /* Give exclusive access */
2025 Resource
->OwnerThreads
[0].OwnerThread
= 1;
2026 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2027 Resource
->ActiveCount
= 1;
2028 Resource
->NumberOfExclusiveWaiters
--;
2030 /* Release the lock and give it away */
2031 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2032 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
2034 &Resource
->OwnerThreads
[0].OwnerThread
);
2038 /* Remove the exclusive flag */
2039 Resource
->Flag
&= ~ResourceOwnedExclusive
;
2044 /* Check if we are in the thread list */
2045 if (Resource
->OwnerThreads
[0].OwnerThread
== Thread
)
2047 /* Found it, get owner */
2048 Owner
= &Resource
->OwnerThreads
[0];
2050 else if (Resource
->OwnerThreads
[1].OwnerThread
== Thread
)
2052 /* Found it on the second list, get owner */
2053 Owner
= &Resource
->OwnerThreads
[1];
2057 /* Assume no valid index */
2060 /* If we got a valid pointer, try to get the resource index */
2061 if (!((ULONG
)Thread
& 3)) i
= ((PKTHREAD
)Thread
)->ResourceIndex
;
2063 /* Do a table lookup */
2064 Owner
= Resource
->OwnerTable
;
2065 ASSERT(Owner
!= NULL
);
2067 /* Check if we're out of the size and don't match */
2068 if ((i
>= Owner
->TableSize
) || (Owner
[i
].OwnerThread
!= Thread
))
2070 /* Get the last entry */
2073 /* Move to the next entry */
2076 /* Check for a match */
2077 if (Owner
->OwnerThread
== Thread
) break;
2082 /* Get the entry directly */
2088 ASSERT(Owner
->OwnerThread
== Thread
);
2089 ASSERT(Owner
->OwnerCount
> 0);
2091 /* Check if we are the last owner */
2092 if (!(--Owner
->OwnerCount
))
2095 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2100 Owner
->OwnerThread
= 0;
2102 /* See if the resource isn't being owned anymore */
2103 ASSERT(Resource
->ActiveCount
> 0);
2104 if (!(--Resource
->ActiveCount
))
2106 /* Check if there's an exclusive waiter */
2107 if (IsExclusiveWaiting(Resource
))
2109 /* Give exclusive access */
2110 Resource
->OwnerThreads
[0].OwnerThread
= 1;
2111 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2112 Resource
->ActiveCount
= 1;
2113 Resource
->NumberOfExclusiveWaiters
--;
2115 /* Release the lock and give it away */
2116 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2117 KeSetEventBoostPriority(Resource
->ExclusiveWaiters
,
2119 &Resource
->OwnerThreads
[0].OwnerThread
);
2126 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2131 * @name ExSetResourceOwnerPointer
2134 * The ExSetResourceOwnerPointer routine routine sets the owner thread
2135 * thread pointer for an executive resource.
2138 * Pointer to the resource whose owner to change.
2140 * @param OwnerPointer
2141 * Pointer to an owner thread pointer of type ERESOURCE_THREAD.
2145 * @remarks ExSetResourceOwnerPointer, used in conjunction with
2146 * ExReleaseResourceForThreadLite, provides a means for one thread
2147 * (acting as an resource manager thread) to acquire and release
2148 * resources for use by another thread (acting as a resource user
2151 * After calling ExSetResourceOwnerPointer for a specific resource,
2152 * the only other routine that can be called for that resource is
2153 * ExReleaseResourceForThreadLite.
2155 * Callers of ExSetResourceOwnerPointer must be running at
2156 * IRQL <= DISPATCH_LEVEL.
2161 ExSetResourceOwnerPointer(IN PERESOURCE Resource
,
2162 IN PVOID OwnerPointer
)
2164 ERESOURCE_THREAD Thread
;
2166 POWNER_ENTRY Owner
, ThisOwner
;
2169 ASSERT((OwnerPointer
!= 0) && (((ULONG_PTR
)OwnerPointer
& 3) == 3));
2171 /* Get the thread */
2172 Thread
= ExGetCurrentResourceThread();
2175 ExpVerifyResource(Resource
);
2177 /* Lock the resource */
2178 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
2180 /* Check if it's exclusive */
2181 if (IsOwnedExclusive(Resource
))
2183 /* If it's exclusive, set the first entry no matter what */
2184 ASSERT(Resource
->OwnerThreads
[0].OwnerThread
== Thread
);
2185 ASSERT(Resource
->OwnerThreads
[0].OwnerCount
> 0);
2186 Resource
->OwnerThreads
[0].OwnerThread
= (ULONG_PTR
)OwnerPointer
;
2190 /* Set the thread in both entries */
2191 ThisOwner
= ExpFindEntryForThread(Resource
,
2192 (ERESOURCE_THREAD
)OwnerPointer
,
2194 Owner
= ExpFindEntryForThread(Resource
, Thread
, 0);
2197 /* Nobody owns it, crash */
2198 KEBUGCHECKEX(RESOURCE_NOT_OWNED
,
2199 (ULONG_PTR
)Resource
,
2201 (ULONG_PTR
)Resource
->OwnerTable
,
2205 /* Set if we are the owner */
2209 ThisOwner
->OwnerCount
+= Owner
->OwnerCount
;
2210 Owner
->OwnerCount
= 0;
2211 Owner
->OwnerThread
= 0;
2212 ASSERT(Resource
->ActiveCount
>= 2);
2213 Resource
->ActiveCount
--;
2217 /* Release the resource */
2218 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2222 * @name ExTryToAcquireResourceExclusiveLite
2225 * The ExTryToAcquireResourceExclusiveLite routine routine attemps to
2226 * acquire the given resource for exclusive access.
2229 * Pointer to the resource to be acquired.
2231 * @return TRUE if the given resource has been acquired for the caller.
2233 * @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at
2234 * IRQL < DISPATCH_LEVEL.
2239 ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource
)
2241 ERESOURCE_THREAD Thread
;
2243 BOOLEAN Acquired
= FALSE
;
2244 DPRINT("ExTryToAcquireResourceExclusiveLite: %p\n", Resource
);
2247 ASSERT((Resource
->Flag
& ResourceNeverExclusive
) == 0);
2249 /* Get the thread */
2250 Thread
= ExGetCurrentResourceThread();
2252 /* Sanity check and validation */
2253 ASSERT(KeIsExecutingDpc() == FALSE
);
2254 ExpVerifyResource(Resource
);
2256 /* Acquire the lock */
2257 ExAcquireResourceLock(&Resource
->SpinLock
, &OldIrql
);
2259 /* Check if there is an owner */
2260 if (!Resource
->ActiveCount
)
2262 /* No owner, give exclusive access */
2263 Resource
->Flag
|= ResourceOwnedExclusive
;
2264 Resource
->OwnerThreads
[0].OwnerThread
= Thread
;
2265 Resource
->OwnerThreads
[0].OwnerCount
= 1;
2266 Resource
->ActiveCount
= 1;
2269 else if ((IsOwnedExclusive(Resource
)) &&
2270 (Resource
->OwnerThreads
[0].OwnerThread
== Thread
))
2272 /* Do a recursive acquire */
2273 Resource
->OwnerThreads
[0].OwnerCount
++;
2277 /* Release the resource */
2278 ExReleaseResourceLock(&Resource
->SpinLock
, OldIrql
);
2283 * @name ExEnterCriticalRegionAndAcquireResourceExclusive
2284 * @implemented NT5.1
2286 * The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical
2287 * region and then exclusively acquires a resource.
2290 * Pointer to the resource to acquire.
2292 * @return Pointer to the Win32K thread pointer of the current thread.
2294 * @remarks See ExAcquireResourceExclusiveLite.
2299 ExEnterCriticalRegionAndAcquireResourceExclusive(PERESOURCE Resource
)
2301 /* Enter critical region */
2302 KeEnterCriticalRegion();
2304 /* Acquire the resource */
2305 ExAcquireResourceExclusiveLite(Resource
, TRUE
);
2307 /* Return the Win32 Thread */
2308 return KeGetCurrentThread()->Win32Thread
;
2312 * @name ExReleaseResourceAndLeaveCriticalRegion
2313 * @implemented NT5.1
2315 * The ExReleaseResourceAndLeaveCriticalRegion release a resource and
2316 * then leaves a critical region.
2319 * Pointer to the resource to release.
2323 * @remarks See ExReleaseResourceLite.
2328 ExReleaseResourceAndLeaveCriticalRegion(PERESOURCE Resource
)
2330 /* Release the resource */
2331 ExReleaseResourceLite(Resource
);
2333 /* Leave critical region */
2334 KeLeaveCriticalRegion();