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