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");
158 static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
159 ERESOURCE_THREAD ResourceThreadId)
161 * FUNCTION: Removes the current thread from the shared owners of the resource
163 * Resource = Pointer to the resource for which the thread is to be
165 * NOTE: Must be called with the resource spinlock held
170 if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
172 Resource->OwnerThreads[1].a.OwnerCount--;
173 if (Resource->OwnerThreads[1].a.OwnerCount == 0)
175 Resource->ActiveCount--;
176 Resource->OwnerThreads[1].OwnerThread = 0;
181 if (Resource->OwnerThreads[1].OwnerThread)
183 /* Oh dear, the caller didn't own the resource after all */
187 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
189 if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
191 Resource->OwnerTable[i].a.OwnerCount--;
192 if (Resource->OwnerTable[i].a.OwnerCount == 0)
194 Resource->ActiveCount--;
195 Resource->OwnerTable[i].OwnerThread = 0;
204 static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
206 * FUNCTION: Adds the current thread to the shared owners of the resource
208 * Resource = Pointer to the resource for which the thread is to be
210 * NOTE: Must be called with the resource spinlock held
213 ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
214 POWNER_ENTRY freeEntry;
217 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource);
219 if (Resource->ActiveCount == 0)
221 /* no owner, it's easy */
222 Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread();
223 Resource->OwnerThreads[1].a.OwnerCount = 1;
224 if (Resource->OwnerTable != NULL)
226 ExFreePool(Resource->OwnerTable);
228 Resource->OwnerTable = NULL;
229 Resource->ActiveCount = 1;
230 DPRINT("EiAddSharedOwner() = TRUE\n");
235 * now, we must search if this thread has already acquired this resource
236 * then increase ownercount if found, else create new entry or reuse free
239 if (Resource->OwnerTable == NULL)
241 DPRINT("Creating owner table\n");
243 /* allocate ownertable,memset to 0, initialize first entry */
244 Resource->OwnerTable =
245 ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3,
247 if (Resource->OwnerTable == NULL)
252 memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
253 memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
254 sizeof(OWNER_ENTRY));
256 Resource->OwnerThreads[1].OwnerThread = 0;
257 Resource->OwnerThreads[1].a.TableSize = 3;
259 Resource->OwnerTable[1].OwnerThread = CurrentThread;
260 Resource->OwnerTable[1].a.OwnerCount = 1;
261 Resource->ActiveCount++;
266 DPRINT("Search free entries\n");
268 DPRINT("Number of entries %d\n",
269 Resource->OwnerThreads[1].a.TableSize);
272 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
274 if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
276 DPRINT("Thread already owns resource\n");
277 Resource->OwnerTable[i].a.OwnerCount++;
280 if (Resource->OwnerTable[i].OwnerThread == 0)
282 freeEntry = &Resource->OwnerTable[i];
287 DPRINT("Found free entry %x\n", freeEntry);
291 DPRINT("Allocating new entry\n");
293 /* reallocate ownertable with one more entry */
295 ExAllocatePoolWithTag(NonPagedPool,
297 (Resource->OwnerThreads[1].a.TableSize+1),
299 if (freeEntry == NULL)
304 memcpy(freeEntry,Resource->OwnerTable,
305 sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].a.TableSize));
306 ExFreePool(Resource->OwnerTable);
307 Resource->OwnerTable=freeEntry;
308 freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].a.TableSize];
309 Resource->OwnerThreads[1].a.TableSize++;
311 DPRINT("Creating entry\n");
312 freeEntry->OwnerThread=ExGetCurrentResourceThread();
313 freeEntry->a.OwnerCount=1;
314 Resource->ActiveCount++;
320 ExAcquireResourceSharedLite (
325 * FUNCTION: Acquires the given resource for shared access by the calling
328 * Resource = Points to the resource to acquire
329 * Wait = Is set to TRUE if the caller should be put into wait state
330 * until the resource can be acquired if it cannot be acquired
332 * RETURNS: TRUE, if the resource is acquire
338 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
341 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
343 /* first, resolve trivial cases */
344 if (Resource->ActiveCount == 0)
346 EiAddSharedOwner(Resource);
347 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
348 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
352 if ((Resource->Flag & ResourceOwnedExclusive)
353 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
355 /* exclusive, but by same thread : it's ok */
357 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
359 Resource->OwnerThreads[0].a.OwnerCount++;
360 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
361 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
365 if ((Resource->Flag & ResourceOwnedExclusive)
366 || Resource->NumberOfExclusiveWaiters)
368 /* exclusive by another thread , or thread waiting for exclusive */
371 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
372 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
377 Resource->NumberOfSharedWaiters++;
380 /* wait for the semaphore */
381 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
382 KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL);
383 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
384 /* the spin lock was released we must check again */
386 while ((Resource->Flag & ResourceOwnedExclusive)
387 || Resource->NumberOfExclusiveWaiters);
388 Resource->NumberOfSharedWaiters--;
392 EiAddSharedOwner(Resource);
393 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
394 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
402 ExConvertExclusiveToSharedLite (
406 * FUNCTION: Converts a given resource from acquired for exclusive access
407 * to acquire for shared access
409 * Resource = Points to the resource for which the access should be
411 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
417 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource);
419 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
421 oldWaiters = Resource->NumberOfSharedWaiters;
423 if (!(Resource->Flag & ResourceOwnedExclusive))
425 /* Might not be what the caller expects, better bug check */
427 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
431 //transfer infos from entry 0 to entry 1 and erase entry 0
432 Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread;
433 Resource->OwnerThreads[1].a.OwnerCount=Resource->OwnerThreads[0].a.OwnerCount;
434 Resource->OwnerThreads[0].OwnerThread=0;
435 Resource->OwnerThreads[0].a.OwnerCount=0;
436 /* erase exclusive flag */
437 Resource->Flag &= (~ResourceOwnedExclusive);
438 /* if no shared waiters, that's all */
441 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
444 /* else, awake the waiters */
445 KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0);
446 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
447 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
452 ExDisableResourceBoostLite (
456 Resource->Flag |= ResourceDisableBoost;
461 ExGetExclusiveWaiterCount (
465 return(Resource->NumberOfExclusiveWaiters);
468 #endif /* LIBCAPTIVE */
472 ExAcquireSharedStarveExclusive (
477 * FUNCTION: Acquires a given resource for shared access without waiting
478 * for any pending attempts to acquire exclusive access to the
481 * Resource = Points to the resource to be acquired for shared access
482 * Wait = Is set to TRUE if the caller will wait until the resource
483 * becomes available when access can't be granted immediately
484 * RETURNS: TRUE if the requested access is granted. The routine returns
485 * FALSE if the input Wait is FALSE and shared access can't be
486 * granted immediately
491 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
494 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
496 /* no owner, it's easy */
497 if (Resource->ActiveCount == 0)
499 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
500 Resource->OwnerThreads[1].a.OwnerCount=1;
501 Resource->ActiveCount=1;
502 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
503 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
507 if ((Resource->Flag & ResourceOwnedExclusive)
508 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
510 /* exclusive, but by same thread : it's ok */
511 Resource->OwnerThreads[0].a.OwnerCount++;
512 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
513 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
517 if (Resource->Flag & ResourceOwnedExclusive)
519 /* exclusive by another thread */
522 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
523 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
528 Resource->NumberOfSharedWaiters++;
529 /* wait for the semaphore */
530 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
531 KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0);
532 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
533 Resource->NumberOfSharedWaiters--;
536 EiAddSharedOwner(Resource);
537 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
538 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
546 ExAcquireSharedWaitForExclusive (
551 return(ExAcquireResourceSharedLite(Resource,Wait));
560 return(ExDeleteResourceLite(Resource));
563 #endif /* LIBCAPTIVE */
567 ExDeleteResourceLite (
571 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource);
572 if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
573 if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
574 if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
575 return(STATUS_SUCCESS);;
582 ExGetSharedWaiterCount (
586 return(Resource->NumberOfSharedWaiters);
591 ExInitializeResource (
595 return(ExInitializeResourceLite(Resource));
598 #endif /* LIBCAPTIVE */
601 ExInitializeResourceLite (PERESOURCE Resource)
603 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource);
604 memset(Resource,0,sizeof(ERESOURCE));
605 Resource->NumberOfSharedWaiters = 0;
606 Resource->NumberOfExclusiveWaiters = 0;
607 KeInitializeSpinLock(&Resource->SpinLock);
609 Resource->ExclusiveWaiters =
610 ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK);
611 KeInitializeEvent(Resource->ExclusiveWaiters,
612 SynchronizationEvent,
614 Resource->SharedWaiters =
615 ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM);
616 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
617 Resource->ActiveCount = 0;
623 ExIsResourceAcquiredExclusiveLite (
627 * FUNCTION: Returns whether the current thread has exclusive access to
630 * Resource = Points to the resource to be queried
631 * RETURNS: TRUE if the caller has exclusive access to the resource,
635 return((Resource->Flag & ResourceOwnedExclusive)
636 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
643 ExIsResourceAcquiredSharedLite (
647 * FUNCTION: Returns whether the current thread has shared access to a given
650 * Resource = Points to the resource to be queried
651 * RETURNS: The number of times the caller has acquired shared access to the
656 if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
658 return(Resource->OwnerThreads[0].a.OwnerCount);
660 if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread())
662 return(Resource->OwnerThreads[1].a.OwnerCount);
664 if (!Resource->OwnerThreads[1].a.TableSize)
668 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
670 if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
672 return Resource->OwnerTable[i].a.OwnerCount;
680 ExReinitializeResourceLite (
684 Resource->NumberOfSharedWaiters = 0;
685 Resource->NumberOfExclusiveWaiters = 0;
686 KeInitializeSpinLock(&Resource->SpinLock);
688 KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
690 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
691 Resource->ActiveCount = 0;
692 if (Resource->OwnerTable)
694 ExFreePool(Resource->OwnerTable);
696 Resource->OwnerThreads[0].OwnerThread=0;
697 Resource->OwnerThreads[0].a.OwnerCount=0;
698 Resource->OwnerThreads[1].OwnerThread=0;
699 Resource->OwnerThreads[1].a.OwnerCount=0;
702 #endif /* LIBCAPTIVE */
706 ExReleaseResourceLite (
710 return(ExReleaseResourceForThreadLite(Resource,
711 ExGetCurrentResourceThread()));
718 ExReleaseResourceForThread (
720 ERESOURCE_THREAD ResourceThreadId
723 return(ExReleaseResourceForThreadLite(Resource,ResourceThreadId));
726 #endif /* LIBCAPTIVE */
730 ExReleaseResourceForThreadLite (
732 ERESOURCE_THREAD ResourceThreadId
735 * FUNCTION: Releases a resource for the given thread
737 * Resource = Points to the release to release
738 * ResourceThreadId = Identifies the thread that originally acquired
740 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
741 * BUG: We don't support starving exclusive waiters
746 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
747 Resource, ResourceThreadId);
749 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
751 if (Resource->Flag & ResourceOwnedExclusive)
753 DPRINT("Releasing from exclusive access\n");
755 Resource->OwnerThreads[0].a.OwnerCount--;
756 if (Resource->OwnerThreads[0].a.OwnerCount > 0)
758 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
759 DPRINT("ExReleaseResourceForThreadLite() finished\n");
763 Resource->OwnerThreads[0].OwnerThread = 0;
764 Resource->ActiveCount--;
765 Resource->Flag &=(~ResourceOwnedExclusive);
766 assert(Resource->ActiveCount == 0);
767 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
768 Resource->NumberOfExclusiveWaiters);
769 if (Resource->NumberOfExclusiveWaiters)
771 /* get resource to first exclusive waiter */
772 KeSetEvent(Resource->ExclusiveWaiters,
775 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
776 DPRINT("ExReleaseResourceForThreadLite() finished\n");
779 DPRINT("Resource->NumberOfSharedWaiters %d\n",
780 Resource->NumberOfSharedWaiters);
781 if (Resource->NumberOfSharedWaiters)
784 DPRINT("Releasing semaphore\n");
785 KeReleaseSemaphore(Resource->SharedWaiters,
787 Resource->NumberOfSharedWaiters,
789 #else /* !LIBCAPTIVE */
791 #endif /* !LIBCAPTIVE */
793 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
794 DPRINT("ExReleaseResourceForThreadLite() finished\n");
798 EiRemoveSharedOwner(Resource, ResourceThreadId);
800 if (Resource->ActiveCount == 0)
802 if (Resource->NumberOfExclusiveWaiters)
804 /* get resource to first exclusive waiter */
805 KeSetEvent(Resource->ExclusiveWaiters,
811 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
812 DPRINT("ExReleaseResourceForThreadLite() finished\n");
819 ExSetResourceOwnerPointer (
820 IN PERESOURCE Resource,
821 IN PVOID OwnerPointer
827 #endif /* LIBCAPTIVE */