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