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 *****************************************************************/
60 ExTryToAcquireResourceExclusiveLite (
64 * FUNCTION: Attempts to require the resource for exclusive access
66 * Resource = Points to the resource of be acquired
67 * RETURNS: TRUE if the resource was acquired for the caller
68 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
71 return(ExAcquireResourceExclusiveLite(Resource,FALSE));
76 ExAcquireResourceExclusive (
81 return(ExAcquireResourceExclusiveLite(Resource,Wait));
84 #endif /* LIBCAPTIVE */
88 ExAcquireResourceExclusiveLite (
93 * FUNCTION: Acquires a resource exclusively for the calling thread
95 * Resource = Points to the resource to acquire
96 * Wait = Is set to TRUE if the caller should wait to acquire the
97 * resource if it can't be acquired immediately
98 * RETURNS: TRUE if the resource was acquired,
100 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
105 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
108 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
110 /* resource already locked */
111 if((Resource->Flag & ResourceOwnedExclusive)
112 && Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
114 /* it's ok : same lock for same thread */
115 Resource->OwnerThreads[0].a.OwnerCount++;
116 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
117 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
121 if (Resource->ActiveCount && !Wait)
123 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
124 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
129 * This is slightly better than it looks because other exclusive
130 * threads who are waiting won't be woken up but there is a race
131 * with new threads trying to grab the resource so we must have
132 * the spinlock, still normally this loop will only be executed
134 * NOTE: We might want to set a timeout to detect deadlock
137 while (Resource->ActiveCount)
139 Resource->NumberOfExclusiveWaiters++;
140 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
141 KeWaitForSingleObject(Resource->ExclusiveWaiters,
146 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
147 Resource->NumberOfExclusiveWaiters--;
149 Resource->Flag |= ResourceOwnedExclusive;
150 Resource->ActiveCount = 1;
151 Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread();
152 Resource->OwnerThreads[0].a.OwnerCount = 1;
153 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
154 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
160 static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
161 ERESOURCE_THREAD ResourceThreadId)
163 * FUNCTION: Removes the current thread from the shared owners of the resource
165 * Resource = Pointer to the resource for which the thread is to be
167 * NOTE: Must be called with the resource spinlock held
172 if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
174 Resource->OwnerThreads[1].a.OwnerCount--;
175 if (Resource->OwnerThreads[1].a.OwnerCount == 0)
177 Resource->ActiveCount--;
178 Resource->OwnerThreads[1].OwnerThread = 0;
183 if (Resource->OwnerThreads[1].OwnerThread)
185 /* Oh dear, the caller didn't own the resource after all */
189 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
191 if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
193 Resource->OwnerTable[i].a.OwnerCount--;
194 if (Resource->OwnerTable[i].a.OwnerCount == 0)
196 Resource->ActiveCount--;
197 Resource->OwnerTable[i].OwnerThread = 0;
205 static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
207 * FUNCTION: Adds the current thread to the shared owners of the resource
209 * Resource = Pointer to the resource for which the thread is to be
211 * NOTE: Must be called with the resource spinlock held
214 ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
215 POWNER_ENTRY freeEntry;
218 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource);
220 if (Resource->ActiveCount == 0)
222 /* no owner, it's easy */
223 Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread();
224 Resource->OwnerThreads[1].a.OwnerCount = 1;
225 if (Resource->OwnerTable != NULL)
227 ExFreePool(Resource->OwnerTable);
229 Resource->OwnerTable = NULL;
230 Resource->ActiveCount = 1;
231 DPRINT("EiAddSharedOwner() = TRUE\n");
236 * now, we must search if this thread has already acquired this resource
237 * then increase ownercount if found, else create new entry or reuse free
240 if (Resource->OwnerTable == NULL)
242 DPRINT("Creating owner table\n");
244 /* allocate ownertable,memset to 0, initialize first entry */
245 Resource->OwnerTable =
246 ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3,
248 if (Resource->OwnerTable == NULL)
253 memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
254 memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
255 sizeof(OWNER_ENTRY));
257 Resource->OwnerThreads[1].OwnerThread = 0;
258 Resource->OwnerThreads[1].a.TableSize = 3;
260 Resource->OwnerTable[1].OwnerThread = CurrentThread;
261 Resource->OwnerTable[1].a.OwnerCount = 1;
262 Resource->ActiveCount++;
267 DPRINT("Search free entries\n");
269 DPRINT("Number of entries %d\n",
270 Resource->OwnerThreads[1].a.TableSize);
273 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
275 if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
277 DPRINT("Thread already owns resource\n");
278 Resource->OwnerTable[i].a.OwnerCount++;
281 if (Resource->OwnerTable[i].OwnerThread == 0)
283 freeEntry = &Resource->OwnerTable[i];
288 DPRINT("Found free entry %x\n", freeEntry);
292 DPRINT("Allocating new entry\n");
294 /* reallocate ownertable with one more entry */
296 ExAllocatePoolWithTag(NonPagedPool,
298 (Resource->OwnerThreads[1].a.TableSize+1),
300 if (freeEntry == NULL)
305 memcpy(freeEntry,Resource->OwnerTable,
306 sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].a.TableSize));
307 ExFreePool(Resource->OwnerTable);
308 Resource->OwnerTable=freeEntry;
309 freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].a.TableSize];
310 Resource->OwnerThreads[1].a.TableSize++;
312 DPRINT("Creating entry\n");
313 freeEntry->OwnerThread=ExGetCurrentResourceThread();
314 freeEntry->a.OwnerCount=1;
315 Resource->ActiveCount++;
321 ExAcquireResourceSharedLite (
326 * FUNCTION: Acquires the given resource for shared access by the calling
329 * Resource = Points to the resource to acquire
330 * Wait = Is set to TRUE if the caller should be put into wait state
331 * until the resource can be acquired if it cannot be acquired
333 * RETURNS: TRUE, if the resource is acquire
339 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
342 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
344 /* first, resolve trivial cases */
345 if (Resource->ActiveCount == 0)
347 EiAddSharedOwner(Resource);
348 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
349 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
353 if ((Resource->Flag & ResourceOwnedExclusive)
354 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
356 /* exclusive, but by same thread : it's ok */
358 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
360 Resource->OwnerThreads[0].a.OwnerCount++;
361 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
362 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
366 if ((Resource->Flag & ResourceOwnedExclusive)
367 || Resource->NumberOfExclusiveWaiters)
369 /* exclusive by another thread , or thread waiting for exclusive */
372 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
373 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
378 Resource->NumberOfSharedWaiters++;
381 /* wait for the semaphore */
382 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
383 KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL);
384 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
385 /* the spin lock was released we must check again */
387 while ((Resource->Flag & ResourceOwnedExclusive)
388 || Resource->NumberOfExclusiveWaiters);
389 Resource->NumberOfSharedWaiters--;
393 EiAddSharedOwner(Resource);
394 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
395 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
401 ExConvertExclusiveToSharedLite (
405 * FUNCTION: Converts a given resource from acquired for exclusive access
406 * to acquire for shared access
408 * Resource = Points to the resource for which the access should be
410 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
416 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource);
418 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
420 oldWaiters = Resource->NumberOfSharedWaiters;
422 if (!(Resource->Flag & ResourceOwnedExclusive))
424 /* Might not be what the caller expects, better bug check */
426 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
430 //transfer infos from entry 0 to entry 1 and erase entry 0
431 Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread;
432 Resource->OwnerThreads[1].a.OwnerCount=Resource->OwnerThreads[0].a.OwnerCount;
433 Resource->OwnerThreads[0].OwnerThread=0;
434 Resource->OwnerThreads[0].a.OwnerCount=0;
435 /* erase exclusive flag */
436 Resource->Flag &= (~ResourceOwnedExclusive);
437 /* if no shared waiters, that's all */
440 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
443 /* else, awake the waiters */
444 KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0);
445 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
446 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
451 ExDisableResourceBoostLite (
455 Resource->Flag |= ResourceDisableBoost;
460 ExGetExclusiveWaiterCount (
464 return(Resource->NumberOfExclusiveWaiters);
469 ExAcquireSharedStarveExclusive (
474 * FUNCTION: Acquires a given resource for shared access without waiting
475 * for any pending attempts to acquire exclusive access to the
478 * Resource = Points to the resource to be acquired for shared access
479 * Wait = Is set to TRUE if the caller will wait until the resource
480 * becomes available when access can't be granted immediately
481 * RETURNS: TRUE if the requested access is granted. The routine returns
482 * FALSE if the input Wait is FALSE and shared access can't be
483 * granted immediately
488 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
491 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
493 /* no owner, it's easy */
494 if (Resource->ActiveCount == 0)
496 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
497 Resource->OwnerThreads[1].a.OwnerCount=1;
498 Resource->ActiveCount=1;
499 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
500 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
504 if ((Resource->Flag & ResourceOwnedExclusive)
505 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
507 /* exclusive, but by same thread : it's ok */
508 Resource->OwnerThreads[0].a.OwnerCount++;
509 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
510 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
514 if (Resource->Flag & ResourceOwnedExclusive)
516 /* exclusive by another thread */
519 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
520 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
525 Resource->NumberOfSharedWaiters++;
526 /* wait for the semaphore */
527 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
528 KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0);
529 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
530 Resource->NumberOfSharedWaiters--;
533 EiAddSharedOwner(Resource);
534 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
535 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
541 ExAcquireSharedWaitForExclusive (
546 return(ExAcquireResourceSharedLite(Resource,Wait));
555 return(ExDeleteResourceLite(Resource));
560 ExDeleteResourceLite (
564 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource);
565 if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
566 if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
567 if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
568 return(STATUS_SUCCESS);;
573 ExGetSharedWaiterCount (
577 return(Resource->NumberOfSharedWaiters);
582 ExInitializeResource (
586 return(ExInitializeResourceLite(Resource));
589 #endif /* LIBCAPTIVE */
592 ExInitializeResourceLite (PERESOURCE Resource)
594 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource);
595 memset(Resource,0,sizeof(ERESOURCE));
596 Resource->NumberOfSharedWaiters = 0;
597 Resource->NumberOfExclusiveWaiters = 0;
598 KeInitializeSpinLock(&Resource->SpinLock);
600 Resource->ExclusiveWaiters =
601 ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK);
602 KeInitializeEvent(Resource->ExclusiveWaiters,
603 SynchronizationEvent,
605 Resource->SharedWaiters =
606 ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM);
607 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
608 Resource->ActiveCount = 0;
616 ExIsResourceAcquiredExclusiveLite (
620 * FUNCTION: Returns whether the current thread has exclusive access to
623 * Resource = Points to the resource to be queried
624 * RETURNS: TRUE if the caller has exclusive access to the resource,
628 return((Resource->Flag & ResourceOwnedExclusive)
629 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
634 ExIsResourceAcquiredSharedLite (
638 * FUNCTION: Returns whether the current thread has shared access to a given
641 * Resource = Points to the resource to be queried
642 * RETURNS: The number of times the caller has acquired shared access to the
647 if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
649 return(Resource->OwnerThreads[0].a.OwnerCount);
651 if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread())
653 return(Resource->OwnerThreads[1].a.OwnerCount);
655 if (!Resource->OwnerThreads[1].a.TableSize)
659 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
661 if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
663 return Resource->OwnerTable[i].a.OwnerCount;
671 ExReinitializeResourceLite (
675 Resource->NumberOfSharedWaiters = 0;
676 Resource->NumberOfExclusiveWaiters = 0;
677 KeInitializeSpinLock(&Resource->SpinLock);
679 KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
681 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
682 Resource->ActiveCount = 0;
683 if (Resource->OwnerTable)
685 ExFreePool(Resource->OwnerTable);
687 Resource->OwnerThreads[0].OwnerThread=0;
688 Resource->OwnerThreads[0].a.OwnerCount=0;
689 Resource->OwnerThreads[1].OwnerThread=0;
690 Resource->OwnerThreads[1].a.OwnerCount=0;
695 ExReleaseResourceLite (
699 return(ExReleaseResourceForThreadLite(Resource,
700 ExGetCurrentResourceThread()));
705 ExReleaseResourceForThread (
707 ERESOURCE_THREAD ResourceThreadId
710 return(ExReleaseResourceForThreadLite(Resource,ResourceThreadId));
715 ExReleaseResourceForThreadLite (
717 ERESOURCE_THREAD ResourceThreadId
720 * FUNCTION: Releases a resource for the given thread
722 * Resource = Points to the release to release
723 * ResourceThreadId = Identifies the thread that originally acquired
725 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
726 * BUG: We don't support starving exclusive waiters
731 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
732 Resource, ResourceThreadId);
734 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
736 if (Resource->Flag & ResourceOwnedExclusive)
738 DPRINT("Releasing from exclusive access\n");
740 Resource->OwnerThreads[0].a.OwnerCount--;
741 if (Resource->OwnerThreads[0].a.OwnerCount > 0)
743 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
744 DPRINT("ExReleaseResourceForThreadLite() finished\n");
748 Resource->OwnerThreads[0].OwnerThread = 0;
749 Resource->ActiveCount--;
750 Resource->Flag &=(~ResourceOwnedExclusive);
751 assert(Resource->ActiveCount == 0);
752 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
753 Resource->NumberOfExclusiveWaiters);
754 if (Resource->NumberOfExclusiveWaiters)
756 /* get resource to first exclusive waiter */
757 KeSetEvent(Resource->ExclusiveWaiters,
760 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
761 DPRINT("ExReleaseResourceForThreadLite() finished\n");
764 DPRINT("Resource->NumberOfSharedWaiters %d\n",
765 Resource->NumberOfSharedWaiters);
766 if (Resource->NumberOfSharedWaiters)
768 DPRINT("Releasing semaphore\n");
769 KeReleaseSemaphore(Resource->SharedWaiters,
771 Resource->NumberOfSharedWaiters,
774 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
775 DPRINT("ExReleaseResourceForThreadLite() finished\n");
779 EiRemoveSharedOwner(Resource, ResourceThreadId);
781 if (Resource->ActiveCount == 0)
783 if (Resource->NumberOfExclusiveWaiters)
785 /* get resource to first exclusive waiter */
786 KeSetEvent(Resource->ExclusiveWaiters,
792 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
793 DPRINT("ExReleaseResourceForThreadLite() finished\n");
799 ExSetResourceOwnerPointer (
800 IN PERESOURCE Resource,
801 IN PVOID OwnerPointer
807 #endif /* LIBCAPTIVE */