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
39 /* INCLUDES *****************************************************************/
41 #include <ddk/ntddk.h>
42 #include <internal/ke.h>
43 #include <internal/pool.h>
46 #include <internal/debug.h>
48 /* GLOBALS *******************************************************************/
50 #define TAG_OWNER_TABLE TAG('R', 'O', 'W', 'N')
51 #define TAG_EXCLUSIVE_LOCK TAG('E', 'R', 'E', 'L')
52 #define TAG_SHARED_SEM TAG('E', 'R', 'S', 'S')
54 /* FUNCTIONS *****************************************************************/
59 ExTryToAcquireResourceExclusiveLite (
63 * FUNCTION: Attempts to require the resource for exclusive access
65 * Resource = Points to the resource of be acquired
66 * RETURNS: TRUE if the resource was acquired for the caller
67 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
70 return(ExAcquireResourceExclusiveLite(Resource,FALSE));
75 ExAcquireResourceExclusive (
80 return(ExAcquireResourceExclusiveLite(Resource,Wait));
85 ExAcquireResourceExclusiveLite (
90 * FUNCTION: Acquires a resource exclusively for the calling thread
92 * Resource = Points to the resource to acquire
93 * Wait = Is set to TRUE if the caller should wait to acquire the
94 * resource if it can't be acquired immediately
95 * RETURNS: TRUE if the resource was acquired,
97 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
102 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
105 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
107 /* resource already locked */
108 if((Resource->Flag & ResourceOwnedExclusive)
109 && Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
111 /* it's ok : same lock for same thread */
112 Resource->OwnerThreads[0].a.OwnerCount++;
113 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
114 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
118 if (Resource->ActiveCount && !Wait)
120 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
121 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
126 * This is slightly better than it looks because other exclusive
127 * threads who are waiting won't be woken up but there is a race
128 * with new threads trying to grab the resource so we must have
129 * the spinlock, still normally this loop will only be executed
131 * NOTE: We might want to set a timeout to detect deadlock
134 while (Resource->ActiveCount)
136 Resource->NumberOfExclusiveWaiters++;
137 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
138 KeWaitForSingleObject(Resource->ExclusiveWaiters,
143 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
144 Resource->NumberOfExclusiveWaiters--;
146 Resource->Flag |= ResourceOwnedExclusive;
147 Resource->ActiveCount = 1;
148 Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread();
149 Resource->OwnerThreads[0].a.OwnerCount = 1;
150 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
151 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
155 static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
156 ERESOURCE_THREAD ResourceThreadId)
158 * FUNCTION: Removes the current thread from the shared owners of the resource
160 * Resource = Pointer to the resource for which the thread is to be
162 * NOTE: Must be called with the resource spinlock held
167 if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
169 Resource->OwnerThreads[1].a.OwnerCount--;
170 if (Resource->OwnerThreads[1].a.OwnerCount == 0)
172 Resource->ActiveCount--;
173 Resource->OwnerThreads[1].OwnerThread = 0;
178 if (Resource->OwnerThreads[1].OwnerThread)
180 /* Oh dear, the caller didn't own the resource after all */
184 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
186 if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
188 Resource->OwnerTable[i].a.OwnerCount--;
189 if (Resource->OwnerTable[i].a.OwnerCount == 0)
191 Resource->ActiveCount--;
192 Resource->OwnerTable[i].OwnerThread = 0;
200 static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
202 * FUNCTION: Adds the current thread to the shared owners of the resource
204 * Resource = Pointer to the resource for which the thread is to be
206 * NOTE: Must be called with the resource spinlock held
209 ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
210 POWNER_ENTRY freeEntry;
213 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource);
215 if (Resource->ActiveCount == 0)
217 /* no owner, it's easy */
218 Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread();
219 Resource->OwnerThreads[1].a.OwnerCount = 1;
220 if (Resource->OwnerTable != NULL)
222 ExFreePool(Resource->OwnerTable);
224 Resource->OwnerTable = NULL;
225 Resource->ActiveCount = 1;
226 DPRINT("EiAddSharedOwner() = TRUE\n");
231 * now, we must search if this thread has already acquired this resource
232 * then increase ownercount if found, else create new entry or reuse free
235 if (Resource->OwnerTable == NULL)
237 DPRINT("Creating owner table\n");
239 /* allocate ownertable,memset to 0, initialize first entry */
240 Resource->OwnerTable =
241 ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3,
243 if (Resource->OwnerTable == NULL)
248 memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
249 memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
250 sizeof(OWNER_ENTRY));
252 Resource->OwnerThreads[1].OwnerThread = 0;
253 Resource->OwnerThreads[1].a.TableSize = 3;
255 Resource->OwnerTable[1].OwnerThread = CurrentThread;
256 Resource->OwnerTable[1].a.OwnerCount = 1;
257 Resource->ActiveCount++;
262 DPRINT("Search free entries\n");
264 DPRINT("Number of entries %d\n",
265 Resource->OwnerThreads[1].a.TableSize);
268 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
270 if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
272 DPRINT("Thread already owns resource\n");
273 Resource->OwnerTable[i].a.OwnerCount++;
276 if (Resource->OwnerTable[i].OwnerThread == 0)
278 freeEntry = &Resource->OwnerTable[i];
283 DPRINT("Found free entry %x\n", freeEntry);
287 DPRINT("Allocating new entry\n");
289 /* reallocate ownertable with one more entry */
291 ExAllocatePoolWithTag(NonPagedPool,
293 (Resource->OwnerThreads[1].a.TableSize+1),
295 if (freeEntry == NULL)
300 memcpy(freeEntry,Resource->OwnerTable,
301 sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].a.TableSize));
302 ExFreePool(Resource->OwnerTable);
303 Resource->OwnerTable=freeEntry;
304 freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].a.TableSize];
305 Resource->OwnerThreads[1].a.TableSize++;
307 DPRINT("Creating entry\n");
308 freeEntry->OwnerThread=ExGetCurrentResourceThread();
309 freeEntry->a.OwnerCount=1;
310 Resource->ActiveCount++;
316 ExAcquireResourceSharedLite (
321 * FUNCTION: Acquires the given resource for shared access by the calling
324 * Resource = Points to the resource to acquire
325 * Wait = Is set to TRUE if the caller should be put into wait state
326 * until the resource can be acquired if it cannot be acquired
328 * RETURNS: TRUE, if the resource is acquire
334 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
337 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
339 /* first, resolve trivial cases */
340 if (Resource->ActiveCount == 0)
342 EiAddSharedOwner(Resource);
343 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
344 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
348 if ((Resource->Flag & ResourceOwnedExclusive)
349 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
351 /* exclusive, but by same thread : it's ok */
353 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
355 Resource->OwnerThreads[0].a.OwnerCount++;
356 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
357 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
361 if ((Resource->Flag & ResourceOwnedExclusive)
362 || Resource->NumberOfExclusiveWaiters)
364 /* exclusive by another thread , or thread waiting for exclusive */
367 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
368 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
373 Resource->NumberOfSharedWaiters++;
376 /* wait for the semaphore */
377 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
378 KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL);
379 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
380 /* the spin lock was released we must check again */
382 while ((Resource->Flag & ResourceOwnedExclusive)
383 || Resource->NumberOfExclusiveWaiters);
384 Resource->NumberOfSharedWaiters--;
388 EiAddSharedOwner(Resource);
389 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
390 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
396 ExConvertExclusiveToSharedLite (
400 * FUNCTION: Converts a given resource from acquired for exclusive access
401 * to acquire for shared access
403 * Resource = Points to the resource for which the access should be
405 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
411 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource);
413 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
415 oldWaiters = Resource->NumberOfSharedWaiters;
417 if (!(Resource->Flag & ResourceOwnedExclusive))
419 /* Might not be what the caller expects, better bug check */
421 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
425 //transfer infos from entry 0 to entry 1 and erase entry 0
426 Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread;
427 Resource->OwnerThreads[1].a.OwnerCount=Resource->OwnerThreads[0].a.OwnerCount;
428 Resource->OwnerThreads[0].OwnerThread=0;
429 Resource->OwnerThreads[0].a.OwnerCount=0;
430 /* erase exclusive flag */
431 Resource->Flag &= (~ResourceOwnedExclusive);
432 /* if no shared waiters, that's all */
435 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
438 /* else, awake the waiters */
439 KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0);
440 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
441 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
446 ExDisableResourceBoostLite (
450 Resource->Flag |= ResourceDisableBoost;
455 ExGetExclusiveWaiterCount (
459 return(Resource->NumberOfExclusiveWaiters);
464 ExAcquireSharedStarveExclusive (
469 * FUNCTION: Acquires a given resource for shared access without waiting
470 * for any pending attempts to acquire exclusive access to the
473 * Resource = Points to the resource to be acquired for shared access
474 * Wait = Is set to TRUE if the caller will wait until the resource
475 * becomes available when access can't be granted immediately
476 * RETURNS: TRUE if the requested access is granted. The routine returns
477 * FALSE if the input Wait is FALSE and shared access can't be
478 * granted immediately
483 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
486 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
488 /* no owner, it's easy */
489 if (Resource->ActiveCount == 0)
491 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
492 Resource->OwnerThreads[1].a.OwnerCount=1;
493 Resource->ActiveCount=1;
494 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
495 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
499 if ((Resource->Flag & ResourceOwnedExclusive)
500 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
502 /* exclusive, but by same thread : it's ok */
503 Resource->OwnerThreads[0].a.OwnerCount++;
504 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
505 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
509 if (Resource->Flag & ResourceOwnedExclusive)
511 /* exclusive by another thread */
514 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
515 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
520 Resource->NumberOfSharedWaiters++;
521 /* wait for the semaphore */
522 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
523 KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0);
524 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
525 Resource->NumberOfSharedWaiters--;
528 EiAddSharedOwner(Resource);
529 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
530 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
536 ExAcquireSharedWaitForExclusive (
541 return(ExAcquireResourceSharedLite(Resource,Wait));
550 return(ExDeleteResourceLite(Resource));
555 ExDeleteResourceLite (
559 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource);
560 if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
561 if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
562 if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
563 return(STATUS_SUCCESS);;
568 ExGetSharedWaiterCount (
572 return(Resource->NumberOfSharedWaiters);
577 ExInitializeResource (
581 return(ExInitializeResourceLite(Resource));
585 ExInitializeResourceLite (PERESOURCE Resource)
587 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource);
588 memset(Resource,0,sizeof(ERESOURCE));
589 Resource->NumberOfSharedWaiters = 0;
590 Resource->NumberOfExclusiveWaiters = 0;
591 KeInitializeSpinLock(&Resource->SpinLock);
593 Resource->ExclusiveWaiters =
594 ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK);
595 KeInitializeEvent(Resource->ExclusiveWaiters,
596 SynchronizationEvent,
598 Resource->SharedWaiters =
599 ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM);
600 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
601 Resource->ActiveCount = 0;
607 ExIsResourceAcquiredExclusiveLite (
611 * FUNCTION: Returns whether the current thread has exclusive access to
614 * Resource = Points to the resource to be queried
615 * RETURNS: TRUE if the caller has exclusive access to the resource,
619 return((Resource->Flag & ResourceOwnedExclusive)
620 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
625 ExIsResourceAcquiredSharedLite (
629 * FUNCTION: Returns whether the current thread has shared access to a given
632 * Resource = Points to the resource to be queried
633 * RETURNS: The number of times the caller has acquired shared access to the
638 if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
640 return(Resource->OwnerThreads[0].a.OwnerCount);
642 if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread())
644 return(Resource->OwnerThreads[1].a.OwnerCount);
646 if (!Resource->OwnerThreads[1].a.TableSize)
650 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
652 if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
654 return Resource->OwnerTable[i].a.OwnerCount;
662 ExReinitializeResourceLite (
666 Resource->NumberOfSharedWaiters = 0;
667 Resource->NumberOfExclusiveWaiters = 0;
668 KeInitializeSpinLock(&Resource->SpinLock);
670 KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
672 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
673 Resource->ActiveCount = 0;
674 if (Resource->OwnerTable)
676 ExFreePool(Resource->OwnerTable);
678 Resource->OwnerThreads[0].OwnerThread=0;
679 Resource->OwnerThreads[0].a.OwnerCount=0;
680 Resource->OwnerThreads[1].OwnerThread=0;
681 Resource->OwnerThreads[1].a.OwnerCount=0;
686 ExReleaseResourceLite (
690 return(ExReleaseResourceForThreadLite(Resource,
691 ExGetCurrentResourceThread()));
696 ExReleaseResourceForThread (
698 ERESOURCE_THREAD ResourceThreadId
701 return(ExReleaseResourceForThreadLite(Resource,ResourceThreadId));
706 ExReleaseResourceForThreadLite (
708 ERESOURCE_THREAD ResourceThreadId
711 * FUNCTION: Releases a resource for the given thread
713 * Resource = Points to the release to release
714 * ResourceThreadId = Identifies the thread that originally acquired
716 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
717 * BUG: We don't support starving exclusive waiters
722 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
723 Resource, ResourceThreadId);
725 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
727 if (Resource->Flag & ResourceOwnedExclusive)
729 DPRINT("Releasing from exclusive access\n");
731 Resource->OwnerThreads[0].a.OwnerCount--;
732 if (Resource->OwnerThreads[0].a.OwnerCount > 0)
734 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
735 DPRINT("ExReleaseResourceForThreadLite() finished\n");
739 Resource->OwnerThreads[0].OwnerThread = 0;
740 Resource->ActiveCount--;
741 Resource->Flag &=(~ResourceOwnedExclusive);
742 assert(Resource->ActiveCount == 0);
743 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
744 Resource->NumberOfExclusiveWaiters);
745 if (Resource->NumberOfExclusiveWaiters)
747 /* get resource to first exclusive waiter */
748 KeSetEvent(Resource->ExclusiveWaiters,
751 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
752 DPRINT("ExReleaseResourceForThreadLite() finished\n");
755 DPRINT("Resource->NumberOfSharedWaiters %d\n",
756 Resource->NumberOfSharedWaiters);
757 if (Resource->NumberOfSharedWaiters)
759 DPRINT("Releasing semaphore\n");
760 KeReleaseSemaphore(Resource->SharedWaiters,
762 Resource->NumberOfSharedWaiters,
765 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
766 DPRINT("ExReleaseResourceForThreadLite() finished\n");
770 EiRemoveSharedOwner(Resource, ResourceThreadId);
772 if (Resource->ActiveCount == 0)
774 if (Resource->NumberOfExclusiveWaiters)
776 /* get resource to first exclusive waiter */
777 KeSetEvent(Resource->ExclusiveWaiters,
783 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
784 DPRINT("ExReleaseResourceForThreadLite() finished\n");
790 ExSetResourceOwnerPointer (
791 IN PERESOURCE Resource,
792 IN PVOID OwnerPointer