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