xref: /reactos/ntoskrnl/ex/resource.c (revision c8d07514)
1 /*
2  * PROJECT:         ReactOS Kernel
3  * LICENSE:         GPL - See COPYING in the top level directory
4  * FILE:            ntoskrnl/ex/resource.c
5  * PURPOSE:         Executive Resource Implementation
6  * PROGRAMMERS:     Alex Ionescu (alex.ionescu@reactos.org)
7  */
8 
9 /* INCLUDES *****************************************************************/
10 
11 #include <ntoskrnl.h>
12 #define NDEBUG
13 #include <debug.h>
14 
15 /* Macros for reading resource flags */
16 #define IsExclusiveWaiting(r)   (r->NumberOfExclusiveWaiters > 0)
17 #define IsSharedWaiting(r)      (r->NumberOfSharedWaiters > 0)
18 #define IsOwnedExclusive(r)     (r->Flag & ResourceOwnedExclusive)
19 #define IsBoostAllowed(r)       (!(r->Flag & ResourceHasDisabledPriorityBoost))
20 
21 #if (!(defined(CONFIG_SMP)) && !(DBG))
22 
23 FORCEINLINE
24 VOID
25 ExAcquireResourceLock(IN PERESOURCE Resource,
26                       IN PKLOCK_QUEUE_HANDLE LockHandle)
27 {
28     UNREFERENCED_PARAMETER(Resource);
29     UNREFERENCED_PARAMETER(LockHandle);
30 
31     /* Simply disable interrupts */
32     _disable();
33 }
34 
35 FORCEINLINE
36 VOID
37 ExReleaseResourceLock(IN PERESOURCE Resource,
38                       IN PKLOCK_QUEUE_HANDLE LockHandle)
39 {
40     UNREFERENCED_PARAMETER(Resource);
41     UNREFERENCED_PARAMETER(LockHandle);
42 
43     /* Simply enable interrupts */
44     _enable();
45 }
46 
47 #else
48 
49 FORCEINLINE
50 VOID
51 ExAcquireResourceLock(IN PERESOURCE Resource,
52                       IN PKLOCK_QUEUE_HANDLE LockHandle)
53 {
54     /* Acquire the lock */
55     KeAcquireInStackQueuedSpinLock(&Resource->SpinLock, LockHandle);
56 }
57 
58 FORCEINLINE
59 VOID
60 ExReleaseResourceLock(IN PERESOURCE Resource,
61                       IN PKLOCK_QUEUE_HANDLE LockHandle)
62 {
63     UNREFERENCED_PARAMETER(Resource);
64 
65     /* Release the lock */
66     KeReleaseInStackQueuedSpinLock(LockHandle);
67 }
68 #endif
69 
70 /* DATA***********************************************************************/
71 
72 LARGE_INTEGER ExShortTime = {{-100000, -1}};
73 LARGE_INTEGER ExpTimeout;
74 ULONG ExpResourceTimeoutCount = 90 * 3600 / 2;
75 KSPIN_LOCK ExpResourceSpinLock;
76 LIST_ENTRY ExpSystemResourcesList;
77 BOOLEAN ExResourceStrict = TRUE;
78 
79 /* PRIVATE FUNCTIONS *********************************************************/
80 
81 #if DBG
82 /*++
83  * @name ExpVerifyResource
84  *
85  *     The ExpVerifyResource routine verifies the correctness of an ERESOURCE
86  *
87  * @param Resource
88  *        Pointer to the resource being verified.
89  *
90  * @return None.
91  *
92  * @remarks Only present on DBG builds.
93  *
94  *--*/
95 VOID
96 NTAPI
97 ExpVerifyResource(IN PERESOURCE Resource)
98 {
99     /* Verify the resource data */
100     ASSERT((((ULONG_PTR)Resource) & (sizeof(ULONG_PTR) - 1)) == 0);
101     ASSERT(!Resource->SharedWaiters ||
102             Resource->SharedWaiters->Header.Type == SemaphoreObject);
103     ASSERT(!Resource->SharedWaiters ||
104             Resource->SharedWaiters->Header.Size == (sizeof(KSEMAPHORE) / sizeof(ULONG)));
105     ASSERT(!Resource->ExclusiveWaiters ||
106             Resource->ExclusiveWaiters->Header.Type == SynchronizationEvent);
107     ASSERT(!Resource->ExclusiveWaiters ||
108             Resource->ExclusiveWaiters->Header.Size == (sizeof(KEVENT) / sizeof(ULONG)));
109 }
110 
111 /*++
112  * @name ExpCheckForApcsDisabled
113  *
114  *     The ExpCheckForApcsDisabled routine checks if Kernel APCs are still
115  *     enabled when they should be disabled, and optionally breakpoints.
116  *
117  * @param Irql
118  *        Specifies the IRQL during the acquire attempt.
119  *
120  * @param Resource
121  *        Pointer to the resource being checked.
122  *
123  * @param Thread
124  *        Pointer to the thread being checked.
125  *
126  * @return None.
127  *
128  * @remarks Only present on DBG builds. Depends on ExResourceStrict value.
129  *
130  *--*/
131 VOID
132 NTAPI
133 ExpCheckForApcsDisabled(IN KIRQL Irql,
134                         IN PERESOURCE Resource,
135                         IN PKTHREAD Thread)
136 {
137     /* Check if we should care and check if we should break */
138     if ((ExResourceStrict) &&
139         (Irql < APC_LEVEL) &&
140         !(((PETHREAD)Thread)->SystemThread) &&
141         !(Thread->CombinedApcDisable))
142     {
143         /* Bad! */
144         DPRINT1("EX: resource: APCs still enabled before resource %p acquire/release "
145                 "!!!\n", Resource);
146         DbgBreakPoint();
147     }
148 }
149 #else
150 #define ExpVerifyResource(r)
151 #define ExpCheckForApcsDisabled(b,r,t)
152 #endif
153 
154 /*++
155  * @name ExpResourceInitialization
156  *
157  *     The ExpResourceInitialization routine initializes resources for use.
158  *
159  * @param None.
160  *
161  * @return None.
162  *
163  * @remarks This routine should only be called once, during system startup.
164  *
165  *--*/
166 CODE_SEG("INIT")
167 VOID
168 NTAPI
169 ExpResourceInitialization(VOID)
170 {
171     /* Setup the timeout */
172     ExpTimeout.QuadPart = Int32x32To64(4, -10000000);
173     InitializeListHead(&ExpSystemResourcesList);
174     KeInitializeSpinLock(&ExpResourceSpinLock);
175 }
176 
177 /*++
178  * @name ExpAllocateExclusiveWaiterEvent
179  *
180  *     The ExpAllocateExclusiveWaiterEvent routine creates the event that will
181  *     be used by exclusive waiters on the resource.
182  *
183  * @param Resource
184  *        Pointer to the resource.
185  *
186  * @param LockHandle
187  *        Pointer to in-stack queued spinlock.
188  *
189  * @return None.
190  *
191  * @remarks The pointer to the event must be atomically set.
192  *
193  *--*/
194 VOID
195 NTAPI
196 ExpAllocateExclusiveWaiterEvent(IN PERESOURCE Resource,
197                                 IN PKLOCK_QUEUE_HANDLE LockHandle)
198 {
199     PKEVENT Event;
200 
201     /* Release the lock */
202     ExReleaseResourceLock(Resource, LockHandle);
203 
204     /* Loop as long as we keep running out of memory */
205     do
206     {
207         /* Allocate the event */
208         Event = ExAllocatePoolWithTag(NonPagedPool,
209                                       sizeof(KEVENT),
210                                       TAG_RESOURCE_EVENT);
211         if (Event)
212         {
213             /* Initialize it */
214             KeInitializeEvent(Event, SynchronizationEvent, FALSE);
215 
216             /* Set it */
217             if (InterlockedCompareExchangePointer((PVOID*)&Resource->ExclusiveWaiters,
218                                                   Event,
219                                                   NULL))
220             {
221                 /* Someone already set it, free our event */
222                 DPRINT1("WARNING: Handling race condition\n");
223                 ExFreePoolWithTag(Event, TAG_RESOURCE_EVENT);
224             }
225 
226             break;
227         }
228 
229         /* Wait a bit before trying again */
230         KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
231     } while (TRUE);
232 
233     /* Re-acquire the lock */
234     ExAcquireResourceLock(Resource, LockHandle);
235 }
236 
237 /*++
238  * @name ExpAllocateSharedWaiterSemaphore
239  *
240  *     The ExpAllocateSharedWaiterSemaphore routine creates the semaphore that
241  *     will be used by shared waiters on the resource.
242  *
243  * @param Resource
244  *        Pointer to the resource.
245  *
246  * @param LockHandle
247  *        Pointer to in-stack queued spinlock.
248  *
249  * @return None.
250  *
251  * @remarks The pointer to the semaphore must be atomically set.
252  *
253  *--*/
254 VOID
255 NTAPI
256 ExpAllocateSharedWaiterSemaphore(IN PERESOURCE Resource,
257                                  IN PKLOCK_QUEUE_HANDLE LockHandle)
258 {
259     PKSEMAPHORE Semaphore;
260 
261     /* Release the lock */
262     ExReleaseResourceLock(Resource, LockHandle);
263 
264     /* Loop as long as we keep running out of memory */
265     do
266     {
267         /* Allocate the semaphore */
268         Semaphore = ExAllocatePoolWithTag(NonPagedPool,
269                                           sizeof(KSEMAPHORE),
270                                           TAG_RESOURCE_SEMAPHORE);
271         if (Semaphore)
272         {
273             /* Initialize it */
274             KeInitializeSemaphore(Semaphore, 0, MAXLONG);
275 
276             /* Set it */
277             if (InterlockedCompareExchangePointer((PVOID*)&Resource->SharedWaiters,
278                                                   Semaphore,
279                                                   NULL))
280             {
281                 /* Someone already set it, free our semaphore */
282                 DPRINT1("WARNING: Handling race condition\n");
283                 ExFreePoolWithTag(Semaphore, TAG_RESOURCE_SEMAPHORE);
284             }
285 
286             break;
287         }
288 
289         /* Wait a bit before trying again */
290         KeDelayExecutionThread(KernelMode, FALSE, &ExShortTime);
291     } while (TRUE);
292 
293     /* Re-acquire the lock */
294     ExAcquireResourceLock(Resource, LockHandle);
295 }
296 
297 /*++
298  * @name ExpExpandResourceOwnerTable
299  *
300  *     The ExpExpandResourceOwnerTable routine expands the owner table of the
301  *     specified resource.
302  *
303  * @param Resource
304  *        Pointer to the resource.
305  *
306  * @param LockHandle
307  *        Pointer to in-stack queued spinlock.
308  *
309  * @return None.
310  *
311  * @remarks None.
312  *
313  *--*/
314 VOID
315 NTAPI
316 ExpExpandResourceOwnerTable(IN PERESOURCE Resource,
317                             IN PKLOCK_QUEUE_HANDLE LockHandle)
318 {
319     POWNER_ENTRY Owner, Table;
320     KIRQL OldIrql;
321     ULONG NewSize, OldSize;
322 
323     /* Get the owner table */
324     Owner = Resource->OwnerTable;
325     if (!Owner)
326     {
327         /* Start with the default size of 3 */
328         OldSize = 0;
329         NewSize = 3;
330     }
331     else
332     {
333         /* Add 4 more entries */
334         OldSize = Owner->TableSize;
335         NewSize = OldSize + 4;
336     }
337 
338     /* Release the lock */
339     ExReleaseResourceLock(Resource, LockHandle);
340 
341     /* Allocate memory for the table */
342     Table = ExAllocatePoolWithTag(NonPagedPool,
343                                   NewSize * sizeof(OWNER_ENTRY),
344                                   TAG_RESOURCE_TABLE);
345 
346     /* Zero the table */
347     RtlZeroMemory(Table + OldSize,
348                   (NewSize - OldSize) * sizeof(OWNER_ENTRY));
349 
350     /* Lock the resource */
351     ExAcquireResourceLock(Resource, LockHandle);
352 
353     /* Make sure nothing has changed */
354     if ((Owner != Resource->OwnerTable) ||
355         ((Owner) && (OldSize != Owner->TableSize)))
356     {
357         /* Resource changed while we weren't holding the lock; bail out */
358         ExReleaseResourceLock(Resource, LockHandle);
359         ExFreePoolWithTag(Table, TAG_RESOURCE_TABLE);
360     }
361     else
362     {
363         /* Copy the table */
364         if (Owner) RtlCopyMemory(Table, Owner, OldSize * sizeof(OWNER_ENTRY));
365 
366         /* Acquire dispatcher lock to prevent thread boosting */
367         OldIrql = KiAcquireDispatcherLock();
368 
369         /* Set the new table data */
370         Table->TableSize = NewSize;
371         Resource->OwnerTable = Table;
372 
373         /* Release dispatcher lock */
374         KiReleaseDispatcherLock(OldIrql);
375 
376         /* Sanity check */
377         ExpVerifyResource(Resource);
378 
379         /* Release lock */
380         ExReleaseResourceLock(Resource, LockHandle);
381 
382         /* Free the old table */
383         if (Owner) ExFreePoolWithTag(Owner, TAG_RESOURCE_TABLE);
384 
385         /* Set the resource index */
386         if (!OldSize) OldSize = 1;
387     }
388 
389     /* Set the resource index */
390     KeGetCurrentThread()->ResourceIndex = (UCHAR)OldSize;
391 
392     /* Lock the resource again */
393     ExAcquireResourceLock(Resource, LockHandle);
394 }
395 
396 /*++
397  * @name ExpFindFreeEntry
398  *
399  *     The ExpFindFreeEntry routine locates an empty owner entry in the
400  *     specified resource. If none was found, then the owner table is
401  *     expanded.
402  *
403  * @param Resource
404  *        Pointer to the resource.
405  *
406  * @param LockHandle
407  *        Pointer to in-stack queued spinlock.
408  *
409  * @return Pointer to an empty OWNER_ENTRY structure.
410  *
411  * @remarks None.
412  *
413  *--*/
414 POWNER_ENTRY
415 FASTCALL
416 ExpFindFreeEntry(IN PERESOURCE Resource,
417                  IN PKLOCK_QUEUE_HANDLE LockHandle)
418 {
419     POWNER_ENTRY Owner, Limit;
420 
421     /* Sanity check */
422     ASSERT(LockHandle != 0);
423     ASSERT(Resource->OwnerEntry.OwnerThread != 0);
424 
425     /* Get the current table pointer */
426     Owner = Resource->OwnerTable;
427     if (Owner)
428     {
429         /* Set the limit, move to the next owner and loop owner entries */
430         Limit = &Owner[Owner->TableSize];
431         Owner++;
432         while (Owner->OwnerThread)
433         {
434             /* Move to the next one */
435             Owner++;
436 
437             /* Check if the entry is free */
438             if (Owner == Limit) goto Expand;
439         }
440 
441         /* Update the resource entry */
442         KeGetCurrentThread()->ResourceIndex = (UCHAR)(Owner - Resource->OwnerTable);
443     }
444     else
445     {
446 Expand:
447         /* No free entry, expand the table */
448         ExpExpandResourceOwnerTable(Resource, LockHandle);
449         Owner = NULL;
450     }
451 
452     /* Return the entry found */
453     return Owner;
454 }
455 
456 /*++
457  * @name ExpFindEntryForThread
458  *
459  *     The ExpFindEntryForThread routine locates the owner entry associated with
460  *     the specified thread in the given resource. If none was found, then the
461  *     owner table is expanded.
462  *
463  * @param Resource
464  *        Pointer to the resource.
465  *
466  * @param Thread
467  *        Pointer to the thread to find.
468  *
469  * @param LockHandle
470  *        Pointer to in-stack queued spinlock.
471  *
472  * @return Pointer to an empty OWNER_ENTRY structure.
473  *
474  * @remarks None.
475  *
476  *--*/
477 POWNER_ENTRY
478 FASTCALL
479 ExpFindEntryForThread(IN PERESOURCE Resource,
480                       IN ERESOURCE_THREAD Thread,
481                       IN PKLOCK_QUEUE_HANDLE LockHandle,
482                       IN BOOLEAN FirstEntryInelligible)
483 {
484     POWNER_ENTRY FreeEntry, Owner, Limit;
485 
486     /* Start by looking in the static array */
487     Owner = &Resource->OwnerEntry;
488     if (Owner->OwnerThread == Thread) return Owner;
489 
490     /* Check if this is a free entry */
491     if ((FirstEntryInelligible) || (Owner->OwnerThread))
492     {
493         /* No free entry */
494         FreeEntry = NULL;
495     }
496     else
497     {
498         /* Use the first entry as our free entry */
499         FreeEntry = Owner;
500     }
501 
502     /* Get the current table pointer */
503     Owner = Resource->OwnerTable;
504     if (Owner)
505     {
506         /* Set the limit, move to the next owner and loop owner entries */
507         Limit = &Owner[Owner->TableSize];
508         Owner++;
509         while (Owner->OwnerThread != Thread)
510         {
511             /* Check if we don't have a free entry */
512             if (!FreeEntry)
513             {
514                 /* Check if this entry is free */
515                 if (!Owner->OwnerThread)
516                 {
517                     /* Save it as our free entry */
518                     FreeEntry = Owner;
519                 }
520             }
521 
522             /* Move to the next one */
523             Owner++;
524 
525             /* Check if the entry is free */
526             if (Owner == Limit) goto Expand;
527         }
528 
529         /* Update the resource entry */
530         KeGetCurrentThread()->ResourceIndex = (UCHAR)(Owner - Resource->OwnerTable);
531         return Owner;
532     }
533     else
534     {
535 Expand:
536         /* Check if it's OK to do an expansion */
537         if (!LockHandle) return NULL;
538 
539         /* If we found a free entry by now, return it */
540         if (FreeEntry)
541         {
542             /* Set the resource index */
543             KeGetCurrentThread()->ResourceIndex = (UCHAR)(FreeEntry - Resource->OwnerTable);
544             return FreeEntry;
545         }
546 
547         /* No free entry, expand the table */
548         ExpExpandResourceOwnerTable(Resource, LockHandle);
549         return NULL;
550     }
551 }
552 
553 /*++
554  * @name ExpBoostOwnerThread
555  *
556  *     The ExpBoostOwnerThread routine increases the priority of a waiting
557  *     thread in an attempt to fight a possible deadlock.
558  *
559  * @param Thread
560  *        Pointer to the current thread.
561  *
562  * @param OwnerThread
563  *        Pointer to thread that owns the resource.
564  *
565  * @return None.
566  *
567  * @remarks None.
568  *
569  *--*/
570 VOID
571 FASTCALL
572 ExpBoostOwnerThread(IN PKTHREAD Thread,
573                     IN PKTHREAD OwnerThread)
574 {
575     /* Make sure the owner thread is a pointer, not an ID */
576     if (!((ULONG_PTR)OwnerThread & 0x3))
577     {
578         /* Check if we can actually boost it */
579         if ((OwnerThread->Priority < Thread->Priority) &&
580             (OwnerThread->Priority < 14))
581         {
582             /* Acquire the thread lock */
583             KiAcquireThreadLock(Thread);
584 
585             /* Set the new priority */
586             OwnerThread->PriorityDecrement += 14 - OwnerThread->Priority;
587 
588             /* Update quantum */
589             OwnerThread->Quantum = OwnerThread->QuantumReset;
590 
591             /* Update the kernel state */
592             KiSetPriorityThread(OwnerThread, 14);
593 
594             /* Release the thread lock */
595             KiReleaseThreadLock(Thread);
596         }
597     }
598 }
599 
600 /*++
601  * @name ExpWaitForResource
602  *
603  *     The ExpWaitForResource routine performs a wait on the specified resource.
604  *
605  * @param Resource
606  *        Pointer to the resource to wait on.
607  *
608  * @param OwnerThread
609  *        Pointer to object (exclusive event or shared semaphore) to wait on.
610  *
611  * @return None.
612  *
613  * @remarks None.
614  *
615  *--*/
616 VOID
617 FASTCALL
618 ExpWaitForResource(IN PERESOURCE Resource,
619                    IN PVOID Object)
620 {
621     ULONG i;
622     ULONG Size;
623     POWNER_ENTRY Owner;
624     ULONG WaitCount = 0;
625     NTSTATUS Status;
626     LARGE_INTEGER Timeout;
627     PKTHREAD Thread, OwnerThread;
628 #if DBG
629     KLOCK_QUEUE_HANDLE LockHandle;
630 #endif
631 
632     /* Increase contention count and use a 5 second timeout */
633     Resource->ContentionCount++;
634     Timeout.QuadPart = 500 * -10000;
635     for (;;)
636     {
637         /* Wait for ownership */
638         Status = KeWaitForSingleObject(Object,
639                                        WrResource,
640                                        KernelMode,
641                                        FALSE,
642                                        &Timeout);
643         if (Status != STATUS_TIMEOUT) break;
644 
645         /* Increase wait count */
646         WaitCount++;
647         Timeout = ExpTimeout;
648 
649         /* Check if we've exceeded the limit */
650         if (WaitCount > ExpResourceTimeoutCount)
651         {
652             /* Reset wait count */
653             WaitCount = 0;
654 #if DBG
655             /* Lock the resource */
656             ExAcquireResourceLock(Resource, &LockHandle);
657 
658             /* Dump debug information */
659             DPRINT1("Resource @ %p\n", Resource);
660             DPRINT1(" ActiveEntries = %04lx  Flags = %s%s%s\n",
661                     Resource->ActiveEntries,
662                     IsOwnedExclusive(Resource) ? "IsOwnedExclusive " : "",
663                     IsSharedWaiting(Resource) ? "SharedWaiter "     : "",
664                     IsExclusiveWaiting(Resource) ? "ExclusiveWaiter "  : "");
665             DPRINT1(" NumberOfExclusiveWaiters = %04lx\n",
666                     Resource->NumberOfExclusiveWaiters);
667             DPRINT1("   Thread = %08lx, Count = %02x\n",
668                     Resource->OwnerEntry.OwnerThread,
669                     Resource->OwnerEntry.OwnerCount);
670 
671             /* Dump out the table too */
672             Owner = Resource->OwnerTable;
673             if (Owner)
674             {
675                 /* Loop every entry */
676                 Size = Owner->TableSize;
677                 for (i = 1; i < Size; i++)
678                 {
679                     /* Print the data */
680                     Owner++;
681                     DPRINT1("   Thread = %08lx, Count = %02x\n",
682                             Owner->OwnerThread,
683                             Owner->OwnerCount);
684                 }
685             }
686 
687             /* Break */
688             DbgBreakPoint();
689             DPRINT1("EX - Rewaiting\n");
690             ExReleaseResourceLock(Resource, &LockHandle);
691 #endif
692         }
693 
694         /* Check if we can boost */
695         if (IsBoostAllowed(Resource))
696         {
697             /* Get the current kernel thread and lock the dispatcher */
698             Thread = KeGetCurrentThread();
699             Thread->WaitIrql = KiAcquireDispatcherLock();
700             Thread->WaitNext = TRUE;
701 
702             /* Get the owner thread and boost it */
703             OwnerThread = (PKTHREAD)Resource->OwnerEntry.OwnerThread;
704             if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
705 
706             /* If it's a shared resource */
707             if (!IsOwnedExclusive(Resource))
708             {
709                 /* Get the table */
710                 Owner = Resource->OwnerTable;
711                 if (Owner)
712                 {
713                     /* Loop every entry */
714                     Size = Owner->TableSize;
715                     for (i = 1; i < Size; i++)
716                     {
717                         /* Move to next entry */
718                         Owner++;
719 
720                         /* Get the thread */
721                         OwnerThread = (PKTHREAD)Owner->OwnerThread;
722 
723                         /* Boost it */
724                         if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
725                     }
726                 }
727             }
728         }
729     }
730 }
731 
732 /* FUNCTIONS *****************************************************************/
733 
734 /*++
735  * @name ExAcquireResourceExclusiveLite
736  * @implemented NT4
737  *
738  *     The ExAcquireResourceExclusiveLite routine acquires the given resource
739  *     for exclusive access by the calling thread.
740  *
741  * @param Resource
742  *        Pointer to the resource to acquire.
743  *
744  * @param Wait
745  *        Specifies the routine's behavior whenever the resource cannot be
746  *        acquired immediately.
747  *
748  * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
749  *         and exclusive access cannot be granted immediately.
750  *
751  * @remarks The caller can release the resource by calling either
752  *          ExReleaseResourceLite or ExReleaseResourceForThreadLite.
753  *
754  *          Normal kernel APC delivery must be disabled before calling this
755  *          routine. Disable normal kernel APC delivery by calling
756  *          KeEnterCriticalRegion. Delivery must remain disabled until the
757  *          resource is released, at which point it can be reenabled by calling
758  *          KeLeaveCriticalRegion.
759  *
760  *          For better performance, call ExTryToAcquireResourceExclusiveLite,
761  *          rather than calling ExAcquireResourceExclusiveLite with Wait set
762  *          to FALSE.
763  *
764  *          Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
765  *          DISPATCH_LEVEL.
766  *
767  *--*/
768 BOOLEAN
769 NTAPI
770 ExAcquireResourceExclusiveLite(IN PERESOURCE Resource,
771                                IN BOOLEAN Wait)
772 {
773     KLOCK_QUEUE_HANDLE LockHandle;
774     ERESOURCE_THREAD Thread;
775     BOOLEAN Success;
776 
777     /* Sanity check */
778     ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
779 
780     /* Get the thread */
781     Thread = ExGetCurrentResourceThread();
782 
783     /* Sanity check and validation */
784     ASSERT(KeIsExecutingDpc() == FALSE);
785     ExpVerifyResource(Resource);
786 
787     /* Acquire the lock */
788     ExAcquireResourceLock(Resource, &LockHandle);
789     ExpCheckForApcsDisabled(LockHandle.OldIrql, Resource, (PKTHREAD)Thread);
790 
791     /* Check if there is a shared owner or exclusive owner */
792 TryAcquire:
793     if (Resource->ActiveEntries)
794     {
795         /* Check if it's exclusively owned, and we own it */
796         if ((IsOwnedExclusive(Resource)) &&
797             (Resource->OwnerEntry.OwnerThread == Thread))
798         {
799             /* Increase the owning count */
800             Resource->OwnerEntry.OwnerCount++;
801             Success = TRUE;
802         }
803         else
804         {
805             /*
806              * If the caller doesn't want us to wait, we can't acquire the
807              * resource because someone else then us owns it. If we can wait,
808              * then we'll wait.
809              */
810             if (!Wait)
811             {
812                 Success = FALSE;
813             }
814             else
815             {
816                 /* Check if it has exclusive waiters */
817                 if (!Resource->ExclusiveWaiters)
818                 {
819                     /* It doesn't, allocate the event and try acquiring again */
820                     ExpAllocateExclusiveWaiterEvent(Resource, &LockHandle);
821                     goto TryAcquire;
822                 }
823 
824                 /* Has exclusive waiters, wait on it */
825                 Resource->NumberOfExclusiveWaiters++;
826                 ExReleaseResourceLock(Resource, &LockHandle);
827                 ExpWaitForResource(Resource, Resource->ExclusiveWaiters);
828 
829                 /* Set owner and return success */
830                 Resource->OwnerEntry.OwnerThread = ExGetCurrentResourceThread();
831                 return TRUE;
832             }
833         }
834     }
835     else
836     {
837         /* Nobody owns it, so let's! */
838         ASSERT(Resource->ActiveEntries == 0);
839         ASSERT(Resource->ActiveCount == 0);
840         Resource->Flag |= ResourceOwnedExclusive;
841         Resource->ActiveEntries = 1;
842         Resource->ActiveCount = 1;
843         Resource->OwnerEntry.OwnerThread = Thread;
844         Resource->OwnerEntry.OwnerCount = 1;
845         Success = TRUE;
846     }
847 
848     /* Release the lock and return */
849     ExReleaseResourceLock(Resource, &LockHandle);
850     return Success;
851 }
852 
853 /*++
854  * @name ExAcquireResourceSharedLite
855  * @implemented NT4
856  *
857  *     The ExAcquireResourceSharedLite routine acquires the given resource
858  *     for shared access by the calling thread.
859  *
860  * @param Resource
861  *        Pointer to the resource to acquire.
862  *
863  * @param Wait
864  *        Specifies the routine's behavior whenever the resource cannot be
865  *        acquired immediately.
866  *
867  * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
868  *         and exclusive access cannot be granted immediately.
869  *
870  * @remarks The caller can release the resource by calling either
871  *          ExReleaseResourceLite or ExReleaseResourceForThreadLite.
872  *
873  *          Normal kernel APC delivery must be disabled before calling this
874  *          routine. Disable normal kernel APC delivery by calling
875  *          KeEnterCriticalRegion. Delivery must remain disabled until the
876  *          resource is released, at which point it can be reenabled by calling
877  *          KeLeaveCriticalRegion.
878  *
879  *          Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
880  *          DISPATCH_LEVEL.
881  *
882  *--*/
883 BOOLEAN
884 NTAPI
885 ExAcquireResourceSharedLite(IN PERESOURCE Resource,
886                             IN BOOLEAN Wait)
887 {
888     KLOCK_QUEUE_HANDLE LockHandle;
889     ERESOURCE_THREAD Thread;
890     POWNER_ENTRY Owner = NULL;
891     BOOLEAN FirstEntryBusy;
892 
893     /* Get the thread */
894     Thread = ExGetCurrentResourceThread();
895 
896     /* Sanity check and validation */
897     ASSERT(KeIsExecutingDpc() == FALSE);
898     ExpVerifyResource(Resource);
899 
900     /* Acquire the lock */
901     ExAcquireResourceLock(Resource, &LockHandle);
902     ExpCheckForApcsDisabled(LockHandle.OldIrql, Resource, (PKTHREAD)Thread);
903 
904     /* Check how many active entries we've got */
905     while (Resource->ActiveEntries != 0)
906     {
907         /* Check if it's exclusively owned */
908         if (IsOwnedExclusive(Resource))
909         {
910             /* Check if we own it */
911             if (Resource->OwnerEntry.OwnerThread == Thread)
912             {
913                 /* Increase the owning count */
914                 Resource->OwnerEntry.OwnerCount++;
915 
916                 /* Release the lock and return */
917                 ExReleaseResourceLock(Resource, &LockHandle);
918                 return TRUE;
919             }
920 
921             /* Find a free entry */
922             Owner = ExpFindFreeEntry(Resource, &LockHandle);
923             if (!Owner) continue;
924         }
925         else
926         {
927             /* Resource is shared, find who owns it */
928             FirstEntryBusy = IsExclusiveWaiting(Resource);
929             Owner = ExpFindEntryForThread(Resource,
930                                           Thread,
931                                           &LockHandle,
932                                           FirstEntryBusy);
933             if (!Owner) continue;
934 
935             /* Is it us? */
936             if (Owner->OwnerThread == Thread)
937             {
938                 /* Increase acquire count and return */
939                 Owner->OwnerCount++;
940                 ASSERT(Owner->OwnerCount != 0);
941 
942                 /* Release the lock and return */
943                 ExReleaseResourceLock(Resource, &LockHandle);
944                 return TRUE;
945             }
946 
947             /* Try to find if there are exclusive waiters */
948             if (!FirstEntryBusy)
949             {
950                 /* There are none, so acquire it */
951                 Owner->OwnerThread = Thread;
952                 Owner->OwnerCount = 1;
953 
954                 /* Check how many active entries we had */
955                 if (Resource->ActiveEntries == 0)
956                 {
957                     /* Set initial counts */
958                     ASSERT(Resource->ActiveCount == 0);
959                     Resource->ActiveEntries = 1;
960                     Resource->ActiveCount = 1;
961                 }
962                 else
963                 {
964                     /* Increase active entries */
965                     ASSERT(Resource->ActiveCount == 1);
966                     Resource->ActiveEntries++;
967                 }
968 
969                 /* Release the lock and return */
970                 ExReleaseResourceLock(Resource, &LockHandle);
971                 return TRUE;
972             }
973         }
974 
975         /* If we got here, then we need to wait. Are we allowed? */
976         if (!Wait)
977         {
978             /* Release the lock and return */
979             ExReleaseResourceLock(Resource, &LockHandle);
980             return FALSE;
981         }
982 
983         /* Check if we have a shared waiters semaphore */
984         if (!Resource->SharedWaiters)
985         {
986             /* Allocate it and try another acquire */
987             ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
988         }
989         else
990         {
991             /* We have shared waiters, wait for it */
992             break;
993         }
994     }
995 
996     /* Did we get here because we don't have active entries? */
997     if (Resource->ActiveEntries == 0)
998     {
999         /* Acquire it */
1000         ASSERT(Resource->ActiveEntries == 0);
1001         ASSERT(Resource->ActiveCount == 0);
1002         Resource->ActiveEntries = 1;
1003         Resource->ActiveCount = 1;
1004         Resource->OwnerEntry.OwnerThread = Thread;
1005         Resource->OwnerEntry.OwnerCount = 1;
1006 
1007         /* Release the lock and return */
1008         ExReleaseResourceLock(Resource, &LockHandle);
1009         return TRUE;
1010     }
1011 
1012     /* Now wait for the resource */
1013     Owner->OwnerThread = Thread;
1014     Owner->OwnerCount = 1;
1015     Resource->NumberOfSharedWaiters++;
1016 
1017     /* Release the lock and return */
1018     ExReleaseResourceLock(Resource, &LockHandle);
1019     ExpWaitForResource(Resource, Resource->SharedWaiters);
1020     return TRUE;
1021 }
1022 
1023 /*++
1024  * @name ExAcquireSharedStarveExclusive
1025  * @implemented NT4
1026  *
1027  *     The ExAcquireSharedStarveExclusive routine acquires the given resource
1028  *     shared access without waiting for any pending attempts to acquire
1029  *     exclusive access to the same resource.
1030  *
1031  * @param Resource
1032  *        Pointer to the resource to acquire.
1033  *
1034  * @param Wait
1035  *        Specifies the routine's behavior whenever the resource cannot be
1036  *        acquired immediately.
1037  *
1038  * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1039  *         and exclusive access cannot be granted immediately.
1040  *
1041  * @remarks The caller can release the resource by calling either
1042  *          ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1043  *
1044  *          Normal kernel APC delivery must be disabled before calling this
1045  *          routine. Disable normal kernel APC delivery by calling
1046  *          KeEnterCriticalRegion. Delivery must remain disabled until the
1047  *          resource is released, at which point it can be reenabled by calling
1048  *          KeLeaveCriticalRegion.
1049  *
1050  *          Callers of ExAcquireSharedStarveExclusive usually need quick access
1051  *          to a shared resource in order to save an exclusive accessor from
1052  *          doing redundant work. For example, a file system might call this
1053  *          routine to modify a cached resource, such as a BCB pinned in the
1054  *          cache, before the Cache Manager can acquire exclusive access to the
1055  *          resource and write the cache out to disk.
1056  *
1057  *          Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1058  *          DISPATCH_LEVEL.
1059  *
1060  *--*/
1061 BOOLEAN
1062 NTAPI
1063 ExAcquireSharedStarveExclusive(IN PERESOURCE Resource,
1064                                IN BOOLEAN Wait)
1065 {
1066     KLOCK_QUEUE_HANDLE LockHandle;
1067     ERESOURCE_THREAD Thread;
1068     POWNER_ENTRY Owner;
1069 
1070     /* Get the thread */
1071     Thread = ExGetCurrentResourceThread();
1072 
1073     /* Sanity check and validation */
1074     ASSERT(KeIsExecutingDpc() == FALSE);
1075     ExpVerifyResource(Resource);
1076 
1077     /* Acquire the lock */
1078     ExAcquireResourceLock(Resource, &LockHandle);
1079 
1080     /* See if anyone owns it */
1081 TryAcquire:
1082     if (Resource->ActiveEntries == 0)
1083     {
1084         /* Nobody owns it, so let's take control */
1085         ASSERT(Resource->ActiveEntries == 0);
1086         ASSERT(Resource->ActiveCount == 0);
1087         Resource->ActiveCount = 1;
1088         Resource->ActiveEntries = 1;
1089         Resource->OwnerEntry.OwnerThread = Thread;
1090         Resource->OwnerEntry.OwnerCount = 1;
1091 
1092         /* Release the lock and return */
1093         ExReleaseResourceLock(Resource, &LockHandle);
1094         return TRUE;
1095     }
1096 
1097     /* Check if it's exclusively owned */
1098     if (IsOwnedExclusive(Resource))
1099     {
1100         /* Check if we own it */
1101         if (Resource->OwnerEntry.OwnerThread == Thread)
1102         {
1103             /* Increase the owning count */
1104             Resource->OwnerEntry.OwnerCount++;
1105 
1106             /* Release the lock and return */
1107             ExReleaseResourceLock(Resource, &LockHandle);
1108             return TRUE;
1109         }
1110 
1111         /* Find a free entry */
1112         Owner = ExpFindFreeEntry(Resource, &LockHandle);
1113         if (!Owner) goto TryAcquire;
1114     }
1115     else
1116     {
1117         /* Resource is shared, find who owns it */
1118         Owner = ExpFindEntryForThread(Resource, Thread, &LockHandle, FALSE);
1119         if (!Owner) goto TryAcquire;
1120 
1121         /* Is it us? */
1122         if (Owner->OwnerThread == Thread)
1123         {
1124             /* Increase acquire count and return */
1125             Owner->OwnerCount++;
1126             ASSERT(Owner->OwnerCount != 0);
1127 
1128             /* Release the lock and return */
1129             ExReleaseResourceLock(Resource, &LockHandle);
1130             return TRUE;
1131         }
1132 
1133         /* Acquire it */
1134         Owner->OwnerThread = Thread;
1135         Owner->OwnerCount = 1;
1136 
1137         /* Check how many active entries we had */
1138         if (Resource->ActiveEntries == 0)
1139         {
1140             /* Set initial counts */
1141             ASSERT(Resource->ActiveCount == 0);
1142             Resource->ActiveEntries = 1;
1143             Resource->ActiveCount = 1;
1144         }
1145         else
1146         {
1147             /* Increase active entries */
1148             ASSERT(Resource->ActiveCount == 1);
1149             Resource->ActiveEntries++;
1150         }
1151 
1152         /* Release the lock and return */
1153         ExReleaseResourceLock(Resource, &LockHandle);
1154         return TRUE;
1155     }
1156 
1157     /* If we got here, then we need to wait. Are we allowed? */
1158     if (!Wait)
1159     {
1160         /* Release the lock and return */
1161         ExReleaseResourceLock(Resource, &LockHandle);
1162         return FALSE;
1163     }
1164 
1165     /* Check if we have a shared waiters semaphore */
1166     if (!Resource->SharedWaiters)
1167     {
1168         /* Allocate it and try another acquire */
1169         ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
1170         goto TryAcquire;
1171     }
1172 
1173     /* Now wait for the resource */
1174     Owner->OwnerThread = Thread;
1175     Owner->OwnerCount = 1;
1176     Resource->NumberOfSharedWaiters++;
1177 
1178     /* Release the lock and return */
1179     ExReleaseResourceLock(Resource, &LockHandle);
1180     ExpWaitForResource(Resource, Resource->SharedWaiters);
1181     return TRUE;
1182 }
1183 
1184 /*++
1185  * @name ExAcquireSharedWaitForExclusive
1186  * @implemented NT4
1187  *
1188  *     The ExAcquireSharedWaitForExclusive routine acquires the given resource
1189  *     for shared access if shared access can be granted and there are no
1190  *     exclusive waiters.
1191  *
1192  * @param Resource
1193  *        Pointer to the resource to acquire.
1194  *
1195  * @param Wait
1196  *        Specifies the routine's behavior whenever the resource cannot be
1197  *        acquired immediately.
1198  *
1199  * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1200  *         and exclusive access cannot be granted immediately.
1201  *
1202  * @remarks The caller can release the resource by calling either
1203  *          ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1204  *
1205  *          Normal kernel APC delivery must be disabled before calling this
1206  *          routine. Disable normal kernel APC delivery by calling
1207  *          KeEnterCriticalRegion. Delivery must remain disabled until the
1208  *          resource is released, at which point it can be reenabled by calling
1209  *          KeLeaveCriticalRegion.
1210  *
1211  *          Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1212  *          DISPATCH_LEVEL.
1213  *
1214  *--*/
1215 BOOLEAN
1216 NTAPI
1217 ExAcquireSharedWaitForExclusive(IN PERESOURCE Resource,
1218                                 IN BOOLEAN Wait)
1219 {
1220     KLOCK_QUEUE_HANDLE LockHandle;
1221     ERESOURCE_THREAD Thread;
1222     POWNER_ENTRY Owner;
1223 
1224     /* Get the thread */
1225     Thread = ExGetCurrentResourceThread();
1226 
1227     /* Sanity check and validation */
1228     ASSERT(KeIsExecutingDpc() == FALSE);
1229     ExpVerifyResource(Resource);
1230 
1231     /* Acquire the lock */
1232     ExAcquireResourceLock(Resource, &LockHandle);
1233 
1234     /* See if nobody owns us */
1235 TryAcquire:
1236     if (!Resource->ActiveEntries)
1237     {
1238         /* Nobody owns it, so let's take control */
1239         ASSERT(Resource->ActiveEntries == 0);
1240         ASSERT(Resource->ActiveCount == 0);
1241         Resource->ActiveCount = 1;
1242         Resource->ActiveEntries = 1;
1243         Resource->OwnerEntry.OwnerThread = Thread;
1244         Resource->OwnerEntry.OwnerCount = 1;
1245 
1246         /* Release the lock and return */
1247         ExReleaseResourceLock(Resource, &LockHandle);
1248         return TRUE;
1249     }
1250 
1251     /* Check if it's exclusively owned */
1252     if (IsOwnedExclusive(Resource))
1253     {
1254         /* Check if we own it */
1255         if (Resource->OwnerEntry.OwnerThread == Thread)
1256         {
1257             /* Increase the owning count */
1258             Resource->OwnerEntry.OwnerCount++;
1259 
1260             /* Release the lock and return */
1261             ExReleaseResourceLock(Resource, &LockHandle);
1262             return TRUE;
1263         }
1264 
1265         /* Find a free entry */
1266         Owner = ExpFindFreeEntry(Resource, &LockHandle);
1267         if (!Owner) goto TryAcquire;
1268     }
1269     else
1270     {
1271         /* Try to find if there are exclusive waiters */
1272         if (IsExclusiveWaiting(Resource))
1273         {
1274             /* We have to wait for the exclusive waiter to be done */
1275             if (!Wait)
1276             {
1277                 /* So bail out if we're not allowed */
1278                 ExReleaseResourceLock(Resource, &LockHandle);
1279                 return FALSE;
1280             }
1281 
1282             /* Check if we have a shared waiters semaphore */
1283             if (!Resource->SharedWaiters)
1284             {
1285                 /* Allocate one and try again */
1286                 ExpAllocateSharedWaiterSemaphore(Resource, &LockHandle);
1287                 goto TryAcquire;
1288             }
1289 
1290             /* Now wait for the resource */
1291             Resource->NumberOfSharedWaiters++;
1292             ExReleaseResourceLock(Resource, &LockHandle);
1293             ExpWaitForResource(Resource, Resource->SharedWaiters);
1294 
1295             /* Get the lock back */
1296             ExAcquireResourceLock(Resource, &LockHandle);
1297 
1298             /* Find who owns it now */
1299             while (!(Owner = ExpFindEntryForThread(Resource, Thread, &LockHandle, TRUE)));
1300 
1301             /* Sanity checks */
1302             ASSERT(IsOwnedExclusive(Resource) == FALSE);
1303             ASSERT(Resource->ActiveEntries > 0);
1304             ASSERT(Owner->OwnerThread != Thread);
1305 
1306             /* Take control */
1307             Owner->OwnerThread = Thread;
1308             Owner->OwnerCount = 1;
1309 
1310             /* Release the lock and return */
1311             ExReleaseResourceLock(Resource, &LockHandle);
1312             return TRUE;
1313         }
1314         else
1315         {
1316             /* Resource is shared, find who owns it */
1317             Owner = ExpFindEntryForThread(Resource, Thread, &LockHandle, FALSE);
1318             if (!Owner) goto TryAcquire;
1319 
1320             /* Is it us? */
1321             if (Owner->OwnerThread == Thread)
1322             {
1323                 /* Increase acquire count and return */
1324                 Owner->OwnerCount++;
1325                 ASSERT(Owner->OwnerCount != 0);
1326 
1327                 /* Release the lock and return */
1328                 ExReleaseResourceLock(Resource, &LockHandle);
1329                 return TRUE;
1330             }
1331 
1332             /* No exclusive waiters, so acquire it */
1333             Owner->OwnerThread = Thread;
1334             Owner->OwnerCount = 1;
1335 
1336             /* Check how many active entries we had */
1337             if (Resource->ActiveEntries == 0)
1338             {
1339                 /* Set initial counts */
1340                 ASSERT(Resource->ActiveCount == 0);
1341                 Resource->ActiveEntries = 1;
1342                 Resource->ActiveCount = 1;
1343             }
1344             else
1345             {
1346                 /* Increase active entries */
1347                 ASSERT(Resource->ActiveCount == 1);
1348                 Resource->ActiveEntries++;
1349             }
1350 
1351             /* Release the lock and return */
1352             ExReleaseResourceLock(Resource, &LockHandle);
1353             return TRUE;
1354         }
1355     }
1356 
1357     /* We have to wait for the exclusive waiter to be done */
1358     if (!Wait)
1359     {
1360         /* So bail out if we're not allowed */
1361         ExReleaseResourceLock(Resource, &LockHandle);
1362         return FALSE;
1363     }
1364 
1365     /* Check if we have a shared waiters semaphore */
1366     if (!Resource->SharedWaiters)
1367     {
1368         /* Allocate one and try again */
1369         ExpAllocateSharedWaiterSemaphore(Resource,&LockHandle);
1370         goto TryAcquire;
1371     }
1372 
1373     /* Take control */
1374     Owner->OwnerThread = Thread;
1375     Owner->OwnerCount = 1;
1376     Resource->NumberOfSharedWaiters++;
1377 
1378     /* Release the lock and return */
1379     ExReleaseResourceLock(Resource, &LockHandle);
1380     ExpWaitForResource(Resource, Resource->SharedWaiters);
1381     return TRUE;
1382 }
1383 
1384 /*++
1385  * @name ExConvertExclusiveToSharedLite
1386  * @implemented NT4
1387  *
1388  *     The ExConvertExclusiveToSharedLite routine converts an exclusively
1389  *     acquired resource into a resource that can be acquired shared.
1390  *
1391  * @param Resource
1392  *        Pointer to the resource to convert.
1393  *
1394  * @return None.
1395  *
1396  * @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL <
1397  *          DISPATCH_LEVEL.
1398  *
1399  *--*/
1400 VOID
1401 NTAPI
1402 ExConvertExclusiveToSharedLite(IN PERESOURCE Resource)
1403 {
1404     ULONG OldWaiters;
1405     KLOCK_QUEUE_HANDLE LockHandle;
1406 
1407     /* Sanity checks */
1408     ASSERT(KeIsExecutingDpc() == FALSE);
1409     ExpVerifyResource(Resource);
1410     ASSERT(IsOwnedExclusive(Resource));
1411     ASSERT(Resource->OwnerEntry.OwnerThread == (ERESOURCE_THREAD)PsGetCurrentThread());
1412 
1413     /* Lock the resource */
1414     ExAcquireResourceLock(Resource, &LockHandle);
1415 
1416     /* Erase the exclusive flag */
1417     Resource->Flag &= ~ResourceOwnedExclusive;
1418 
1419     /* Check if we have shared waiters */
1420     if (IsSharedWaiting(Resource))
1421     {
1422         /* Make the waiters active owners */
1423         OldWaiters = Resource->NumberOfSharedWaiters;
1424         Resource->ActiveEntries += OldWaiters;
1425         Resource->NumberOfSharedWaiters = 0;
1426 
1427         /* Release lock and wake the waiters */
1428         ExReleaseResourceLock(Resource, &LockHandle);
1429         KeReleaseSemaphore(Resource->SharedWaiters, 0, OldWaiters, FALSE);
1430     }
1431     else
1432     {
1433         /* Release lock */
1434         ExReleaseResourceLock(Resource, &LockHandle);
1435     }
1436 }
1437 
1438 /*++
1439  * @name ExDeleteResourceLite
1440  * @implemented NT4
1441  *
1442  *     The ExConvertExclusiveToSharedLite routine deletes a given resource
1443  *     from the system�s resource list.
1444  *
1445  * @param Resource
1446  *        Pointer to the resource to delete.
1447  *
1448  * @return STATUS_SUCCESS if the resource was deleted.
1449  *
1450  * @remarks Callers of ExDeleteResourceLite must be running at IRQL <
1451  *          DISPATCH_LEVEL.
1452  *
1453  *--*/
1454 NTSTATUS
1455 NTAPI
1456 ExDeleteResourceLite(IN PERESOURCE Resource)
1457 {
1458     KLOCK_QUEUE_HANDLE LockHandle;
1459 
1460     /* Sanity checks */
1461     ASSERT(IsSharedWaiting(Resource) == FALSE);
1462     ASSERT(IsExclusiveWaiting(Resource) == FALSE);
1463     ASSERT(KeIsExecutingDpc() == FALSE);
1464     ExpVerifyResource(Resource);
1465 
1466     /* Lock the resource */
1467     KeAcquireInStackQueuedSpinLock(&ExpResourceSpinLock, &LockHandle);
1468 
1469     /* Remove the resource */
1470     RemoveEntryList(&Resource->SystemResourcesList);
1471 
1472     /* Release the lock */
1473     KeReleaseInStackQueuedSpinLock(&LockHandle);
1474 
1475     /* Free every  structure */
1476     if (Resource->OwnerTable) ExFreePoolWithTag(Resource->OwnerTable, TAG_RESOURCE_TABLE);
1477     if (Resource->SharedWaiters) ExFreePoolWithTag(Resource->SharedWaiters, TAG_RESOURCE_SEMAPHORE);
1478     if (Resource->ExclusiveWaiters) ExFreePoolWithTag(Resource->ExclusiveWaiters, TAG_RESOURCE_EVENT);
1479 
1480     /* Return success */
1481     return STATUS_SUCCESS;
1482 }
1483 
1484 /*++
1485  * @name ExDisableResourceBoostLite
1486  * @implemented NT4
1487  *
1488  *     The ExDisableResourceBoostLite routine disables thread boosting for
1489  *     the given resource.
1490  *
1491  * @param Resource
1492  *        Pointer to the resource whose thread boosting will be disabled.
1493  *
1494  * @return None.
1495  *
1496  * @remarks None.
1497  *
1498  *--*/
1499 VOID
1500 NTAPI
1501 ExDisableResourceBoostLite(IN PERESOURCE Resource)
1502 {
1503     KLOCK_QUEUE_HANDLE LockHandle;
1504 
1505     /* Sanity check */
1506     ExpVerifyResource(Resource);
1507 
1508     /* Lock the resource */
1509     ExAcquireResourceLock(Resource, &LockHandle);
1510 
1511     /* Remove the flag */
1512     Resource->Flag |= ResourceHasDisabledPriorityBoost;
1513 
1514     /* Release the lock */
1515     ExReleaseResourceLock(Resource, &LockHandle);
1516 }
1517 
1518 /*++
1519  * @name ExGetExclusiveWaiterCount
1520  * @implemented NT4
1521  *
1522  *     The ExGetExclusiveWaiterCount routine returns the number of exclusive
1523  *     waiters for the given resource.
1524  *
1525  * @param Resource
1526  *        Pointer to the resource to check.
1527  *
1528  * @return The number of exclusive waiters.
1529  *
1530  * @remarks None.
1531  *
1532  *--*/
1533 ULONG
1534 NTAPI
1535 ExGetExclusiveWaiterCount(IN PERESOURCE Resource)
1536 {
1537     /* Return the count */
1538     return Resource->NumberOfExclusiveWaiters;
1539 }
1540 
1541 /*++
1542  * @name ExGetSharedWaiterCount
1543  * @implemented NT4
1544  *
1545  *     The ExGetSharedWaiterCount routine returns the number of shared
1546  *     waiters for the given resource.
1547  *
1548  * @param Resource
1549  *        Pointer to the resource to check.
1550  *
1551  * @return The number of shared waiters.
1552  *
1553  * @remarks None.
1554  *
1555  *--*/
1556 ULONG
1557 NTAPI
1558 ExGetSharedWaiterCount(IN PERESOURCE Resource)
1559 {
1560     /* Return the count */
1561     return Resource->NumberOfSharedWaiters;
1562 }
1563 
1564 /*++
1565  * @name ExInitializeResourceLite
1566  * @implemented NT4
1567  *
1568  *     The ExInitializeResourceLite routine initializes a resource variable.
1569  *
1570  * @param Resource
1571  *        Pointer to the resource to check.
1572  *
1573  * @return STATUS_SUCCESS.
1574  *
1575  * @remarks The storage for ERESOURCE must not be allocated from paged pool.
1576  *
1577  *          The storage must be 8-byte aligned.
1578  *
1579  *--*/
1580 NTSTATUS
1581 NTAPI
1582 ExInitializeResourceLite(IN PERESOURCE Resource)
1583 {
1584     KLOCK_QUEUE_HANDLE LockHandle;
1585 
1586     /* Clear the structure */
1587     RtlZeroMemory(Resource, sizeof(ERESOURCE));
1588 
1589     /* Initialize the lock */
1590     KeInitializeSpinLock(&Resource->SpinLock);
1591 
1592     /* Add it into the system list */
1593     KeAcquireInStackQueuedSpinLock(&ExpResourceSpinLock, &LockHandle);
1594     InsertTailList(&ExpSystemResourcesList, &Resource->SystemResourcesList);
1595     KeReleaseInStackQueuedSpinLock(&LockHandle);
1596 
1597     /* Return success */
1598     return STATUS_SUCCESS;
1599 }
1600 
1601 /*++
1602  * @name ExIsResourceAcquiredExclusiveLite
1603  * @implemented NT4
1604  *
1605  *     The ExIsResourceAcquiredExclusiveLite routine returns whether the
1606  *     current thread has exclusive access to a given resource.
1607  *
1608  * @param Resource
1609  *        Pointer to the resource to check.
1610  *
1611  * @return TRUE if the caller already has exclusive access to the given resource.
1612  *
1613  * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1614  *          IRQL <= DISPATCH_LEVEL.
1615  *
1616  *--*/
1617 BOOLEAN
1618 NTAPI
1619 ExIsResourceAcquiredExclusiveLite(IN PERESOURCE Resource)
1620 {
1621     BOOLEAN IsAcquired = FALSE;
1622 
1623     /* Sanity check */
1624     ExpVerifyResource(Resource);
1625 
1626     /* Check if it's exclusively acquired */
1627     if ((IsOwnedExclusive(Resource)) &&
1628         (Resource->OwnerEntry.OwnerThread == ExGetCurrentResourceThread()))
1629     {
1630         /* It is acquired */
1631         IsAcquired = TRUE;
1632     }
1633 
1634     /* Return if it's acquired */
1635     return IsAcquired;
1636 }
1637 
1638 /*++
1639  * @name ExIsResourceAcquiredSharedLite
1640  * @implemented NT4
1641  *
1642  *     The ExIsResourceAcquiredSharedLite routine returns whether the
1643  *     current thread has has access (either shared or exclusive) to a
1644  *     given resource.
1645  *
1646  * @param Resource
1647  *        Pointer to the resource to check.
1648  *
1649  * @return Number of times the caller has acquired the given resource for
1650  *         shared or exclusive access.
1651  *
1652  * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1653  *          IRQL <= DISPATCH_LEVEL.
1654  *
1655  *--*/
1656 ULONG
1657 NTAPI
1658 ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource)
1659 {
1660     ERESOURCE_THREAD Thread;
1661     ULONG i, Size;
1662     ULONG Count = 0;
1663     KLOCK_QUEUE_HANDLE LockHandle;
1664     POWNER_ENTRY Owner;
1665 
1666     /* Sanity check */
1667     ExpVerifyResource(Resource);
1668 
1669     /* Check if nobody owns us */
1670     if (!Resource->ActiveEntries) return 0;
1671 
1672     /* Get the thread */
1673     Thread = ExGetCurrentResourceThread();
1674 
1675     /* Check if we are in the thread list */
1676     if (Resource->OwnerEntry.OwnerThread == Thread)
1677     {
1678         /* Found it, return count */
1679         Count = Resource->OwnerEntry.OwnerCount;
1680     }
1681     else
1682     {
1683         /* We can't own an exclusive resource at this point */
1684         if (IsOwnedExclusive(Resource)) return 0;
1685 
1686         /* Lock the resource */
1687         ExAcquireResourceLock(Resource, &LockHandle);
1688 
1689         /* Not in the list, do a full table look up */
1690         Owner = Resource->OwnerTable;
1691         if (Owner)
1692         {
1693             /* Get the resource index */
1694             i = ((PKTHREAD)Thread)->ResourceIndex;
1695             Size = Owner->TableSize;
1696 
1697             /* Check if the index is valid and check if we don't match */
1698             if ((i >= Size) || (Owner[i].OwnerThread != Thread))
1699             {
1700                 /* Sh*t! We need to do a full search */
1701                 for (i = 1; i < Size; i++)
1702                 {
1703                     /* Move to next owner */
1704                     Owner++;
1705 
1706                     /* Try to find a match */
1707                     if (Owner->OwnerThread == Thread)
1708                     {
1709                         /* Finally! */
1710                         Count = Owner->OwnerCount;
1711                         break;
1712                     }
1713                 }
1714             }
1715             else
1716             {
1717                 /* We found the match directlry */
1718                 Count = Owner[i].OwnerCount;
1719             }
1720         }
1721 
1722         /* Release the lock */
1723         ExReleaseResourceLock(Resource, &LockHandle);
1724     }
1725 
1726     /* Return count */
1727     return Count;
1728 }
1729 
1730 /*++
1731  * @name ExReinitializeResourceLite
1732  * @implemented NT4
1733  *
1734  *     The ExReinitializeResourceLite routine routine reinitializes
1735  *     an existing resource variable.
1736  *
1737  * @param Resource
1738  *        Pointer to the resource to be reinitialized.
1739  *
1740  * @return STATUS_SUCCESS.
1741  *
1742  * @remarks With a single call to ExReinitializeResource, a driver writer can
1743  *          replace three calls: one to ExDeleteResourceLite, another to
1744  *          ExAllocatePool, and a third to ExInitializeResourceLite. As
1745  *          contention for a resource variable increases, memory is dynamically
1746  *          allocated and attached to the resource in order to track this
1747  *          contention. As an optimization, ExReinitializeResourceLite retains
1748  *          and zeroes this previously allocated memory.
1749  *
1750  *          Callers of ExReinitializeResourceLite must be running at
1751  *          IRQL <= DISPATCH_LEVEL.
1752  *
1753  *--*/
1754 NTSTATUS
1755 NTAPI
1756 ExReinitializeResourceLite(IN PERESOURCE Resource)
1757 {
1758     PKEVENT Event;
1759     PKSEMAPHORE Semaphore;
1760     ULONG i, Size;
1761     POWNER_ENTRY Owner;
1762 
1763     /* Get the owner table */
1764     Owner = Resource->OwnerTable;
1765     if (Owner)
1766     {
1767         /* Get the size and loop it */
1768         Size = Owner->TableSize;
1769         for (i = 0; i < Size; i++)
1770         {
1771             /* Zero the table */
1772             Owner[i].OwnerThread = 0;
1773             Owner[i].OwnerCount = 0;
1774         }
1775     }
1776 
1777     /* Zero the flags and count */
1778     Resource->Flag = 0;
1779     Resource->ActiveCount = 0;
1780     Resource->ActiveEntries = 0;
1781 
1782     /* Reset the semaphore */
1783     Semaphore = Resource->SharedWaiters;
1784     if (Semaphore) KeInitializeSemaphore(Semaphore, 0, MAXLONG);
1785 
1786     /* Reset the event */
1787     Event = Resource->ExclusiveWaiters;
1788     if (Event) KeInitializeEvent(Event, SynchronizationEvent, FALSE);
1789 
1790     /* Clear the resource data */
1791     Resource->OwnerEntry.OwnerThread = 0;
1792     Resource->OwnerEntry.OwnerCount = 0;
1793     Resource->ContentionCount = 0;
1794     Resource->NumberOfSharedWaiters = 0;
1795     Resource->NumberOfExclusiveWaiters = 0;
1796     return STATUS_SUCCESS;
1797 }
1798 
1799 /*++
1800  * @name ExReleaseResourceLite
1801  * @implemented NT4
1802  *
1803  *     The ExReleaseResourceLite routine routine releases
1804  *     a specified executive resource owned by the current thread.
1805  *
1806  * @param Resource
1807  *        Pointer to the resource to be released.
1808  *
1809  * @return None.
1810  *
1811  * @remarks Callers of ExReleaseResourceLite must be running at
1812  *          IRQL <= DISPATCH_LEVEL.
1813  *
1814  *--*/
1815 VOID
1816 FASTCALL
1817 ExReleaseResourceLite(IN PERESOURCE Resource)
1818 {
1819     /* Just call the For-Thread function */
1820     ExReleaseResourceForThreadLite(Resource, ExGetCurrentResourceThread());
1821 }
1822 
1823 /*++
1824  * @name ExReleaseResourceForThreadLite
1825  * @implemented NT4
1826  *
1827  *     The ExReleaseResourceForThreadLite routine routine releases
1828  *     the input resource of the indicated thread.
1829  *
1830  * @param Resource
1831  *        Pointer to the resource to be released.
1832  *
1833  * @param Thread
1834  *        Identifies the thread that originally acquired the resource.
1835   *
1836  * @return None.
1837  *
1838  * @remarks Callers of ExReleaseResourceForThreadLite must be running at
1839  *          IRQL <= DISPATCH_LEVEL.
1840  *
1841  *--*/
1842 VOID
1843 NTAPI
1844 ExReleaseResourceForThreadLite(IN PERESOURCE Resource,
1845                                IN ERESOURCE_THREAD Thread)
1846 {
1847     ULONG i;
1848     ULONG Count;
1849     KLOCK_QUEUE_HANDLE LockHandle;
1850     POWNER_ENTRY Owner, Limit;
1851     ASSERT(Thread != 0);
1852 
1853     /* Get the thread and lock the resource */
1854     ExAcquireResourceLock(Resource, &LockHandle);
1855 
1856     /* Sanity checks */
1857     ExpVerifyResource(Resource);
1858     ExpCheckForApcsDisabled(LockHandle.OldIrql, Resource, KeGetCurrentThread());
1859 
1860     /* Check if it's exclusively owned */
1861     if (IsOwnedExclusive(Resource))
1862     {
1863         /* Decrement owner count and check if we're done */
1864         ASSERT(Resource->OwnerEntry.OwnerThread == Thread);
1865         if (--Resource->OwnerEntry.OwnerCount)
1866         {
1867             /* Done, release lock! */
1868             ExReleaseResourceLock(Resource, &LockHandle);
1869             return;
1870         }
1871 
1872         /* Clear the owner */
1873         Resource->OwnerEntry.OwnerThread = 0;
1874 
1875         /* Decrement the number of active entries */
1876         ASSERT(Resource->ActiveEntries == 1);
1877         Resource->ActiveEntries--;
1878 
1879         /* Check if there are shared waiters */
1880         if (IsSharedWaiting(Resource))
1881         {
1882             /* Remove the exclusive flag */
1883             Resource->Flag &= ~ResourceOwnedExclusive;
1884 
1885             /* Give ownage to another thread */
1886             Count = Resource->NumberOfSharedWaiters;
1887             Resource->ActiveEntries = Count;
1888             Resource->NumberOfSharedWaiters = 0;
1889 
1890             /* Release lock and let someone else have it */
1891             ASSERT(Resource->ActiveCount == 1);
1892             ExReleaseResourceLock(Resource, &LockHandle);
1893             KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE);
1894             return;
1895         }
1896         else if (IsExclusiveWaiting(Resource))
1897         {
1898             /* Give exclusive access */
1899             Resource->OwnerEntry.OwnerThread = 1;
1900             Resource->OwnerEntry.OwnerCount = 1;
1901             Resource->ActiveEntries = 1;
1902             Resource->NumberOfExclusiveWaiters--;
1903 
1904             /* Release the lock and give it away */
1905             ASSERT(Resource->ActiveCount == 1);
1906             ExReleaseResourceLock(Resource, &LockHandle);
1907             KeSetEventBoostPriority(Resource->ExclusiveWaiters,
1908                                     (PKTHREAD*)&Resource->OwnerEntry.OwnerThread);
1909             return;
1910         }
1911 
1912         /* Remove the exclusive flag */
1913         Resource->Flag &= ~ResourceOwnedExclusive;
1914         Resource->ActiveCount = 0;
1915     }
1916     else
1917     {
1918         /* Check if we are in the thread list */
1919         if (Resource->OwnerEntry.OwnerThread == Thread)
1920         {
1921             /* Found it, get owner */
1922             Owner = &Resource->OwnerEntry;
1923         }
1924         else
1925         {
1926             /* Assume no valid index */
1927             i = 1;
1928 
1929             /* If we got a valid pointer, try to get the resource index */
1930             if (!((ULONG)Thread & 3)) i = ((PKTHREAD)Thread)->ResourceIndex;
1931 
1932             /* Do a table lookup */
1933             Owner = Resource->OwnerTable;
1934             ASSERT(Owner != NULL);
1935 
1936             /* Check if we're out of the size and don't match */
1937             if ((i >= Owner->TableSize) || (Owner[i].OwnerThread != Thread))
1938             {
1939                 /* Get the last entry */
1940                 Limit = &Owner[Owner->TableSize];
1941                 for (;;)
1942                 {
1943                     /* Move to the next entry */
1944                     Owner++;
1945 
1946                     /* Make sure we're not out of bounds */
1947                     if (Owner >= Limit)
1948                     {
1949                         /* Bugcheck, nobody owns us */
1950                         KeBugCheckEx(RESOURCE_NOT_OWNED,
1951                                      (ULONG_PTR)Resource,
1952                                      (ULONG_PTR)Thread,
1953                                      (ULONG_PTR)Resource->OwnerTable,
1954                                      (ULONG_PTR)3);
1955                     }
1956 
1957                     /* Check for a match */
1958                     if (Owner->OwnerThread == Thread) break;
1959                 }
1960             }
1961             else
1962             {
1963                 /* Get the entry directly */
1964                 Owner = &Owner[i];
1965             }
1966         }
1967 
1968         /* Sanity checks */
1969         ASSERT(Owner->OwnerThread == Thread);
1970         ASSERT(Owner->OwnerCount > 0);
1971 
1972         /* Check if we are the last owner */
1973         if (--Owner->OwnerCount)
1974         {
1975             /* There are other owners, release lock */
1976             ExReleaseResourceLock(Resource, &LockHandle);
1977             return;
1978         }
1979 
1980         /* Clear owner */
1981         Owner->OwnerThread = 0;
1982 
1983         /* See if the resource isn't being owned anymore */
1984         ASSERT(Resource->ActiveEntries > 0);
1985         if (!(--Resource->ActiveEntries))
1986         {
1987             /* Check if there's an exclusive waiter */
1988             if (IsExclusiveWaiting(Resource))
1989             {
1990                 /* Give exclusive access */
1991                 Resource->Flag |= ResourceOwnedExclusive;
1992                 Resource->OwnerEntry.OwnerThread = 1;
1993                 Resource->OwnerEntry.OwnerCount = 1;
1994                 Resource->ActiveEntries = 1;
1995                 Resource->NumberOfExclusiveWaiters--;
1996 
1997                 /* Release the lock and give it away */
1998                 ASSERT(Resource->ActiveCount == 1);
1999                 ExReleaseResourceLock(Resource, &LockHandle);
2000                 KeSetEventBoostPriority(Resource->ExclusiveWaiters,
2001                                         (PKTHREAD*)&Resource->OwnerEntry.OwnerThread);
2002                 return;
2003             }
2004 
2005             /* Clear the active count */
2006             Resource->ActiveCount = 0;
2007         }
2008     }
2009 
2010     /* Release lock */
2011     ExReleaseResourceLock(Resource, &LockHandle);
2012 }
2013 
2014 /*++
2015  * @name ExSetResourceOwnerPointer
2016  * @implemented NT4
2017  *
2018  *     The ExSetResourceOwnerPointer routine routine sets the owner thread
2019  *     thread pointer for an executive resource.
2020  *
2021  * @param Resource
2022  *        Pointer to the resource whose owner to change.
2023  *
2024  * @param OwnerPointer
2025  *        Pointer to an owner thread pointer of type ERESOURCE_THREAD.
2026   *
2027  * @return None.
2028  *
2029  * @remarks ExSetResourceOwnerPointer, used in conjunction with
2030  *          ExReleaseResourceForThreadLite, provides a means for one thread
2031  *          (acting as an resource manager thread) to acquire and release
2032  *          resources for use by another thread (acting as a resource user
2033  *          thread).
2034  *
2035  *          After calling ExSetResourceOwnerPointer for a specific resource,
2036  *          the only other routine that can be called for that resource is
2037  *          ExReleaseResourceForThreadLite.
2038  *
2039  *          Callers of ExSetResourceOwnerPointer must be running at
2040  *          IRQL <= DISPATCH_LEVEL.
2041  *
2042  *--*/
2043 VOID
2044 NTAPI
2045 ExSetResourceOwnerPointer(IN PERESOURCE Resource,
2046                           IN PVOID OwnerPointer)
2047 {
2048     ERESOURCE_THREAD Thread;
2049     KLOCK_QUEUE_HANDLE LockHandle;
2050     POWNER_ENTRY Owner, ThisOwner;
2051 
2052     /* Sanity check */
2053     ASSERT((OwnerPointer != 0) && (((ULONG_PTR)OwnerPointer & 3) == 3));
2054 
2055     /* Get the thread */
2056     Thread = ExGetCurrentResourceThread();
2057 
2058     /* Sanity check */
2059     ExpVerifyResource(Resource);
2060 
2061     /* Lock the resource */
2062     ExAcquireResourceLock(Resource, &LockHandle);
2063 
2064     /* Check if it's exclusive */
2065     if (IsOwnedExclusive(Resource))
2066     {
2067         /* If it's exclusive, set the first entry no matter what */
2068         ASSERT(Resource->OwnerEntry.OwnerThread == Thread);
2069         ASSERT(Resource->OwnerEntry.OwnerCount > 0);
2070         Resource->OwnerEntry.OwnerThread = (ULONG_PTR)OwnerPointer;
2071     }
2072     else
2073     {
2074         /* Set the thread in both entries */
2075         ThisOwner = ExpFindEntryForThread(Resource,
2076                                           (ERESOURCE_THREAD)OwnerPointer,
2077                                           0,
2078                                           FALSE);
2079         Owner = ExpFindEntryForThread(Resource, Thread, 0, FALSE);
2080         if (!Owner)
2081         {
2082             /* Nobody owns it, crash */
2083             KeBugCheckEx(RESOURCE_NOT_OWNED,
2084                          (ULONG_PTR)Resource,
2085                          Thread,
2086                          (ULONG_PTR)Resource->OwnerTable,
2087                          3);
2088         }
2089 
2090         /* Set if we are the owner */
2091         if (ThisOwner)
2092         {
2093             /* Update data */
2094             ThisOwner->OwnerCount += Owner->OwnerCount;
2095             Owner->OwnerCount = 0;
2096             Owner->OwnerThread = 0;
2097             ASSERT(Resource->ActiveEntries >= 2);
2098             Resource->ActiveEntries--;
2099         }
2100         else
2101         {
2102             /* Update the owner entry instead */
2103             Owner->OwnerThread = (ERESOURCE_THREAD)OwnerPointer;
2104         }
2105     }
2106 
2107     /* Release the resource */
2108     ExReleaseResourceLock(Resource, &LockHandle);
2109 }
2110 
2111 /*++
2112  * @name ExTryToAcquireResourceExclusiveLite
2113  * @implemented NT4
2114  *
2115  *     The ExTryToAcquireResourceExclusiveLite routine routine attemps to
2116  *     acquire the given resource for exclusive access.
2117  *
2118  * @param Resource
2119  *        Pointer to the resource to be acquired.
2120  *
2121  * @return TRUE if the given resource has been acquired for the caller.
2122  *
2123  * @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at
2124  *          IRQL < DISPATCH_LEVEL.
2125  *
2126  *--*/
2127 BOOLEAN
2128 NTAPI
2129 ExTryToAcquireResourceExclusiveLite(IN PERESOURCE Resource)
2130 {
2131     ERESOURCE_THREAD Thread;
2132     KLOCK_QUEUE_HANDLE LockHandle;
2133     BOOLEAN Acquired = FALSE;
2134 
2135     /* Sanity check */
2136     ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
2137 
2138     /* Get the thread */
2139     Thread = ExGetCurrentResourceThread();
2140 
2141     /* Sanity check and validation */
2142     ASSERT(KeIsExecutingDpc() == FALSE);
2143     ExpVerifyResource(Resource);
2144 
2145     /* Acquire the lock */
2146     ExAcquireResourceLock(Resource, &LockHandle);
2147 
2148     /* Check if there is an owner */
2149     if (!Resource->ActiveCount)
2150     {
2151         /* No owner, give exclusive access */
2152         Resource->Flag |= ResourceOwnedExclusive;
2153         Resource->OwnerEntry.OwnerThread = Thread;
2154         Resource->OwnerEntry.OwnerCount = 1;
2155         Resource->ActiveCount = 1;
2156         Resource->ActiveEntries = 1;
2157         Acquired = TRUE;
2158     }
2159     else if ((IsOwnedExclusive(Resource)) &&
2160              (Resource->OwnerEntry.OwnerThread == Thread))
2161     {
2162         /* Do a recursive acquire */
2163         Resource->OwnerEntry.OwnerCount++;
2164         Acquired = TRUE;
2165     }
2166 
2167     /* Release the resource */
2168     ExReleaseResourceLock(Resource, &LockHandle);
2169     return Acquired;
2170 }
2171 
2172 /*++
2173  * @name ExEnterCriticalRegionAndAcquireResourceExclusive
2174  * @implemented NT5.1
2175  *
2176  *     The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical
2177  *     region and then exclusively acquires a resource.
2178  *
2179  * @param Resource
2180  *        Pointer to the resource to acquire.
2181  *
2182  * @return Pointer to the Win32K thread pointer of the current thread.
2183  *
2184  * @remarks See ExAcquireResourceExclusiveLite.
2185  *
2186  *--*/
2187 PVOID
2188 NTAPI
2189 ExEnterCriticalRegionAndAcquireResourceExclusive(IN PERESOURCE Resource)
2190 {
2191     /* Enter critical region */
2192     KeEnterCriticalRegion();
2193 
2194     /* Acquire the resource */
2195     ExAcquireResourceExclusiveLite(Resource, TRUE);
2196 
2197     /* Return the Win32 Thread */
2198     return KeGetCurrentThread()->Win32Thread;
2199 }
2200 
2201 /*++
2202  * @name ExEnterCriticalRegionAndAcquireResourceShared
2203  * @implemented NT5.2
2204  *
2205  *     The ExEnterCriticalRegionAndAcquireResourceShared routine
2206  *     enters a critical region and then acquires a resource shared.
2207  *
2208  * @param Resource
2209  *        Pointer to the resource to acquire.
2210  *
2211  * @return Pointer to the Win32K thread pointer of the current thread.
2212  *
2213  * @remarks See ExAcquireResourceSharedLite.
2214  *
2215  *--*/
2216 PVOID
2217 NTAPI
2218 ExEnterCriticalRegionAndAcquireResourceShared(IN PERESOURCE Resource)
2219 {
2220     /* Enter critical region */
2221     KeEnterCriticalRegion();
2222 
2223     /* Acquire the resource */
2224     ExAcquireResourceSharedLite(Resource, TRUE);
2225 
2226     /* Return the Win32 Thread */
2227     return KeGetCurrentThread()->Win32Thread;
2228 }
2229 
2230 /*++
2231  * @name ExEnterCriticalRegionAndAcquireSharedWaitForExclusive
2232  * @implemented NT5.2
2233  *
2234  *     The ExEnterCriticalRegionAndAcquireSharedWaitForExclusive routine
2235  *     enters a critical region and then acquires a resource shared if
2236  *     shared access can be granted and there are no exclusive waiters.
2237  *     It then acquires the resource exclusively.
2238  *
2239  * @param Resource
2240  *        Pointer to the resource to acquire.
2241  *
2242  * @return Pointer to the Win32K thread pointer of the current thread.
2243  *
2244  * @remarks See ExAcquireSharedWaitForExclusive.
2245  *
2246  *--*/
2247 PVOID
2248 NTAPI
2249 ExEnterCriticalRegionAndAcquireSharedWaitForExclusive(IN PERESOURCE Resource)
2250 {
2251     /* Enter critical region */
2252     KeEnterCriticalRegion();
2253 
2254     /* Acquire the resource */
2255     ExAcquireSharedWaitForExclusive(Resource, TRUE);
2256 
2257     /* Return the Win32 Thread */
2258     return KeGetCurrentThread()->Win32Thread;
2259 }
2260 
2261 /*++
2262  * @name ExReleaseResourceAndLeaveCriticalRegion
2263  * @implemented NT5.1
2264  *
2265  *     The ExReleaseResourceAndLeaveCriticalRegion release a resource and
2266  *     then leaves a critical region.
2267  *
2268  * @param Resource
2269  *        Pointer to the resource to release.
2270  *
2271  * @return None
2272  *
2273  * @remarks See ExReleaseResourceLite.
2274  *
2275  *--*/
2276 VOID
2277 FASTCALL
2278 ExReleaseResourceAndLeaveCriticalRegion(IN PERESOURCE Resource)
2279 {
2280     /* Release the resource */
2281     ExReleaseResourceLite(Resource);
2282 
2283     /* Leave critical region */
2284     KeLeaveCriticalRegion();
2285 }
2286