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));
86 ExAcquireResourceExclusiveLite (
91 * FUNCTION: Acquires a resource exclusively for the calling thread
93 * Resource = Points to the resource to acquire
94 * Wait = Is set to TRUE if the caller should wait to acquire the
95 * resource if it can't be acquired immediately
96 * RETURNS: TRUE if the resource was acquired,
98 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
103 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
106 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
108 /* resource already locked */
109 if((Resource->Flag & ResourceOwnedExclusive)
110 && Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
112 /* it's ok : same lock for same thread */
113 Resource->OwnerThreads[0].a.OwnerCount++;
114 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
115 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
119 if (Resource->ActiveCount && !Wait)
121 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
122 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
127 * This is slightly better than it looks because other exclusive
128 * threads who are waiting won't be woken up but there is a race
129 * with new threads trying to grab the resource so we must have
130 * the spinlock, still normally this loop will only be executed
132 * NOTE: We might want to set a timeout to detect deadlock
135 while (Resource->ActiveCount)
137 Resource->NumberOfExclusiveWaiters++;
138 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
139 KeWaitForSingleObject(Resource->ExclusiveWaiters,
144 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
145 Resource->NumberOfExclusiveWaiters--;
147 Resource->Flag |= ResourceOwnedExclusive;
148 Resource->ActiveCount = 1;
149 Resource->OwnerThreads[0].OwnerThread = ExGetCurrentResourceThread();
150 Resource->OwnerThreads[0].a.OwnerCount = 1;
151 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
152 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
156 static BOOLEAN EiRemoveSharedOwner(PERESOURCE Resource,
157 ERESOURCE_THREAD ResourceThreadId)
159 * FUNCTION: Removes the current thread from the shared owners of the resource
161 * Resource = Pointer to the resource for which the thread is to be
163 * NOTE: Must be called with the resource spinlock held
168 if (Resource->OwnerThreads[1].OwnerThread == ResourceThreadId)
170 Resource->OwnerThreads[1].a.OwnerCount--;
171 if (Resource->OwnerThreads[1].a.OwnerCount == 0)
173 Resource->ActiveCount--;
174 Resource->OwnerThreads[1].OwnerThread = 0;
179 if (Resource->OwnerThreads[1].OwnerThread)
181 /* Oh dear, the caller didn't own the resource after all */
185 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
187 if (Resource->OwnerTable[i].OwnerThread == ResourceThreadId)
189 Resource->OwnerTable[i].a.OwnerCount--;
190 if (Resource->OwnerTable[i].a.OwnerCount == 0)
192 Resource->ActiveCount--;
193 Resource->OwnerTable[i].OwnerThread = 0;
201 static BOOLEAN EiAddSharedOwner(PERESOURCE Resource)
203 * FUNCTION: Adds the current thread to the shared owners of the resource
205 * Resource = Pointer to the resource for which the thread is to be
207 * NOTE: Must be called with the resource spinlock held
210 ERESOURCE_THREAD CurrentThread = ExGetCurrentResourceThread();
211 POWNER_ENTRY freeEntry;
214 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource);
216 if (Resource->ActiveCount == 0)
218 /* no owner, it's easy */
219 Resource->OwnerThreads[1].OwnerThread = ExGetCurrentResourceThread();
220 Resource->OwnerThreads[1].a.OwnerCount = 1;
221 if (Resource->OwnerTable != NULL)
223 ExFreePool(Resource->OwnerTable);
225 Resource->OwnerTable = NULL;
226 Resource->ActiveCount = 1;
227 DPRINT("EiAddSharedOwner() = TRUE\n");
232 * now, we must search if this thread has already acquired this resource
233 * then increase ownercount if found, else create new entry or reuse free
236 if (Resource->OwnerTable == NULL)
238 DPRINT("Creating owner table\n");
240 /* allocate ownertable,memset to 0, initialize first entry */
241 Resource->OwnerTable =
242 ExAllocatePoolWithTag(NonPagedPool, sizeof(OWNER_ENTRY)*3,
244 if (Resource->OwnerTable == NULL)
249 memset(Resource->OwnerTable,0,sizeof(OWNER_ENTRY)*3);
250 memcpy(&Resource->OwnerTable[0], &Resource->OwnerThreads[1],
251 sizeof(OWNER_ENTRY));
253 Resource->OwnerThreads[1].OwnerThread = 0;
254 Resource->OwnerThreads[1].a.TableSize = 3;
256 Resource->OwnerTable[1].OwnerThread = CurrentThread;
257 Resource->OwnerTable[1].a.OwnerCount = 1;
258 Resource->ActiveCount++;
263 DPRINT("Search free entries\n");
265 DPRINT("Number of entries %d\n",
266 Resource->OwnerThreads[1].a.TableSize);
269 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
271 if (Resource->OwnerTable[i].OwnerThread == CurrentThread)
273 DPRINT("Thread already owns resource\n");
274 Resource->OwnerTable[i].a.OwnerCount++;
277 if (Resource->OwnerTable[i].OwnerThread == 0)
279 freeEntry = &Resource->OwnerTable[i];
284 DPRINT("Found free entry %x\n", freeEntry);
288 DPRINT("Allocating new entry\n");
290 /* reallocate ownertable with one more entry */
292 ExAllocatePoolWithTag(NonPagedPool,
294 (Resource->OwnerThreads[1].a.TableSize+1),
296 if (freeEntry == NULL)
301 memcpy(freeEntry,Resource->OwnerTable,
302 sizeof(OWNER_ENTRY)*(Resource->OwnerThreads[1].a.TableSize));
303 ExFreePool(Resource->OwnerTable);
304 Resource->OwnerTable=freeEntry;
305 freeEntry=&Resource->OwnerTable[Resource->OwnerThreads[1].a.TableSize];
306 Resource->OwnerThreads[1].a.TableSize++;
308 DPRINT("Creating entry\n");
309 freeEntry->OwnerThread=ExGetCurrentResourceThread();
310 freeEntry->a.OwnerCount=1;
311 Resource->ActiveCount++;
317 ExAcquireResourceSharedLite (
322 * FUNCTION: Acquires the given resource for shared access by the calling
325 * Resource = Points to the resource to acquire
326 * Wait = Is set to TRUE if the caller should be put into wait state
327 * until the resource can be acquired if it cannot be acquired
329 * RETURNS: TRUE, if the resource is acquire
335 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
338 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
340 /* first, resolve trivial cases */
341 if (Resource->ActiveCount == 0)
343 EiAddSharedOwner(Resource);
344 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
345 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
349 if ((Resource->Flag & ResourceOwnedExclusive)
350 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
352 /* exclusive, but by same thread : it's ok */
354 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
356 Resource->OwnerThreads[0].a.OwnerCount++;
357 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
358 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
362 if ((Resource->Flag & ResourceOwnedExclusive)
363 || Resource->NumberOfExclusiveWaiters)
365 /* exclusive by another thread , or thread waiting for exclusive */
368 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
369 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
374 Resource->NumberOfSharedWaiters++;
377 /* wait for the semaphore */
378 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
379 KeWaitForSingleObject(Resource->SharedWaiters,0, KernelMode, FALSE, NULL);
380 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
381 /* the spin lock was released we must check again */
383 while ((Resource->Flag & ResourceOwnedExclusive)
384 || Resource->NumberOfExclusiveWaiters);
385 Resource->NumberOfSharedWaiters--;
389 EiAddSharedOwner(Resource);
390 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
391 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
397 ExConvertExclusiveToSharedLite (
401 * FUNCTION: Converts a given resource from acquired for exclusive access
402 * to acquire for shared access
404 * Resource = Points to the resource for which the access should be
406 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
412 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource);
414 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
416 oldWaiters = Resource->NumberOfSharedWaiters;
418 if (!(Resource->Flag & ResourceOwnedExclusive))
420 /* Might not be what the caller expects, better bug check */
422 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
426 //transfer infos from entry 0 to entry 1 and erase entry 0
427 Resource->OwnerThreads[1].OwnerThread=Resource->OwnerThreads[0].OwnerThread;
428 Resource->OwnerThreads[1].a.OwnerCount=Resource->OwnerThreads[0].a.OwnerCount;
429 Resource->OwnerThreads[0].OwnerThread=0;
430 Resource->OwnerThreads[0].a.OwnerCount=0;
431 /* erase exclusive flag */
432 Resource->Flag &= (~ResourceOwnedExclusive);
433 /* if no shared waiters, that's all */
436 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
439 /* else, awake the waiters */
440 KeReleaseSemaphore(Resource->SharedWaiters,0,oldWaiters,0);
441 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
442 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
447 ExDisableResourceBoostLite (
451 Resource->Flag |= ResourceDisableBoost;
456 ExGetExclusiveWaiterCount (
460 return(Resource->NumberOfExclusiveWaiters);
465 ExAcquireSharedStarveExclusive (
470 * FUNCTION: Acquires a given resource for shared access without waiting
471 * for any pending attempts to acquire exclusive access to the
474 * Resource = Points to the resource to be acquired for shared access
475 * Wait = Is set to TRUE if the caller will wait until the resource
476 * becomes available when access can't be granted immediately
477 * RETURNS: TRUE if the requested access is granted. The routine returns
478 * FALSE if the input Wait is FALSE and shared access can't be
479 * granted immediately
484 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
487 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
489 /* no owner, it's easy */
490 if (Resource->ActiveCount == 0)
492 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
493 Resource->OwnerThreads[1].a.OwnerCount=1;
494 Resource->ActiveCount=1;
495 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
496 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
500 if ((Resource->Flag & ResourceOwnedExclusive)
501 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread())
503 /* exclusive, but by same thread : it's ok */
504 Resource->OwnerThreads[0].a.OwnerCount++;
505 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
506 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
510 if (Resource->Flag & ResourceOwnedExclusive)
512 /* exclusive by another thread */
515 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
516 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
521 Resource->NumberOfSharedWaiters++;
522 /* wait for the semaphore */
523 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
524 KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0);
525 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
526 Resource->NumberOfSharedWaiters--;
529 EiAddSharedOwner(Resource);
530 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
531 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
537 ExAcquireSharedWaitForExclusive (
542 return(ExAcquireResourceSharedLite(Resource,Wait));
551 return(ExDeleteResourceLite(Resource));
556 ExDeleteResourceLite (
560 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource);
561 if (Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
562 if (Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
563 if (Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
564 return(STATUS_SUCCESS);;
569 ExGetSharedWaiterCount (
573 return(Resource->NumberOfSharedWaiters);
578 ExInitializeResource (
582 return(ExInitializeResourceLite(Resource));
585 #endif /* LIBCAPTIVE */
588 ExInitializeResourceLite (PERESOURCE Resource)
590 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource);
591 memset(Resource,0,sizeof(ERESOURCE));
592 Resource->NumberOfSharedWaiters = 0;
593 Resource->NumberOfExclusiveWaiters = 0;
594 KeInitializeSpinLock(&Resource->SpinLock);
596 Resource->ExclusiveWaiters =
597 ExAllocatePoolWithTag(NonPagedPool, sizeof(KEVENT), TAG_EXCLUSIVE_LOCK);
598 KeInitializeEvent(Resource->ExclusiveWaiters,
599 SynchronizationEvent,
601 Resource->SharedWaiters =
602 ExAllocatePoolWithTag(NonPagedPool ,sizeof(KSEMAPHORE), TAG_SHARED_SEM);
603 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
604 Resource->ActiveCount = 0;
612 ExIsResourceAcquiredExclusiveLite (
616 * FUNCTION: Returns whether the current thread has exclusive access to
619 * Resource = Points to the resource to be queried
620 * RETURNS: TRUE if the caller has exclusive access to the resource,
624 return((Resource->Flag & ResourceOwnedExclusive)
625 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
630 ExIsResourceAcquiredSharedLite (
634 * FUNCTION: Returns whether the current thread has shared access to a given
637 * Resource = Points to the resource to be queried
638 * RETURNS: The number of times the caller has acquired shared access to the
643 if (Resource->OwnerThreads[0].OwnerThread == ExGetCurrentResourceThread())
645 return(Resource->OwnerThreads[0].a.OwnerCount);
647 if (Resource->OwnerThreads[1].OwnerThread == ExGetCurrentResourceThread())
649 return(Resource->OwnerThreads[1].a.OwnerCount);
651 if (!Resource->OwnerThreads[1].a.TableSize)
655 for (i=0; i<Resource->OwnerThreads[1].a.TableSize; i++)
657 if (Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
659 return Resource->OwnerTable[i].a.OwnerCount;
667 ExReinitializeResourceLite (
671 Resource->NumberOfSharedWaiters = 0;
672 Resource->NumberOfExclusiveWaiters = 0;
673 KeInitializeSpinLock(&Resource->SpinLock);
675 KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
677 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
678 Resource->ActiveCount = 0;
679 if (Resource->OwnerTable)
681 ExFreePool(Resource->OwnerTable);
683 Resource->OwnerThreads[0].OwnerThread=0;
684 Resource->OwnerThreads[0].a.OwnerCount=0;
685 Resource->OwnerThreads[1].OwnerThread=0;
686 Resource->OwnerThreads[1].a.OwnerCount=0;
691 ExReleaseResourceLite (
695 return(ExReleaseResourceForThreadLite(Resource,
696 ExGetCurrentResourceThread()));
701 ExReleaseResourceForThread (
703 ERESOURCE_THREAD ResourceThreadId
706 return(ExReleaseResourceForThreadLite(Resource,ResourceThreadId));
711 ExReleaseResourceForThreadLite (
713 ERESOURCE_THREAD ResourceThreadId
716 * FUNCTION: Releases a resource for the given thread
718 * Resource = Points to the release to release
719 * ResourceThreadId = Identifies the thread that originally acquired
721 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
722 * BUG: We don't support starving exclusive waiters
727 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
728 Resource, ResourceThreadId);
730 KeAcquireSpinLock(&Resource->SpinLock, &oldIrql);
732 if (Resource->Flag & ResourceOwnedExclusive)
734 DPRINT("Releasing from exclusive access\n");
736 Resource->OwnerThreads[0].a.OwnerCount--;
737 if (Resource->OwnerThreads[0].a.OwnerCount > 0)
739 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
740 DPRINT("ExReleaseResourceForThreadLite() finished\n");
744 Resource->OwnerThreads[0].OwnerThread = 0;
745 Resource->ActiveCount--;
746 Resource->Flag &=(~ResourceOwnedExclusive);
747 assert(Resource->ActiveCount == 0);
748 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
749 Resource->NumberOfExclusiveWaiters);
750 if (Resource->NumberOfExclusiveWaiters)
752 /* get resource to first exclusive waiter */
753 KeSetEvent(Resource->ExclusiveWaiters,
756 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
757 DPRINT("ExReleaseResourceForThreadLite() finished\n");
760 DPRINT("Resource->NumberOfSharedWaiters %d\n",
761 Resource->NumberOfSharedWaiters);
762 if (Resource->NumberOfSharedWaiters)
764 DPRINT("Releasing semaphore\n");
765 KeReleaseSemaphore(Resource->SharedWaiters,
767 Resource->NumberOfSharedWaiters,
770 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
771 DPRINT("ExReleaseResourceForThreadLite() finished\n");
775 EiRemoveSharedOwner(Resource, ResourceThreadId);
777 if (Resource->ActiveCount == 0)
779 if (Resource->NumberOfExclusiveWaiters)
781 /* get resource to first exclusive waiter */
782 KeSetEvent(Resource->ExclusiveWaiters,
788 KeReleaseSpinLock(&Resource->SpinLock, oldIrql);
789 DPRINT("ExReleaseResourceForThreadLite() finished\n");
795 ExSetResourceOwnerPointer (
796 IN PERESOURCE Resource,
797 IN PVOID OwnerPointer
803 #endif /* LIBCAPTIVE */