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