3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS kernel
5 * FILE: ntoskrnl/ex/resource.c
6 * PURPOSE: Resource synchronization construct
14 * Usage of ERESOURCE members is not documented.
15 * From names of members and functionnalities, we can assume :
17 * OwnerTable = list of threads who have shared access(if more than one)
18 * ActiveCount = number of threads who have access to the resource
19 * Flag = bits : ResourceOwnedExclusive=0x80
20 * ResourceNeverExclusive=0x10
21 * ResourceReleaseByOtherThread=0x20
22 * ResourceDisableBoost=0x08
23 * SharedWaiters = semaphore, used to manage wait list of shared waiters.
24 * ExclusiveWaiters = event, used to manage wait list of exclusive waiters.
25 * OwnerThreads[0]= thread who have exclusive access
26 * OwnerThreads[1]= if only one thread own the resource
27 * thread who have shared access
30 * and TableSize = number of entries in the owner table
31 * NumberOfExclusiveWaiters = number of threads waiting for exclusive access.
32 * NumberOfSharedWaiters = number of threads waiting for exclusive access.
36 #define ResourceOwnedExclusive 0x80
37 #define ResourceDisableBoost 0x08
40 //#define NONAMELESSUNION
44 /* INCLUDES *****************************************************************/
46 #include <ddk/ntddk.h>
47 #include <internal/ke.h>
48 #include <internal/pool.h>
51 #include <internal/debug.h>
53 /* GLOBALS *******************************************************************/
55 #define TAG_OWNER_TABLE TAG('R', 'O', 'W', 'N')
56 #define TAG_EXCLUSIVE_LOCK TAG('E', 'R', 'E', 'L')
57 #define TAG_SHARED_SEM TAG('E', 'R', 'S', 'S')
59 /* FUNCTIONS *****************************************************************/
64 ExTryToAcquireResourceExclusiveLite (
68 * FUNCTION: Attempts to require the resource for exclusive access
70 * Resource = Points to the resource of be acquired
71 * RETURNS: TRUE if the resource was acquired for the caller
72 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
75 return(ExAcquireResourceExclusiveLite(Resource,FALSE));
78 #ifdef ExAcquireResourceExclusive
79 #undef ExAcquireResourceExclusive
87 ExAcquireResourceExclusive (
92 return(ExAcquireResourceExclusiveLite(Resource,Wait));
101 ExAcquireResourceExclusiveLite (
106 * FUNCTION: Acquires a resource exclusively for the calling thread
108 * Resource = Points to the resource to acquire
109 * Wait = Is set to TRUE if the caller should wait to acquire the
110 * resource if it can't be acquired immediately
111 * RETURNS: TRUE if the resource was acquired,
113 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
118 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
121 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
123 /* resource already locked */
124 if((Resource->Flag & ResourceOwnedExclusive)
125 && Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
127 /* it's ok : same lock for same thread */
128 Resource->OwnerThreads[0].OwnerCount++;
129 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
130 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
134 if (Resource->ActiveCount && !Wait)
136 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
137 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
142 * This is slightly better than it looks because other exclusive
143 * threads who are waiting won't be woken up but there is a race
144 * with new threads trying to grab the resource so we must have
145 * the spinlock, still normally this loop will only be executed
147 * NOTE: We might want to set a timeout to detect deadlock
150 while (Resource->ActiveCount)
152 Resource->NumberOfExclusiveWaiters++;
153 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
154 KeWaitForSingleObject(Resource->ExclusiveWaiters,
159 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
160 Resource->NumberOfExclusiveWaiters--;
162 Resource->Flag |= ResourceOwnedExclusive;
163 Resource->ActiveCount = 1;
164 Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread();
165 Resource->OwnerThreads[0].OwnerCount = 1;
166 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
167 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
171 static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
172 ERESOURCE_THREAD ResourceThreadId)
174 * FUNCTION: Removes the current thread from the shared owners of the resource
176 * Resource = Pointer to the resource for which the thread is to be
178 * NOTE: Must be called with the resource spinlock held
183 if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
185 Resource->OwnerThreads[1].OwnerCount--;
186 if (Resource->OwnerThreads[1].OwnerCount == 0)
188 Resource->ActiveCount--;
189 Resource->OwnerThreads[1].OwnerThread = 0;
194 if (Resource->OwnerThreads[1].OwnerThread)
196 /* Oh dear, the caller didn't own the resource after all */
200 for (i=0; i<Resource->OwnerThreads[1].TableSize; i++)
202 if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
204 Resource->OwnerTable[i].OwnerCount--;
205 if (Resource->OwnerTable[i].OwnerCount == 0)
207 Resource->ActiveCount--;
208 Resource->OwnerTable[i].OwnerThread = 0;
216 static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
218 * FUNCTION: Adds the current thread to the shared owners of the resource
220 * Resource = Pointer to the resource for which the thread is to be
222 * NOTE: Must be called with the resource spinlock held
225 ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
226 POWNER_ENTRY freeEntry;
229 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource);
231 if (Resource->ActiveCount == 0)
233 /* no owner, it's easy */
234 Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread();
235 Resource->OwnerThreads[1].OwnerCount = 1;
236 if (Resource->OwnerTable != NULL)
238 ExFreePool(Resource->OwnerTable);
240 Resource->OwnerTable = NULL;
241 Resource->ActiveCount = 1;
242 DPRINT("EiAddSharedOwner() = TRUE\n");
247 * now, we must search if this thread has already acquired this resource
248 * then increase ownercount if found, else create new entry or reuse free
251 if (Resource->OwnerTable == NULL)
253 DPRINT("Creating owner table\n");
255 /* allocate ownertable,memset to 0, initialize first entry */
256 Resource->OwnerTable =
257 ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3,
259 if (Resource->OwnerTable == NULL)
264 memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
265 memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
266 sizeof(OWNER_ENTRY));
268 Resource->OwnerThreads[1].OwnerThread = 0;
269 Resource->OwnerThreads[1].TableSize = 3;
271 Resource->OwnerTable[1].OwnerThread = CurrentThread;
272 Resource->OwnerTable[1].OwnerCount = 1;
273 Resource->ActiveCount++;
278 DPRINT("Search free entries\n");
280 DPRINT("Number of entries %d\n",
281 Resource->OwnerThreads[1].TableSize);
284 for (i=0; i<Resource->OwnerThreads[1].TableSize; i++)
286 if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
288 DPRINT("Thread already owns resource\n");
289 Resource->OwnerTable[i].OwnerCount++;
292 if (Resource->OwnerTable[i].OwnerThread == 0)
294 freeEntry = &Resource->OwnerTable[i];
299 DPRINT("Found free entry %x\n", freeEntry);
303 DPRINT("Allocating new entry\n");
305 /* reallocate ownertable with one more entry */
307 ExAllocatePoolWithTag(NonPagedPool,
309 (Resource->OwnerThreads[1].TableSize+1),
311 if (freeEntry == NULL)
316 memcpy(freeEntry,Resource->OwnerTable,
317 sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].TableSize));
318 ExFreePool(Resource->OwnerTable);
319 Resource->OwnerTable=freeEntry;
320 freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].TableSize];
321 Resource->OwnerThreads[1].TableSize++;
323 DPRINT("Creating entry\n");
324 freeEntry->OwnerThread=ExGetCurrentResourceThread();
325 freeEntry->OwnerCount=1;
326 Resource->ActiveCount++;
335 ExAcquireResourceSharedLite (
340 * FUNCTION: Acquires the given resource for shared access by the calling
343 * Resource = Points to the resource to acquire
344 * Wait = Is set to TRUE if the caller should be put into wait state
345 * until the resource can be acquired if it cannot be acquired
347 * RETURNS: TRUE, if the resource is acquire
353 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
356 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
358 /* first, resolve trivial cases */
359 if (Resource->ActiveCount == 0)
361 EiAddSharedOwner(Resource);
362 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
363 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
367 if ((Resource->Flag & ResourceOwnedExclusive)
368 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
370 /* exclusive, but by same thread : it's ok */
372 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
374 Resource->OwnerThreads[0].OwnerCount++;
375 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
376 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
380 if ((Resource->Flag & ResourceOwnedExclusive)
381 || Resource->NumberOfExclusiveWaiters)
383 /* exclusive by another thread , or thread waiting for exclusive */
386 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
387 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
392 Resource->NumberOfSharedWaiters++;
395 /* wait for the semaphore */
396 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
397 KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL);
398 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
399 /* the spin lock was released we must check again */
401 while ((Resource->Flag & ResourceOwnedExclusive)
402 || Resource->NumberOfExclusiveWaiters);
403 Resource->NumberOfSharedWaiters--;
407 EiAddSharedOwner(Resource);
408 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
409 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
418 ExConvertExclusiveToSharedLite (
422 * FUNCTION: Converts a given resource from acquired for exclusive access
423 * to acquire for shared access
425 * Resource = Points to the resource for which the access should be
427 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
433 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource);
435 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
437 oldWaiters = Resource->NumberOfSharedWaiters;
439 if (!(Resource->Flag & ResourceOwnedExclusive))
441 /* Might not be what the caller expects, better bug check */
443 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
447 //transfer infos from entry 0 to entry 1 and erase entry 0
448 Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread;
449 Resource->OwnerThreads[1].OwnerCount=Resource->OwnerThreads[0].OwnerCount;
450 Resource->OwnerThreads[0].OwnerThread=0;
451 Resource->OwnerThreads[0].OwnerCount=0;
452 /* erase exclusive flag */
453 Resource->Flag &= (~ResourceOwnedExclusive);
454 /* if no shared waiters, that's all */
457 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
460 /* else, awake the waiters */
461 KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0);
462 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
463 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
471 ExDisableResourceBoostLite (
475 Resource->Flag |= ResourceDisableBoost;
483 ExGetExclusiveWaiterCount (
487 return(Resource->NumberOfExclusiveWaiters);
495 ExAcquireSharedStarveExclusive (
500 * FUNCTION: Acquires a given resource for shared access without waiting
501 * for any pending attempts to acquire exclusive access to the
504 * Resource = Points to the resource to be acquired for shared access
505 * Wait = Is set to TRUE if the caller will wait until the resource
506 * becomes available when access can't be granted immediately
507 * RETURNS: TRUE if the requested access is granted. The routine returns
508 * FALSE if the input Wait is FALSE and shared access can't be
509 * granted immediately
514 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
517 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
519 /* no owner, it's easy */
520 if (Resource->ActiveCount == 0)
522 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
523 Resource->OwnerThreads[1].OwnerCount=1;
524 Resource->ActiveCount=1;
525 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
526 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
530 if ((Resource->Flag & ResourceOwnedExclusive)
531 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
533 /* exclusive, but by same thread : it's ok */
534 Resource->OwnerThreads[0].OwnerCount++;
535 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
536 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
540 if (Resource->Flag & ResourceOwnedExclusive)
542 /* exclusive by another thread */
545 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
546 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
551 Resource->NumberOfSharedWaiters++;
552 /* wait for the semaphore */
553 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
554 KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0);
555 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
556 Resource->NumberOfSharedWaiters--;
559 EiAddSharedOwner(Resource);
560 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
561 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
570 ExAcquireSharedWaitForExclusive (
575 return(ExAcquireResourceSharedLite(Resource,Wait));
579 #ifdef ExDeleteResource
580 #undef ExDeleteResource
593 return(ExDeleteResourceLite(Resource));
601 ExDeleteResourceLite (
605 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource);
606 if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
607 if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
608 if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
609 return(STATUS_SUCCESS);;
617 ExGetSharedWaiterCount (
621 return(Resource->NumberOfSharedWaiters);
625 #ifdef ExInitializeResource
626 #undef ExInitializeResource
634 ExInitializeResource (
638 return(ExInitializeResourceLite(Resource));
645 ExInitializeResourceLite (PERESOURCE Resource)
647 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource);
648 memset(Resource,0,sizeof(ERESOURCE));
649 Resource->NumberOfSharedWaiters = 0;
650 Resource->NumberOfExclusiveWaiters = 0;
651 KeInitializeSpinLock(&Resource->SpinLock);
653 Resource->ExclusiveWaiters =
654 ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK);
655 KeInitializeEvent(Resource->ExclusiveWaiters,
656 SynchronizationEvent,
658 Resource->SharedWaiters =
659 ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM);
660 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
661 Resource->ActiveCount = 0;
670 ExIsResourceAcquiredExclusiveLite (
674 * FUNCTION: Returns whether the current thread has exclusive access to
677 * Resource = Points to the resource to be queried
678 * RETURNS: TRUE if the caller has exclusive access to the resource,
682 return((Resource->Flag & ResourceOwnedExclusive)
683 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
688 #ifdef ExIsResourceAcquiredSharedLite
689 #undef ExIsResourceAcquiredSharedLite
701 ExIsResourceAcquiredSharedLite(
702 IN PERESOURCE Resource)
704 * FUNCTION: Returns whether the current thread has shared access to a given
707 * Resource = Points to the resource to be queried
708 * RETURNS: The number of times the caller has acquired shared access to the
713 if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
715 return(Resource->OwnerThreads[0].OwnerCount);
717 if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread())
719 return(Resource->OwnerThreads[1].OwnerCount);
721 if (!Resource->OwnerThreads[1].TableSize)
725 for (i=0; i<Resource->OwnerThreads[1].TableSize; i++)
727 if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
729 return Resource->OwnerTable[i].OwnerCount;
740 ExReinitializeResourceLite (
744 Resource->NumberOfSharedWaiters = 0;
745 Resource->NumberOfExclusiveWaiters = 0;
746 KeInitializeSpinLock(&Resource->SpinLock);
748 KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
750 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
751 Resource->ActiveCount = 0;
752 if (Resource->OwnerTable)
754 ExFreePool(Resource->OwnerTable);
756 Resource->OwnerThreads[0].OwnerThread=0;
757 Resource->OwnerThreads[0].OwnerCount=0;
758 Resource->OwnerThreads[1].OwnerThread=0;
759 Resource->OwnerThreads[1].OwnerCount=0;
767 ExReleaseResourceLite (
771 return(ExReleaseResourceForThreadLite(Resource,
772 ExGetCurrentResourceThread()));
777 #ifdef ExReleaseResourceForThread
778 #undef ExReleaseResourceForThread
787 ExReleaseResourceForThread (
789 ERESOURCE_THREAD ResourceThreadId
792 return(ExReleaseResourceForThreadLite(Resource,ResourceThreadId));
801 ExReleaseResourceForThreadLite (
803 ERESOURCE_THREAD ResourceThreadId
806 * FUNCTION: Releases a resource for the given thread
808 * Resource = Points to the release to release
809 * ResourceThreadId = Identifies the thread that originally acquired
811 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
812 * BUG: We don't support starving exclusive waiters
817 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
818 Resource, ResourceThreadId);
820 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
822 if (Resource->Flag & ResourceOwnedExclusive)
824 DPRINT("Releasing from exclusive access\n");
826 Resource->OwnerThreads[0].OwnerCount--;
827 if (Resource->OwnerThreads[0].OwnerCount > 0)
829 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
830 DPRINT("ExReleaseResourceForThreadLite() finished\n");
834 Resource->OwnerThreads[0].OwnerThread = 0;
835 Resource->ActiveCount--;
836 Resource->Flag &=(~ResourceOwnedExclusive);
837 assert(Resource->ActiveCount == 0);
838 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
839 Resource->NumberOfExclusiveWaiters);
840 if (Resource->NumberOfExclusiveWaiters)
842 /* get resource to first exclusive waiter */
843 KeSetEvent(Resource->ExclusiveWaiters,
846 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
847 DPRINT("ExReleaseResourceForThreadLite() finished\n");
850 DPRINT("Resource->NumberOfSharedWaiters %d\n",
851 Resource->NumberOfSharedWaiters);
852 if (Resource->NumberOfSharedWaiters)
854 DPRINT("Releasing semaphore\n");
855 KeReleaseSemaphore(Resource->SharedWaiters,
857 Resource->NumberOfSharedWaiters,
860 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
861 DPRINT("ExReleaseResourceForThreadLite() finished\n");
865 EiRemoveSharedOwner(Resource, ResourceThreadId);
867 if (Resource->ActiveCount == 0)
869 if (Resource->NumberOfExclusiveWaiters)
871 /* get resource to first exclusive waiter */
872 KeSetEvent(Resource->ExclusiveWaiters,
878 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
879 DPRINT("ExReleaseResourceForThreadLite() finished\n");
888 ExSetResourceOwnerPointer (
889 IN PERESOURCE Resource,
890 IN PVOID OwnerPointer