- We cannot access the OwnerTable without locking the resource.
[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 #if 0
539
540 /*
541 * Disabled, read the comments bellow.
542 */
543 VOID
544 FASTCALL
545 ExpBoostOwnerThread(IN PKTHREAD Thread,
546 IN PKTHREAD OwnerThread)
547 {
548 BOOLEAN Released = FALSE;
549 DPRINT("ExpBoostOwnerThread: %p\n", Thread);
550
551 /* Make sure the owner thread is a pointer, not an ID */
552 if (!((ULONG_PTR)OwnerThread & 0x3))
553 {
554 /* Check if we can actually boost it */
555 if ((OwnerThread->Priority < Thread->Priority) &&
556 (OwnerThread->Priority < 14))
557 {
558 /* Make sure we're at dispatch */
559 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
560
561 /* Set the new priority */
562 OwnerThread->PriorityDecrement += 14 - OwnerThread->Priority;
563
564 /* Update quantum */
565 OwnerThread->Quantum = OwnerThread->QuantumReset;
566
567 /* Update the kernel state */
568 KiSetPriorityThread(OwnerThread, 14, &Released);
569
570 /* Release Lock if needed */
571 if (!Released) KeReleaseDispatcherDatabaseLockFromDpcLevel();
572
573 /* Make sure we're still at dispatch */
574 ASSERT(KeGetCurrentIrql() >= DISPATCH_LEVEL);
575 }
576 }
577 }
578 #endif
579
580 /*++
581 * @name ExpWaitForResource
582 *
583 * The ExpWaitForResource routine performs a wait on the specified resource.
584 *
585 * @param Resource
586 * Pointer to the resource to wait on.
587 *
588 * @param OwnerThread
589 * Pointer to object (exclusive event or shared semaphore) to wait on.
590 *
591 * @return None.
592 *
593 * @remarks None.
594 *
595 *--*/
596 VOID
597 FASTCALL
598 ExpWaitForResource(IN PERESOURCE Resource,
599 IN PVOID Object)
600 {
601 ULONG i;
602 ULONG Size, WaitCount = 0;
603 KIRQL OldIrql;
604 POWNER_ENTRY Owner;
605 NTSTATUS Status;
606 LARGE_INTEGER Timeout;
607
608 /* Increase contention count and use a 5 second timeout */
609 Resource->ContentionCount++;
610 Timeout.QuadPart = 500 * -10000;
611 for(;;)
612 {
613 /* Wait for ownership */
614 Status = KeWaitForSingleObject(Object,
615 WrResource,
616 KernelMode,
617 FALSE,
618 &Timeout);
619 if (Status != STATUS_TIMEOUT) break;
620
621 /* Increase wait count */
622 WaitCount++;
623 Timeout = ExpTimeout;
624
625 /* Check if we've exceeded the limit */
626 if (WaitCount > ExpResourceTimeoutCount)
627 {
628 /* Reset wait count */
629 WaitCount = 0;
630 #if DBG
631 /* Lock the resource */
632 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
633
634 /* Dump debug information */
635 DPRINT1("Resource @ %lx\n", Resource);
636 DPRINT1(" ActiveCount = %04lx Flags = %s%s%s\n",
637 Resource->ActiveCount,
638 IsOwnedExclusive(Resource) ? "IsOwnedExclusive " : "",
639 IsSharedWaiting(Resource) ? "SharedWaiter " : "",
640 IsExclusiveWaiting(Resource) ? "ExclusiveWaiter " : "");
641 DPRINT1(" NumberOfExclusiveWaiters = %04lx\n",
642 Resource->NumberOfExclusiveWaiters);
643 DPRINT1(" Thread = %08lx, Count = %02x\n",
644 Resource->OwnerThreads[0].OwnerThread,
645 Resource->OwnerThreads[0].OwnerCount);
646 DPRINT1(" Thread = %08lx, Count = %02x\n",
647 Resource->OwnerThreads[1].OwnerThread,
648 Resource->OwnerThreads[1].OwnerCount);
649
650 /* Dump out the table too */
651 Owner = Resource->OwnerTable;
652 if (Owner)
653 {
654 /* Loop every entry */
655 Size = Owner->TableSize;
656 for (i = 1; i < Size; i++)
657 {
658 /* Print the data */
659 Owner++;
660 DPRINT1(" Thread = %08lx, Count = %02x\n",
661 Owner->OwnerThread,
662 Owner->OwnerCount);
663 }
664 }
665
666 /* Break */
667 DbgBreakPoint();
668 DPRINT1("EX - Rewaiting\n");
669 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
670 #endif
671 }
672 #if 0
673 /*
674 * Disabled because:
675 * - We cannot access the OwnerTable without locking the resource.
676 * - The shared waiters may wait also on the semaphore. It makes no sense to boost a waiting thread.
677 * - The thread header is initialized like KeWaitForSingleObject (?, ?, ?, TRUE, ?).
678 * During the boost, possible the dispatcher lock is released but the thread block (WaitNext) isn't changed.
679 */
680
681 /* Check if we can boost */
682 if (!(Resource->Flag & ResourceHasDisabledPriorityBoost))
683 {
684 PKTHREAD Thread, OwnerThread;
685
686 /* Get the current kernel thread and lock the dispatcher */
687 Thread = KeGetCurrentThread();
688 Thread->WaitIrql = KeAcquireDispatcherDatabaseLock();
689 Thread->WaitNext = TRUE;
690
691 /* Get the owner thread and boost it */
692 OwnerThread = (PKTHREAD)Resource->OwnerThreads[0].OwnerThread;
693 if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
694
695 /* If it's a shared resource */
696 if (!IsOwnedExclusive(Resource))
697 {
698 /* Boost the other owner thread too */
699 OwnerThread = (PKTHREAD)Resource->OwnerThreads[1].OwnerThread;
700 if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
701
702 /* Get the table */
703 Owner = Resource->OwnerTable;
704 if (Owner)
705 {
706 /* Loop every entry */
707 Size = Owner->TableSize;
708 for (i = 1; i < Size; i++)
709 {
710 /* Boot every thread in the table */
711 OwnerThread = (PKTHREAD)
712 Resource->OwnerThreads[1].OwnerThread;
713 if (OwnerThread) ExpBoostOwnerThread(Thread, OwnerThread);
714 }
715 }
716 }
717 }
718 #endif
719 }
720 }
721
722 /* FUNCTIONS *****************************************************************/
723
724 /*++
725 * @name ExAcquireResourceExclusiveLite
726 * @implemented NT4
727 *
728 * The ExAcquireResourceExclusiveLite routine acquires the given resource
729 * for exclusive access by the calling thread.
730 *
731 * @param Resource
732 * Pointer to the resource to acquire.
733 *
734 * @param Wait
735 * Specifies the routine's behavior whenever the resource cannot be
736 * acquired immediately.
737 *
738 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
739 * and exclusive access cannot be granted immediately.
740 *
741 * @remarks The caller can release the resource by calling either
742 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
743 *
744 * Normal kernel APC delivery must be disabled before calling this
745 * routine. Disable normal kernel APC delivery by calling
746 * KeEnterCriticalRegion. Delivery must remain disabled until the
747 * resource is released, at which point it can be reenabled by calling
748 * KeLeaveCriticalRegion.
749 *
750 * For better performance, call ExTryToAcquireResourceExclusiveLite,
751 * rather than calling ExAcquireResourceExclusiveLite with Wait set
752 * to FALSE.
753 *
754 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
755 * DISPATCH_LEVEL.
756 *
757 *--*/
758 BOOLEAN
759 NTAPI
760 ExAcquireResourceExclusiveLite(PERESOURCE Resource,
761 BOOLEAN Wait)
762 {
763 KIRQL OldIrql = PASSIVE_LEVEL;
764 ERESOURCE_THREAD Thread;
765 BOOLEAN Success;
766
767 /* Sanity check */
768 ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
769
770 /* Get the thread */
771 Thread = ExGetCurrentResourceThread();
772
773 /* Sanity check and validation */
774 ASSERT(KeIsExecutingDpc() == FALSE);
775 ExpVerifyResource(Resource);
776
777 /* Acquire the lock */
778 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
779 ExpCheckForApcsDisabled(TRUE, Resource, (PETHREAD)Thread);
780
781 /* Check if there is a shared owner or exclusive owner */
782 TryAcquire:
783 DPRINT("ExAcquireResourceExclusiveLite(Resource 0x%p, Wait %d)\n",
784 Resource, Wait);
785 if (Resource->ActiveCount)
786 {
787 /* Check if it's exclusively owned, and we own it */
788 if ((IsOwnedExclusive(Resource)) &&
789 (Resource->OwnerThreads[0].OwnerThread == Thread))
790 {
791 /* Increase the owning count */
792 Resource->OwnerThreads[0].OwnerCount++;
793 Success = TRUE;
794 }
795 else
796 {
797 /*
798 * If the caller doesn't want us to wait, we can't acquire the
799 * resource because someone else then us owns it. If we can wait,
800 * then we'll wait.
801 */
802 if (!Wait)
803 {
804 Success = FALSE;
805 }
806 else
807 {
808 /* Check if it has exclusive waiters */
809 if (!Resource->ExclusiveWaiters)
810 {
811 /* It doesn't, allocate the event and try acquiring again */
812 ExpAllocateExclusiveWaiterEvent(Resource, &OldIrql);
813 goto TryAcquire;
814 }
815 else
816 {
817 /* Has exclusive waiters, wait on it */
818 Resource->NumberOfExclusiveWaiters++;
819 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
820 ExpWaitForResource(Resource, Resource->ExclusiveWaiters);
821
822 /* Set owner and return success */
823 Resource->OwnerThreads[0].OwnerThread = Thread;
824 return TRUE;
825 }
826 }
827 }
828 }
829 else
830 {
831 /* Nobody owns it, so let's! */
832 Resource->Flag |= ResourceOwnedExclusive;
833 Resource->ActiveCount = 1;
834 Resource->OwnerThreads[0].OwnerThread = Thread;
835 Resource->OwnerThreads[0].OwnerCount = 1;
836 Success = TRUE;
837 }
838
839 /* Release the lock and return */
840 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
841 return Success;
842 }
843
844 /*++
845 * @name ExAcquireResourceSharedLite
846 * @implemented NT4
847 *
848 * The ExAcquireResourceSharedLite routine acquires the given resource
849 * for shared access by the calling thread.
850 *
851 * @param Resource
852 * Pointer to the resource to acquire.
853 *
854 * @param Wait
855 * Specifies the routine's behavior whenever the resource cannot be
856 * acquired immediately.
857 *
858 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
859 * and exclusive access cannot be granted immediately.
860 *
861 * @remarks The caller can release the resource by calling either
862 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
863 *
864 * Normal kernel APC delivery must be disabled before calling this
865 * routine. Disable normal kernel APC delivery by calling
866 * KeEnterCriticalRegion. Delivery must remain disabled until the
867 * resource is released, at which point it can be reenabled by calling
868 * KeLeaveCriticalRegion.
869 *
870 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
871 * DISPATCH_LEVEL.
872 *
873 *--*/
874 BOOLEAN
875 NTAPI
876 ExAcquireResourceSharedLite(PERESOURCE Resource,
877 BOOLEAN Wait)
878 {
879 KIRQL OldIrql;
880 ERESOURCE_THREAD Thread;
881 POWNER_ENTRY Owner;
882
883 /* Get the thread */
884 Thread = ExGetCurrentResourceThread();
885
886 /* Sanity check and validation */
887 ASSERT(KeIsExecutingDpc() == FALSE);
888 ExpVerifyResource(Resource);
889
890 /* Acquire the lock */
891 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
892 ExpCheckForApcsDisabled(TRUE, Resource, (PETHREAD)Thread);
893
894 /* See if nobody owns us */
895 TryAcquire:
896 DPRINT("ExAcquireResourceSharedLite(Resource 0x%p, Wait %d)\n",
897 Resource, Wait);
898 if (!Resource->ActiveCount)
899 {
900 if (Resource->NumberOfSharedWaiters == 0)
901 {
902 Owner = &Resource->OwnerThreads[1];
903 }
904 else
905 {
906 /* Find a free entry */
907 Owner = ExpFindFreeEntry(Resource, &OldIrql);
908 if (!Owner) goto TryAcquire;
909 }
910
911 Owner->OwnerThread = Thread;
912 Owner->OwnerCount = 1;
913 Resource->ActiveCount = 1;
914
915 /* Release the lock and return */
916 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
917 return TRUE;
918 }
919
920 /* Check if it's exclusively owned */
921 if (IsOwnedExclusive(Resource))
922 {
923 /* Check if we own it */
924 if (Resource->OwnerThreads[0].OwnerThread == Thread)
925 {
926 /* Increase the owning count */
927 Resource->OwnerThreads[0].OwnerCount++;
928
929 /* Release the lock and return */
930 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
931 return TRUE;
932 }
933
934 /* Find a free entry */
935 Owner = ExpFindFreeEntry(Resource, &OldIrql);
936 if (!Owner) goto TryAcquire;
937 }
938 else
939 {
940 /* Resource is shared, find who owns it */
941 Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql);
942 if (!Owner) goto TryAcquire;
943
944 /* Is it us? */
945 if (Owner->OwnerThread == Thread)
946 {
947 /* Increase acquire count and return */
948 Owner->OwnerCount++;
949 ASSERT(Owner->OwnerCount != 0);
950
951 /* Release the lock and return */
952 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
953 return TRUE;
954 }
955
956 /* Try to find if there are exclusive waiters */
957 if (!IsExclusiveWaiting(Resource))
958 {
959 /* There are none, so acquire it */
960 Owner->OwnerThread = Thread;
961 Owner->OwnerCount = 1;
962 Resource->ActiveCount++;
963
964 /* Release the lock and return */
965 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
966 return TRUE;
967 }
968 }
969
970 /* If we got here, then we need to wait. Are we allowed? */
971 if (!Wait)
972 {
973 /* Release the lock and return */
974 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
975 return FALSE;
976 }
977
978 /* Check if we have a shared waiters semaphore */
979 if (!Resource->SharedWaiters)
980 {
981 /* Allocate it and try another acquire */
982 ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
983 goto TryAcquire;
984 }
985
986 /* Now wait for the resource */
987 Owner->OwnerThread = Thread;
988 Owner->OwnerCount = 1;
989 Resource->NumberOfSharedWaiters++;
990
991 /* Release the lock and return */
992 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
993 ExpWaitForResource(Resource, Resource->SharedWaiters);
994 return TRUE;
995 }
996
997 /*++
998 * @name ExAcquireSharedStarveExclusive
999 * @implemented NT4
1000 *
1001 * The ExAcquireSharedStarveExclusive routine acquires the given resource
1002 * shared access without waiting for any pending attempts to acquire
1003 * exclusive access to the same resource.
1004 *
1005 * @param Resource
1006 * Pointer to the resource to acquire.
1007 *
1008 * @param Wait
1009 * Specifies the routine's behavior whenever the resource cannot be
1010 * acquired immediately.
1011 *
1012 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1013 * and exclusive access cannot be granted immediately.
1014 *
1015 * @remarks The caller can release the resource by calling either
1016 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1017 *
1018 * Normal kernel APC delivery must be disabled before calling this
1019 * routine. Disable normal kernel APC delivery by calling
1020 * KeEnterCriticalRegion. Delivery must remain disabled until the
1021 * resource is released, at which point it can be reenabled by calling
1022 * KeLeaveCriticalRegion.
1023 *
1024 * Callers of ExAcquireSharedStarveExclusive usually need quick access
1025 * to a shared resource in order to save an exclusive accessor from
1026 * doing redundant work. For example, a file system might call this
1027 * routine to modify a cached resource, such as a BCB pinned in the
1028 * cache, before the Cache Manager can acquire exclusive access to the
1029 * resource and write the cache out to disk.
1030 *
1031 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1032 * DISPATCH_LEVEL.
1033 *
1034 *--*/
1035 BOOLEAN
1036 NTAPI
1037 ExAcquireSharedStarveExclusive(PERESOURCE Resource,
1038 BOOLEAN Wait)
1039 {
1040 KIRQL OldIrql;
1041 ERESOURCE_THREAD Thread;
1042 POWNER_ENTRY Owner;
1043
1044 /* Get the thread */
1045 Thread = ExGetCurrentResourceThread();
1046
1047 /* Sanity check and validation */
1048 ASSERT(KeIsExecutingDpc() == FALSE);
1049 ExpVerifyResource(Resource);
1050
1051 /* Acquire the lock */
1052 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1053
1054 /* See if nobody owns us */
1055 TryAcquire:
1056 DPRINT("ExAcquireSharedStarveExclusive(Resource 0x%p, Wait %d)\n",
1057 Resource, Wait);
1058 if (!Resource->ActiveCount)
1059 {
1060 /* Nobody owns it, so let's take control */
1061 Resource->ActiveCount = 1;
1062 Resource->OwnerThreads[1].OwnerThread = Thread;
1063 Resource->OwnerThreads[1].OwnerCount = 1;
1064
1065 /* Release the lock and return */
1066 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1067 return TRUE;
1068 }
1069
1070 /* Check if it's exclusively owned */
1071 if (IsOwnedExclusive(Resource))
1072 {
1073 /* Check if we own it */
1074 if (Resource->OwnerThreads[0].OwnerThread == Thread)
1075 {
1076 /* Increase the owning count */
1077 Resource->OwnerThreads[0].OwnerCount++;
1078
1079 /* Release the lock and return */
1080 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1081 return TRUE;
1082 }
1083
1084 /* Find a free entry */
1085 Owner = ExpFindFreeEntry(Resource, &OldIrql);
1086 if (!Owner) goto TryAcquire;
1087
1088 /* If we got here, then we need to wait. Are we allowed? */
1089 if (!Wait)
1090 {
1091 /* Release the lock and return */
1092 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1093 return TRUE;
1094 }
1095
1096 /* Check if we have a shared waiters semaphore */
1097 if (!Resource->SharedWaiters)
1098 {
1099 /* Allocate one and try again */
1100 ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
1101 goto TryAcquire;
1102 }
1103 }
1104 else
1105 {
1106 /* Resource is shared, find who owns it */
1107 Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql);
1108 if (!Owner) goto TryAcquire;
1109
1110 /* Is it us? */
1111 if (Owner->OwnerThread == Thread)
1112 {
1113 /* Increase acquire count and return */
1114 Owner->OwnerCount++;
1115 ASSERT(Owner->OwnerCount != 0);
1116
1117 /* Release the lock and return */
1118 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1119 return TRUE;
1120 }
1121
1122 /* Acquire it */
1123 Owner->OwnerThread = Thread;
1124 Owner->OwnerCount = 1;
1125 Resource->ActiveCount++;
1126
1127 /* Release the lock and return */
1128 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1129 return TRUE;
1130 }
1131
1132 /* If we got here, then we need to wait. Are we allowed? */
1133 if (!Wait)
1134 {
1135 /* Release the lock and return */
1136 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1137 return TRUE;
1138 }
1139
1140 /* Now wait for the resource */
1141 Owner->OwnerThread = Thread;
1142 Owner->OwnerCount = 1;
1143 Resource->NumberOfSharedWaiters++;
1144
1145 /* Release the lock and return */
1146 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1147 ExpWaitForResource(Resource, Resource->SharedWaiters);
1148 return TRUE;
1149 }
1150
1151 /*++
1152 * @name ExAcquireSharedWaitForExclusive
1153 * @implemented NT4
1154 *
1155 * The ExAcquireSharedWaitForExclusive routine acquires the given resource
1156 * for shared access if shared access can be granted and there are no
1157 * exclusive waiters.
1158 *
1159 * @param Resource
1160 * Pointer to the resource to acquire.
1161 *
1162 * @param Wait
1163 * Specifies the routine's behavior whenever the resource cannot be
1164 * acquired immediately.
1165 *
1166 * @return TRUE if the resource is acquired. FALSE if the input Wait is FALSE
1167 * and exclusive access cannot be granted immediately.
1168 *
1169 * @remarks The caller can release the resource by calling either
1170 * ExReleaseResourceLite or ExReleaseResourceForThreadLite.
1171 *
1172 * Normal kernel APC delivery must be disabled before calling this
1173 * routine. Disable normal kernel APC delivery by calling
1174 * KeEnterCriticalRegion. Delivery must remain disabled until the
1175 * resource is released, at which point it can be reenabled by calling
1176 * KeLeaveCriticalRegion.
1177 *
1178 * Callers of ExAcquireResourceExclusiveLite must be running at IRQL <
1179 * DISPATCH_LEVEL.
1180 *
1181 *--*/
1182 BOOLEAN
1183 NTAPI
1184 ExAcquireSharedWaitForExclusive(PERESOURCE Resource,
1185 BOOLEAN Wait)
1186 {
1187 KIRQL OldIrql;
1188 ERESOURCE_THREAD Thread;
1189 POWNER_ENTRY Owner;
1190
1191 /* Get the thread */
1192 Thread = ExGetCurrentResourceThread();
1193
1194 /* Sanity check and validation */
1195 ASSERT(KeIsExecutingDpc() == FALSE);
1196 ExpVerifyResource(Resource);
1197
1198 /* Acquire the lock */
1199 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1200
1201 /* See if nobody owns us */
1202 TryAcquire:
1203 DPRINT("ExAcquireSharedWaitForExclusive(Resource 0x%p, Wait %d)\n",
1204 Resource, Wait);
1205 if (!Resource->ActiveCount)
1206 {
1207 /* Nobody owns it, so let's take control */
1208 Resource->ActiveCount = 1;
1209 Resource->OwnerThreads[1].OwnerThread = Thread;
1210 Resource->OwnerThreads[1].OwnerCount = 1;
1211
1212 /* Release the lock and return */
1213 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1214 return TRUE;
1215 }
1216
1217 /* Check if it's exclusively owned */
1218 if (IsOwnedExclusive(Resource))
1219 {
1220 /* Check if we own it */
1221 if (Resource->OwnerThreads[0].OwnerThread == Thread)
1222 {
1223 /* Increase the owning count */
1224 Resource->OwnerThreads[0].OwnerCount++;
1225
1226 /* Release the lock and return */
1227 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1228 return TRUE;
1229 }
1230 else
1231 {
1232 /* Find a free entry */
1233 Owner = ExpFindFreeEntry(Resource, &OldIrql);
1234 if (!Owner) goto TryAcquire;
1235
1236 /* If we got here, then we need to wait. Are we allowed? */
1237 if (!Wait)
1238 {
1239 /* Release the lock and return */
1240 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1241 return TRUE;
1242 }
1243
1244 /* Check if we have a shared waiters semaphore */
1245 if (!Resource->SharedWaiters)
1246 {
1247 /* Allocate one and try again */
1248 ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
1249 goto TryAcquire;
1250 }
1251
1252 /* Now take control of the resource */
1253 Owner->OwnerThread = Thread;
1254 Owner->OwnerCount = 1;
1255 Resource->NumberOfSharedWaiters++;
1256
1257 /* Release the lock and wait for it to be ours */
1258 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1259 ExpWaitForResource(Resource, Resource->SharedWaiters);
1260 return TRUE;
1261 }
1262 }
1263 else
1264 {
1265 /* Try to find if there are exclusive waiters */
1266 if (IsExclusiveWaiting(Resource))
1267 {
1268 /* We have to wait for the exclusive waiter to be done */
1269 if (!Wait)
1270 {
1271 /* So bail out if we're not allowed */
1272 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1273 return TRUE;
1274 }
1275
1276 /* Check if we have a shared waiters semaphore */
1277 if (!Resource->SharedWaiters)
1278 {
1279 /* Allocate one and try again */
1280 ExpAllocateSharedWaiterSemaphore(Resource, &OldIrql);
1281 goto TryAcquire;
1282 }
1283
1284 /* Now wait for the resource */
1285 Resource->NumberOfSharedWaiters++;
1286 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1287 ExpWaitForResource(Resource, Resource->SharedWaiters);
1288
1289 /* Get the lock back */
1290 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1291
1292 /* Find who owns it now */
1293 Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql);
1294
1295 /* Sanity checks */
1296 ASSERT(IsOwnedExclusive(Resource) == FALSE);
1297 ASSERT(Resource->ActiveCount > 0);
1298 ASSERT(Owner->OwnerThread != Thread);
1299
1300 /* Take control */
1301 Owner->OwnerThread = Thread;
1302 Owner->OwnerCount = 1;
1303
1304 /* Release the lock and return */
1305 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1306 return TRUE;
1307 }
1308 else
1309 {
1310 /* Resource is shared, find who owns it */
1311 Owner = ExpFindEntryForThread(Resource, Thread, &OldIrql);
1312 if (!Owner) goto TryAcquire;
1313
1314 /* Is it us? */
1315 if (Owner->OwnerThread == Thread)
1316 {
1317 /* Increase acquire count and return */
1318 Owner->OwnerCount++;
1319 ASSERT(Owner->OwnerCount != 0);
1320
1321 /* Release the lock and return */
1322 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1323 return TRUE;
1324 }
1325
1326 /* No exclusive waiters, so acquire it */
1327 Owner->OwnerThread = Thread;
1328 Owner->OwnerCount = 1;
1329 Resource->ActiveCount++;
1330
1331 /* Release the lock and return */
1332 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1333 return TRUE;
1334 }
1335 }
1336 }
1337
1338 /*++
1339 * @name ExConvertExclusiveToSharedLite
1340 * @implemented NT4
1341 *
1342 * The ExConvertExclusiveToSharedLite routine converts an exclusively
1343 * acquired resource into a resource that can be acquired shared.
1344 *
1345 * @param Resource
1346 * Pointer to the resource to convert.
1347 *
1348 * @return None.
1349 *
1350 * @remarks Callers of ExConvertExclusiveToSharedLite must be running at IRQL <
1351 * DISPATCH_LEVEL.
1352 *
1353 *--*/
1354 VOID
1355 NTAPI
1356 ExConvertExclusiveToSharedLite(PERESOURCE Resource)
1357 {
1358 ULONG OldWaiters;
1359 KIRQL OldIrql;
1360 DPRINT("ExConvertExclusiveToSharedLite(Resource 0x%p)\n", Resource);
1361
1362 /* Lock the resource */
1363 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1364
1365 /* Sanity checks */
1366 ASSERT(KeIsExecutingDpc() == FALSE);
1367 ExpVerifyResource(Resource);
1368 ASSERT(IsOwnedExclusive(Resource));
1369 ASSERT(Resource->OwnerThreads[0].OwnerThread == (ERESOURCE_THREAD)PsGetCurrentThread());
1370
1371 /* Erase the exclusive flag */
1372 Resource->Flag &= ~ResourceOwnedExclusive;
1373
1374 /* Check if we have shared waiters */
1375 OldWaiters = Resource->NumberOfSharedWaiters;
1376 if (OldWaiters)
1377 {
1378 /* Make the waiters active owners */
1379 Resource->ActiveCount = Resource->ActiveCount + (USHORT)OldWaiters;
1380 Resource->NumberOfSharedWaiters = 0;
1381
1382 /* Release lock and wake the waiters */
1383 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1384 KeReleaseSemaphore(Resource->SharedWaiters, 0, OldWaiters, FALSE);
1385 }
1386 else
1387 {
1388 /* Release lock */
1389 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1390 }
1391 }
1392
1393 /*++
1394 * @name ExDeleteResourceLite
1395 * @implemented NT4
1396 *
1397 * The ExConvertExclusiveToSharedLite routine deletes a given resource
1398 * from the system\92s resource list.
1399 *
1400 * @param Resource
1401 * Pointer to the resource to delete.
1402 *
1403 * @return STATUS_SUCCESS if the resource was deleted.
1404 *
1405 * @remarks Callers of ExDeleteResourceLite must be running at IRQL <
1406 * DISPATCH_LEVEL.
1407 *
1408 *--*/
1409 NTSTATUS
1410 NTAPI
1411 ExDeleteResourceLite(PERESOURCE Resource)
1412 {
1413 KIRQL OldIrql;
1414 DPRINT("ExDeleteResourceLite(Resource 0x%p)\n", Resource);
1415
1416 /* Sanity checks */
1417 ASSERT(IsSharedWaiting(Resource) == FALSE);
1418 ASSERT(IsExclusiveWaiting(Resource) == FALSE);
1419 ASSERT(KeIsExecutingDpc() == FALSE);
1420 ExpVerifyResource(Resource);
1421
1422 /* Lock the resource */
1423 KeAcquireSpinLock(&ExpResourceSpinLock, &OldIrql);
1424
1425 /* Remove the resource */
1426 RemoveEntryList(&Resource->SystemResourcesList);
1427
1428 /* Release the lock */
1429 KeReleaseSpinLock(&ExpResourceSpinLock, OldIrql);
1430
1431 /* Free every structure */
1432 if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
1433 if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
1434 if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
1435
1436 /* Return success */
1437 return STATUS_SUCCESS;
1438 }
1439
1440 /*++
1441 * @name ExDisableResourceBoostLite
1442 * @implemented NT4
1443 *
1444 * The ExDisableResourceBoostLite routine disables thread boosting for
1445 * the given resource.
1446 *
1447 * @param Resource
1448 * Pointer to the resource whose thread boosting will be disabled.
1449 *
1450 * @return None.
1451 *
1452 * @remarks None.
1453 *
1454 *--*/
1455 VOID
1456 NTAPI
1457 ExDisableResourceBoostLite(PERESOURCE Resource)
1458 {
1459 KIRQL OldIrql;
1460
1461 /* Sanity check */
1462 ExpVerifyResource(Resource);
1463
1464 /* Lock the resource */
1465 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1466
1467 /* Remove the flag */
1468 Resource->Flag |= ResourceHasDisabledPriorityBoost;
1469
1470 /* Release the lock */
1471 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1472 }
1473
1474 /*++
1475 * @name ExGetExclusiveWaiterCount
1476 * @implemented NT4
1477 *
1478 * The ExGetExclusiveWaiterCount routine returns the number of exclusive
1479 * waiters for the given resource.
1480 *
1481 * @param Resource
1482 * Pointer to the resource to check.
1483 *
1484 * @return The number of exclusive waiters.
1485 *
1486 * @remarks None.
1487 *
1488 *--*/
1489 ULONG
1490 NTAPI
1491 ExGetExclusiveWaiterCount(PERESOURCE Resource)
1492 {
1493 /* Return the count */
1494 return Resource->NumberOfExclusiveWaiters;
1495 }
1496
1497 /*++
1498 * @name ExGetSharedWaiterCount
1499 * @implemented NT4
1500 *
1501 * The ExGetSharedWaiterCount routine returns the number of shared
1502 * waiters for the given resource.
1503 *
1504 * @param Resource
1505 * Pointer to the resource to check.
1506 *
1507 * @return The number of shared waiters.
1508 *
1509 * @remarks None.
1510 *
1511 *--*/
1512 ULONG
1513 NTAPI
1514 ExGetSharedWaiterCount(PERESOURCE Resource)
1515 {
1516 /* Return the count */
1517 return Resource->NumberOfSharedWaiters;
1518 }
1519
1520 /*++
1521 * @name ExInitializeResourceLite
1522 * @implemented NT4
1523 *
1524 * The ExInitializeResourceLite routine initializes a resource variable.
1525 *
1526 * @param Resource
1527 * Pointer to the resource to check.
1528 *
1529 * @return STATUS_SUCCESS.
1530 *
1531 * @remarks The storage for ERESOURCE must not be allocated from paged pool.
1532 *
1533 * The storage must be 8-byte aligned.
1534 *
1535 *--*/
1536 NTSTATUS
1537 NTAPI
1538 ExInitializeResourceLite(PERESOURCE Resource)
1539 {
1540 DPRINT("ExInitializeResourceLite(Resource 0x%p)\n", Resource);
1541
1542 /* Clear the structure */
1543 RtlZeroMemory(Resource, sizeof(ERESOURCE));
1544
1545 /* Initialize the lock */
1546 KeInitializeSpinLock(&Resource->SpinLock);
1547
1548 /* Add it into the system list */
1549 ExInterlockedInsertTailList(&ExpSystemResourcesList,
1550 &Resource->SystemResourcesList,
1551 &ExpResourceSpinLock);
1552
1553 /* Return success */
1554 return STATUS_SUCCESS;
1555 }
1556
1557 /*++
1558 * @name ExIsResourceAcquiredExclusiveLite
1559 * @implemented NT4
1560 *
1561 * The ExIsResourceAcquiredExclusiveLite routine returns whether the
1562 * current thread has exclusive access to a given resource.
1563 *
1564 * @param Resource
1565 * Pointer to the resource to check.
1566 *
1567 * @return TRUE if the caller already has exclusive access to the given resource.
1568 *
1569 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1570 * IRQL <= DISPATCH_LEVEL.
1571 *
1572 *--*/
1573 BOOLEAN
1574 NTAPI
1575 ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource)
1576 {
1577 ERESOURCE_THREAD Thread = ExGetCurrentResourceThread();
1578 BOOLEAN IsAcquired = FALSE;
1579
1580 /* Sanity check */
1581 ExpVerifyResource(Resource);
1582
1583 /* Check if it's exclusively acquired */
1584 if ((IsOwnedExclusive(Resource)) &&
1585 (Resource->OwnerThreads[0].OwnerThread == Thread))
1586 {
1587 /* It is acquired */
1588 IsAcquired = TRUE;
1589 }
1590
1591 /* Return if it's acquired */
1592 return IsAcquired;
1593 }
1594
1595 /*++
1596 * @name ExIsResourceAcquiredSharedLite
1597 * @implemented NT4
1598 *
1599 * The ExIsResourceAcquiredSharedLite routine returns whether the
1600 * current thread has has access (either shared or exclusive) to a
1601 * given resource.
1602 *
1603 * @param Resource
1604 * Pointer to the resource to check.
1605 *
1606 * @return Number of times the caller has acquired the given resource for
1607 * shared or exclusive access.
1608 *
1609 * @remarks Callers of ExIsResourceAcquiredExclusiveLite must be running at
1610 * IRQL <= DISPATCH_LEVEL.
1611 *
1612 *--*/
1613 ULONG
1614 NTAPI
1615 ExIsResourceAcquiredSharedLite(IN PERESOURCE Resource)
1616 {
1617 ERESOURCE_THREAD Thread;
1618 ULONG i, Size;
1619 ULONG Count = 0;
1620 KIRQL OldIrql;
1621 POWNER_ENTRY Owner;
1622
1623 /* Sanity check */
1624 ExpVerifyResource(Resource);
1625
1626 /* Get the thread */
1627 Thread = ExGetCurrentResourceThread();
1628
1629 /* Check if we are in the thread list */
1630 if (Resource->OwnerThreads[0].OwnerThread == Thread)
1631 {
1632 /* Found it, return count */
1633 Count = Resource->OwnerThreads[0].OwnerCount;
1634 }
1635 else if (Resource->OwnerThreads[1].OwnerThread == Thread)
1636 {
1637 /* Found it on the second list, return count */
1638 Count = Resource->OwnerThreads[1].OwnerCount;
1639 }
1640 else
1641 {
1642 /* Not in the list, do a full table look up */
1643 Owner = Resource->OwnerTable;
1644 if (Owner)
1645 {
1646 /* Lock the resource */
1647 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1648
1649 /* Get the resource index */
1650 i = ((PKTHREAD)Thread)->ResourceIndex;
1651 Size = Owner->TableSize;
1652
1653 /* Check if the index is valid and check if we don't match */
1654 if ((i >= Size) || (Owner[i].OwnerThread != Thread))
1655 {
1656 /* Sh*t! We need to do a full search */
1657 for (i = 1; i < Size; i++)
1658 {
1659 /* Move to next owner */
1660 Owner++;
1661
1662 /* Try to find a match */
1663 if (Owner->OwnerThread == Thread)
1664 {
1665 /* Finally! */
1666 Count = Owner->OwnerCount;
1667 break;
1668 }
1669 }
1670 }
1671 else
1672 {
1673 /* We found the match directlry */
1674 Count = Owner[i].OwnerCount;
1675 }
1676
1677 /* Release the lock */
1678 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1679 }
1680 }
1681
1682 /* Return count */
1683 return Count;
1684 }
1685
1686 /*++
1687 * @name ExReinitializeResourceLite
1688 * @implemented NT4
1689 *
1690 * The ExReinitializeResourceLite routine routine reinitializes
1691 * an existing resource variable.
1692 *
1693 * @param Resource
1694 * Pointer to the resource to be reinitialized.
1695 *
1696 * @return STATUS_SUCCESS.
1697 *
1698 * @remarks With a single call to ExReinitializeResource, a driver writer can
1699 * replace three calls: one to ExDeleteResourceLite, another to
1700 * ExAllocatePool, and a third to ExInitializeResourceLite. As
1701 * contention for a resource variable increases, memory is dynamically
1702 * allocated and attached to the resource in order to track this
1703 * contention. As an optimization, ExReinitializeResourceLite retains
1704 * and zeroes this previously allocated memory.
1705 *
1706 * Callers of ExReinitializeResourceLite must be running at
1707 * IRQL <= DISPATCH_LEVEL.
1708 *
1709 *--*/
1710 NTSTATUS
1711 NTAPI
1712 ExReinitializeResourceLite(PERESOURCE Resource)
1713 {
1714 PKEVENT Event;
1715 PKSEMAPHORE Semaphore;
1716 ULONG i, Size;
1717 POWNER_ENTRY Owner;
1718
1719 /* Get the owner table */
1720 Owner = Resource->OwnerTable;
1721 if (Owner)
1722 {
1723 /* Get the size and loop it */
1724 Size = Owner->TableSize;
1725 for (i = 0; i < Size; i++)
1726 {
1727 /* Zero the table */
1728 Owner[i].OwnerThread = 0;
1729 Owner[i].OwnerCount = 0;
1730 }
1731 }
1732
1733 /* Zero the flags and count */
1734 Resource->Flag = 0;
1735 Resource->ActiveCount = 0;
1736
1737 /* Reset the semaphore */
1738 Semaphore = Resource->SharedWaiters;
1739 if (Semaphore) KeInitializeSemaphore(Semaphore, 0, MAXLONG);
1740
1741 /* Reset the event */
1742 Event = Resource->ExclusiveWaiters;
1743 if (Event) KeInitializeEvent(Event, SynchronizationEvent, FALSE);
1744
1745 /* Clear the resource data */
1746 Resource->OwnerThreads[0].OwnerThread = 0;
1747 Resource->OwnerThreads[1].OwnerThread = 0;
1748 Resource->OwnerThreads[0].OwnerCount = 0;
1749 Resource->OwnerThreads[1].OwnerCount = 0;
1750 Resource->ContentionCount = 0;
1751 Resource->NumberOfSharedWaiters = 0;
1752 Resource->NumberOfExclusiveWaiters = 0;
1753
1754 /* Reset the spinlock */
1755 KeInitializeSpinLock(&Resource->SpinLock);
1756 return STATUS_SUCCESS;
1757 }
1758
1759 /*++
1760 * @name ExReleaseResourceLite
1761 * @implemented NT4
1762 *
1763 * The ExReleaseResourceLite routine routine releases
1764 * a specified executive resource owned by the current thread.
1765 *
1766 * @param Resource
1767 * Pointer to the resource to be released.
1768 *
1769 * @return None.
1770 *
1771 * @remarks Callers of ExReleaseResourceLite must be running at
1772 * IRQL <= DISPATCH_LEVEL.
1773 *
1774 *--*/
1775 VOID
1776 FASTCALL
1777 ExReleaseResourceLite(PERESOURCE Resource)
1778 {
1779 ERESOURCE_THREAD Thread;
1780 ULONG Count, i;
1781 KIRQL OldIrql;
1782 POWNER_ENTRY Owner, Limit;
1783 DPRINT("ExReleaseResourceLite: %p\n", Resource);
1784
1785 /* Sanity check */
1786 ExpVerifyResource(Resource);
1787
1788 /* Get the thread and lock the resource */
1789 Thread = ExGetCurrentResourceThread();
1790 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1791 ExpCheckForApcsDisabled(TRUE, Resource, (PETHREAD)Thread);
1792
1793 /* Check if it's exclusively owned */
1794 if (IsOwnedExclusive(Resource))
1795 {
1796 /* Decrement owner count and check if we're done */
1797 ASSERT(Resource->OwnerThreads[0].OwnerCount > 0);
1798 ASSERT(Resource->ActiveCount == 1);
1799 if (--Resource->OwnerThreads[0].OwnerCount)
1800 {
1801 /* Done, release lock! */
1802 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1803 return;
1804 }
1805
1806 /* Clear the owner */
1807 Resource->OwnerThreads[0].OwnerThread = 0;
1808
1809 /* Check if there are shared waiters */
1810 if (IsSharedWaiting(Resource))
1811 {
1812 /* Remove the exclusive flag */
1813 Resource->Flag &= ~ResourceOwnedExclusive;
1814
1815 /* Give ownage to another thread */
1816 Count = Resource->NumberOfSharedWaiters;
1817 Resource->ActiveCount = (USHORT)Count;
1818 Resource->NumberOfSharedWaiters = 0;
1819
1820 /* Release lock and let someone else have it */
1821 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1822 KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE);
1823 return;
1824 }
1825 else if (IsExclusiveWaiting(Resource))
1826 {
1827 /* Give exclusive access */
1828 Resource->OwnerThreads[0].OwnerThread = 1;
1829 Resource->OwnerThreads[0].OwnerCount = 1;
1830 Resource->ActiveCount = 1;
1831 Resource->NumberOfExclusiveWaiters--;
1832
1833 /* Release the lock and give it away */
1834 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1835 KeSetEventBoostPriority(Resource->ExclusiveWaiters,
1836 (PKTHREAD*)
1837 &Resource->OwnerThreads[0].OwnerThread);
1838 return;
1839 }
1840
1841 /* Remove the exclusive flag */
1842 Resource->Flag &= ~ResourceOwnedExclusive;
1843
1844 Resource->ActiveCount = 0;
1845 }
1846 else
1847 {
1848 /* Check if we are in the thread list */
1849 if (Resource->OwnerThreads[1].OwnerThread == Thread)
1850 {
1851 /* Found it, get owner */
1852 Owner = &Resource->OwnerThreads[1];
1853 }
1854 else
1855 {
1856 /* Not in the list, do a full table look up */
1857 i = ((PKTHREAD)Thread)->ResourceIndex;
1858 Owner = Resource->OwnerTable;
1859 if (!Owner)
1860 {
1861 /* Nobody owns us, bugcheck! */
1862 KEBUGCHECKEX(RESOURCE_NOT_OWNED,
1863 (ULONG_PTR)Resource,
1864 Thread,
1865 (ULONG_PTR)Resource->OwnerTable,
1866 (ULONG_PTR)2);
1867 }
1868
1869 /* Check if we're out of the size and don't match */
1870 if ((i >= Owner->TableSize) || (Owner[i].OwnerThread != Thread))
1871 {
1872 /* Get the last entry */
1873 Limit = &Owner[Owner->TableSize];
1874 for (;;)
1875 {
1876 /* Move to the next entry */
1877 Owner++;
1878
1879 /* Check if we don't match */
1880 if (Owner >= Limit)
1881 {
1882 /* Nobody owns us, bugcheck! */
1883 KEBUGCHECKEX(RESOURCE_NOT_OWNED,
1884 (ULONG_PTR)Resource,
1885 Thread,
1886 (ULONG_PTR)Resource->OwnerTable,
1887 2);
1888 }
1889
1890 /* Check for a match */
1891 if (Owner->OwnerThread == Thread) break;
1892 }
1893 }
1894 else
1895 {
1896 /* Get the entry directly */
1897 Owner = &Owner[i];
1898 }
1899 }
1900
1901 /* Sanity checks */
1902 ASSERT(Owner->OwnerThread == Thread);
1903 ASSERT(Owner->OwnerCount > 0);
1904
1905 /* Check if we are the last owner */
1906 if (--Owner->OwnerCount)
1907 {
1908 /* Release lock */
1909 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1910 return;
1911 }
1912
1913 /* Clear owner */
1914 Owner->OwnerThread = 0;
1915
1916 /* See if the resource isn't being owned anymore */
1917 ASSERT(Resource->ActiveCount > 0);
1918 if (!(--Resource->ActiveCount))
1919 {
1920 /* Check if there's an exclusive waiter */
1921 if (IsExclusiveWaiting(Resource))
1922 {
1923 /* Give exclusive access */
1924 Resource->Flag |= ResourceOwnedExclusive;
1925 Resource->OwnerThreads[0].OwnerThread = 1;
1926 Resource->OwnerThreads[0].OwnerCount = 1;
1927 Resource->ActiveCount = 1;
1928 Resource->NumberOfExclusiveWaiters--;
1929
1930 /* Release the lock and give it away */
1931 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1932 KeSetEventBoostPriority(Resource->ExclusiveWaiters,
1933 (PKTHREAD*)
1934 &Resource->OwnerThreads[0].OwnerThread);
1935 return;
1936 }
1937 }
1938 }
1939
1940 /* Release lock */
1941 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1942 return;
1943 }
1944
1945 /*++
1946 * @name ExReleaseResourceForThreadLite
1947 * @implemented NT4
1948 *
1949 * The ExReleaseResourceForThreadLite routine routine releases
1950 * the input resource of the indicated thread.
1951 *
1952 * @param Resource
1953 * Pointer to the resource to be released.
1954 *
1955 * @param Thread
1956 * Identifies the thread that originally acquired the resource.
1957 *
1958 * @return None.
1959 *
1960 * @remarks Callers of ExReleaseResourceForThreadLite must be running at
1961 * IRQL <= DISPATCH_LEVEL.
1962 *
1963 *--*/
1964 VOID
1965 NTAPI
1966 ExReleaseResourceForThreadLite(PERESOURCE Resource,
1967 ERESOURCE_THREAD Thread)
1968 {
1969 ULONG i;
1970 ULONG Count;
1971 KIRQL OldIrql;
1972 POWNER_ENTRY Owner;
1973 ASSERT(Thread != 0);
1974 DPRINT("ExReleaseResourceForThreadLite: %p\n", Resource);
1975
1976 /* Get the thread and lock the resource */
1977 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
1978
1979 /* Sanity check */
1980 ExpVerifyResource(Resource);
1981
1982 /* Check if it's exclusively owned */
1983 if (IsOwnedExclusive(Resource))
1984 {
1985 /* Decrement owner count and check if we're done */
1986 ASSERT(Resource->OwnerThreads[0].OwnerThread == Thread);
1987 ASSERT(Resource->OwnerThreads[0].OwnerCount > 0);
1988 if (--Resource->OwnerThreads[0].OwnerCount)
1989 {
1990 /* Done, release lock! */
1991 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
1992 return;
1993 }
1994
1995 /* Clear the owner */
1996 Resource->OwnerThreads[0].OwnerThread = 0;
1997
1998 /* See if the resource isn't being owned anymore */
1999 ASSERT(Resource->ActiveCount > 0);
2000 if (!(--Resource->ActiveCount))
2001 {
2002 /* Check if there are shared waiters */
2003 if (IsSharedWaiting(Resource))
2004 {
2005 /* Remove the exclusive flag */
2006 Resource->Flag &= ~ResourceOwnedExclusive;
2007
2008 /* Give ownage to another thread */
2009 Count = Resource->NumberOfSharedWaiters;
2010 Resource->ActiveCount = (USHORT)Count;
2011 Resource->NumberOfSharedWaiters = 0;
2012
2013 /* Release lock and let someone else have it */
2014 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2015 KeReleaseSemaphore(Resource->SharedWaiters, 0, Count, FALSE);
2016 return;
2017 }
2018 else if (IsExclusiveWaiting(Resource))
2019 {
2020 /* Give exclusive access */
2021 Resource->OwnerThreads[0].OwnerThread = 1;
2022 Resource->OwnerThreads[0].OwnerCount = 1;
2023 Resource->ActiveCount = 1;
2024 Resource->NumberOfExclusiveWaiters--;
2025
2026 /* Release the lock and give it away */
2027 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2028 KeSetEventBoostPriority(Resource->ExclusiveWaiters,
2029 (PKTHREAD*)
2030 &Resource->OwnerThreads[0].OwnerThread);
2031 return;
2032 }
2033
2034 /* Remove the exclusive flag */
2035 Resource->Flag &= ~ResourceOwnedExclusive;
2036 }
2037 }
2038 else
2039 {
2040 /* Check if we are in the thread list */
2041 if (Resource->OwnerThreads[0].OwnerThread == Thread)
2042 {
2043 /* Found it, get owner */
2044 Owner = &Resource->OwnerThreads[0];
2045 }
2046 else if (Resource->OwnerThreads[1].OwnerThread == Thread)
2047 {
2048 /* Found it on the second list, get owner */
2049 Owner = &Resource->OwnerThreads[1];
2050 }
2051 else
2052 {
2053 /* Assume no valid index */
2054 i = 1;
2055
2056 /* If we got a valid pointer, try to get the resource index */
2057 if (!((ULONG)Thread & 3)) i = ((PKTHREAD)Thread)->ResourceIndex;
2058
2059 /* Do a table lookup */
2060 Owner = Resource->OwnerTable;
2061 ASSERT(Owner != NULL);
2062
2063 /* Check if we're out of the size and don't match */
2064 if ((i >= Owner->TableSize) || (Owner[i].OwnerThread != Thread))
2065 {
2066 /* Get the last entry */
2067 for (;;)
2068 {
2069 /* Move to the next entry */
2070 Owner++;
2071
2072 /* Check for a match */
2073 if (Owner->OwnerThread == Thread) break;
2074 }
2075 }
2076 else
2077 {
2078 /* Get the entry directly */
2079 Owner = &Owner[i];
2080 }
2081 }
2082
2083 /* Sanity checks */
2084 ASSERT(Owner->OwnerThread == Thread);
2085 ASSERT(Owner->OwnerCount > 0);
2086
2087 /* Check if we are the last owner */
2088 if (!(--Owner->OwnerCount))
2089 {
2090 /* Release lock */
2091 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2092 return;
2093 }
2094
2095 /* Clear owner */
2096 Owner->OwnerThread = 0;
2097
2098 /* See if the resource isn't being owned anymore */
2099 ASSERT(Resource->ActiveCount > 0);
2100 if (!(--Resource->ActiveCount))
2101 {
2102 /* Check if there's an exclusive waiter */
2103 if (IsExclusiveWaiting(Resource))
2104 {
2105 /* Give exclusive access */
2106 Resource->OwnerThreads[0].OwnerThread = 1;
2107 Resource->OwnerThreads[0].OwnerCount = 1;
2108 Resource->ActiveCount = 1;
2109 Resource->NumberOfExclusiveWaiters--;
2110
2111 /* Release the lock and give it away */
2112 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2113 KeSetEventBoostPriority(Resource->ExclusiveWaiters,
2114 (PKTHREAD*)
2115 &Resource->OwnerThreads[0].OwnerThread);
2116 return;
2117 }
2118 }
2119 }
2120
2121 /* Release lock */
2122 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2123 return;
2124 }
2125
2126 /*++
2127 * @name ExSetResourceOwnerPointer
2128 * @implemented NT4
2129 *
2130 * The ExSetResourceOwnerPointer routine routine sets the owner thread
2131 * thread pointer for an executive resource.
2132 *
2133 * @param Resource
2134 * Pointer to the resource whose owner to change.
2135 *
2136 * @param OwnerPointer
2137 * Pointer to an owner thread pointer of type ERESOURCE_THREAD.
2138 *
2139 * @return None.
2140 *
2141 * @remarks ExSetResourceOwnerPointer, used in conjunction with
2142 * ExReleaseResourceForThreadLite, provides a means for one thread
2143 * (acting as an resource manager thread) to acquire and release
2144 * resources for use by another thread (acting as a resource user
2145 * thread).
2146 *
2147 * After calling ExSetResourceOwnerPointer for a specific resource,
2148 * the only other routine that can be called for that resource is
2149 * ExReleaseResourceForThreadLite.
2150 *
2151 * Callers of ExSetResourceOwnerPointer must be running at
2152 * IRQL <= DISPATCH_LEVEL.
2153 *
2154 *--*/
2155 VOID
2156 NTAPI
2157 ExSetResourceOwnerPointer(IN PERESOURCE Resource,
2158 IN PVOID OwnerPointer)
2159 {
2160 ERESOURCE_THREAD Thread;
2161 KIRQL OldIrql;
2162 POWNER_ENTRY Owner, ThisOwner;
2163
2164 /* Sanity check */
2165 ASSERT((OwnerPointer != 0) && (((ULONG_PTR)OwnerPointer & 3) == 3));
2166
2167 /* Get the thread */
2168 Thread = ExGetCurrentResourceThread();
2169
2170 /* Sanity check */
2171 ExpVerifyResource(Resource);
2172
2173 /* Lock the resource */
2174 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
2175
2176 /* Check if it's exclusive */
2177 if (IsOwnedExclusive(Resource))
2178 {
2179 /* If it's exclusive, set the first entry no matter what */
2180 ASSERT(Resource->OwnerThreads[0].OwnerThread == Thread);
2181 ASSERT(Resource->OwnerThreads[0].OwnerCount > 0);
2182 Resource->OwnerThreads[0].OwnerThread = (ULONG_PTR)OwnerPointer;
2183 }
2184 else
2185 {
2186 /* Set the thread in both entries */
2187 ThisOwner = ExpFindEntryForThread(Resource,
2188 (ERESOURCE_THREAD)OwnerPointer,
2189 0);
2190 Owner = ExpFindEntryForThread(Resource, Thread, 0);
2191 if (!Owner)
2192 {
2193 /* Nobody owns it, crash */
2194 KEBUGCHECKEX(RESOURCE_NOT_OWNED,
2195 (ULONG_PTR)Resource,
2196 Thread,
2197 (ULONG_PTR)Resource->OwnerTable,
2198 3);
2199 }
2200
2201 /* Set if we are the owner */
2202 if (ThisOwner)
2203 {
2204 /* Update data */
2205 ThisOwner->OwnerCount += Owner->OwnerCount;
2206 Owner->OwnerCount = 0;
2207 Owner->OwnerThread = 0;
2208 ASSERT(Resource->ActiveCount >= 2);
2209 Resource->ActiveCount--;
2210 }
2211 }
2212
2213 /* Release the resource */
2214 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2215 }
2216
2217 /*++
2218 * @name ExTryToAcquireResourceExclusiveLite
2219 * @implemented NT4
2220 *
2221 * The ExTryToAcquireResourceExclusiveLite routine routine attemps to
2222 * acquire the given resource for exclusive access.
2223 *
2224 * @param Resource
2225 * Pointer to the resource to be acquired.
2226 *
2227 * @return TRUE if the given resource has been acquired for the caller.
2228 *
2229 * @remarks Callers of ExTryToAcquireResourceExclusiveLite must be running at
2230 * IRQL < DISPATCH_LEVEL.
2231 *
2232 *--*/
2233 BOOLEAN
2234 NTAPI
2235 ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource)
2236 {
2237 ERESOURCE_THREAD Thread;
2238 KIRQL OldIrql;
2239 BOOLEAN Acquired = FALSE;
2240 DPRINT("ExTryToAcquireResourceExclusiveLite: %p\n", Resource);
2241
2242 /* Sanity check */
2243 ASSERT((Resource->Flag & ResourceNeverExclusive) == 0);
2244
2245 /* Get the thread */
2246 Thread = ExGetCurrentResourceThread();
2247
2248 /* Sanity check and validation */
2249 ASSERT(KeIsExecutingDpc() == FALSE);
2250 ExpVerifyResource(Resource);
2251
2252 /* Acquire the lock */
2253 ExAcquireResourceLock(&Resource->SpinLock, &OldIrql);
2254
2255 /* Check if there is an owner */
2256 if (!Resource->ActiveCount)
2257 {
2258 /* No owner, give exclusive access */
2259 Resource->Flag |= ResourceOwnedExclusive;
2260 Resource->OwnerThreads[0].OwnerThread = Thread;
2261 Resource->OwnerThreads[0].OwnerCount = 1;
2262 Resource->ActiveCount = 1;
2263 Acquired = TRUE;
2264 }
2265 else if ((IsOwnedExclusive(Resource)) &&
2266 (Resource->OwnerThreads[0].OwnerThread == Thread))
2267 {
2268 /* Do a recursive acquire */
2269 Resource->OwnerThreads[0].OwnerCount++;
2270 Acquired = TRUE;
2271 }
2272
2273 /* Release the resource */
2274 ExReleaseResourceLock(&Resource->SpinLock, OldIrql);
2275 return Acquired;
2276 }
2277
2278 /*++
2279 * @name ExEnterCriticalRegionAndAcquireResourceExclusive
2280 * @implemented NT5.1
2281 *
2282 * The ExEnterCriticalRegionAndAcquireResourceExclusive enters a critical
2283 * region and then exclusively acquires a resource.
2284 *
2285 * @param Resource
2286 * Pointer to the resource to acquire.
2287 *
2288 * @return Pointer to the Win32K thread pointer of the current thread.
2289 *
2290 * @remarks See ExAcquireResourceExclusiveLite.
2291 *
2292 *--*/
2293 PVOID
2294 NTAPI
2295 ExEnterCriticalRegionAndAcquireResourceExclusive(PERESOURCE Resource)
2296 {
2297 /* Enter critical region */
2298 KeEnterCriticalRegion();
2299
2300 /* Acquire the resource */
2301 ExAcquireResourceExclusiveLite(Resource, TRUE);
2302
2303 /* Return the Win32 Thread */
2304 return KeGetCurrentThread()->Win32Thread;
2305 }
2306
2307 /*++
2308 * @name ExReleaseResourceAndLeaveCriticalRegion
2309 * @implemented NT5.1
2310 *
2311 * The ExReleaseResourceAndLeaveCriticalRegion release a resource and
2312 * then leaves a critical region.
2313 *
2314 * @param Resource
2315 * Pointer to the resource to release.
2316 *
2317 * @return None
2318 *
2319 * @remarks See ExReleaseResourceLite.
2320 *
2321 *--*/
2322 VOID
2323 FASTCALL
2324 ExReleaseResourceAndLeaveCriticalRegion(PERESOURCE Resource)
2325 {
2326 /* Release the resource */
2327 ExReleaseResourceLite(Resource);
2328
2329 /* Leave critical region */
2330 KeLeaveCriticalRegion();
2331 }
2332
2333 /* EOF */
2334