1 /* $Id: resource.c,v 1.23 2003/07/11 01:23:14 royce Exp $
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
));
78 ExAcquireResourceExclusive (
83 return(ExAcquireResourceExclusiveLite(Resource
,Wait
));
91 ExAcquireResourceExclusiveLite (
96 * FUNCTION: Acquires a resource exclusively for the calling thread
98 * Resource = Points to the resource to acquire
99 * Wait = Is set to TRUE if the caller should wait to acquire the
100 * resource if it can't be acquired immediately
101 * RETURNS: TRUE if the resource was acquired,
103 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
108 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
111 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
113 /* resource already locked */
114 if((Resource
->Flag
& ResourceOwnedExclusive
)
115 && Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
117 /* it's ok : same lock for same thread */
118 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
119 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
120 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
124 if (Resource
->ActiveCount
&& !Wait
)
126 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
127 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
132 * This is slightly better than it looks because other exclusive
133 * threads who are waiting won't be woken up but there is a race
134 * with new threads trying to grab the resource so we must have
135 * the spinlock, still normally this loop will only be executed
137 * NOTE: We might want to set a timeout to detect deadlock
140 while (Resource
->ActiveCount
)
142 Resource
->NumberOfExclusiveWaiters
++;
143 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
144 KeWaitForSingleObject(Resource
->ExclusiveWaiters
,
149 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
150 Resource
->NumberOfExclusiveWaiters
--;
152 Resource
->Flag
|= ResourceOwnedExclusive
;
153 Resource
->ActiveCount
= 1;
154 Resource
->OwnerThreads
[0].OwnerThread
= ExGetCurrentResourceThread();
155 Resource
->OwnerThreads
[0].a
.OwnerCount
= 1;
156 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
157 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
161 static BOOLEAN
EiRemoveSharedOwner(PERESOURCE Resource
,
162 ERESOURCE_THREAD ResourceThreadId
)
164 * FUNCTION: Removes the current thread from the shared owners of the resource
166 * Resource = Pointer to the resource for which the thread is to be
168 * NOTE: Must be called with the resource spinlock held
173 if (Resource
->OwnerThreads
[1].OwnerThread
== ResourceThreadId
)
175 Resource
->OwnerThreads
[1].a
.OwnerCount
--;
176 if (Resource
->OwnerThreads
[1].a
.OwnerCount
== 0)
178 Resource
->ActiveCount
--;
179 Resource
->OwnerThreads
[1].OwnerThread
= 0;
184 if (Resource
->OwnerThreads
[1].OwnerThread
)
186 /* Oh dear, the caller didn't own the resource after all */
190 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
192 if (Resource
->OwnerTable
[i
].OwnerThread
== ResourceThreadId
)
194 Resource
->OwnerTable
[i
].a
.OwnerCount
--;
195 if (Resource
->OwnerTable
[i
].a
.OwnerCount
== 0)
197 Resource
->ActiveCount
--;
198 Resource
->OwnerTable
[i
].OwnerThread
= 0;
206 static BOOLEAN
EiAddSharedOwner(PERESOURCE Resource
)
208 * FUNCTION: Adds the current thread to the shared owners of the resource
210 * Resource = Pointer to the resource for which the thread is to be
212 * NOTE: Must be called with the resource spinlock held
215 ERESOURCE_THREAD CurrentThread
= ExGetCurrentResourceThread();
216 POWNER_ENTRY freeEntry
;
219 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource
);
221 if (Resource
->ActiveCount
== 0)
223 /* no owner, it's easy */
224 Resource
->OwnerThreads
[1].OwnerThread
= ExGetCurrentResourceThread();
225 Resource
->OwnerThreads
[1].a
.OwnerCount
= 1;
226 if (Resource
->OwnerTable
!= NULL
)
228 ExFreePool(Resource
->OwnerTable
);
230 Resource
->OwnerTable
= NULL
;
231 Resource
->ActiveCount
= 1;
232 DPRINT("EiAddSharedOwner() = TRUE\n");
237 * now, we must search if this thread has already acquired this resource
238 * then increase ownercount if found, else create new entry or reuse free
241 if (Resource
->OwnerTable
== NULL
)
243 DPRINT("Creating owner table\n");
245 /* allocate ownertable,memset to 0, initialize first entry */
246 Resource
->OwnerTable
=
247 ExAllocatePoolWithTag(NonPagedPool
, sizeof(OWNER_ENTRY
)*3,
249 if (Resource
->OwnerTable
== NULL
)
254 memset(Resource
->OwnerTable
,0,sizeof(OWNER_ENTRY
)*3);
255 memcpy(&Resource
->OwnerTable
[0], &Resource
->OwnerThreads
[1],
256 sizeof(OWNER_ENTRY
));
258 Resource
->OwnerThreads
[1].OwnerThread
= 0;
259 Resource
->OwnerThreads
[1].a
.TableSize
= 3;
261 Resource
->OwnerTable
[1].OwnerThread
= CurrentThread
;
262 Resource
->OwnerTable
[1].a
.OwnerCount
= 1;
263 Resource
->ActiveCount
++;
268 DPRINT("Search free entries\n");
270 DPRINT("Number of entries %d\n",
271 Resource
->OwnerThreads
[1].a
.TableSize
);
274 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
276 if (Resource
->OwnerTable
[i
].OwnerThread
== CurrentThread
)
278 DPRINT("Thread already owns resource\n");
279 Resource
->OwnerTable
[i
].a
.OwnerCount
++;
282 if (Resource
->OwnerTable
[i
].OwnerThread
== 0)
284 freeEntry
= &Resource
->OwnerTable
[i
];
289 DPRINT("Found free entry %x\n", freeEntry
);
293 DPRINT("Allocating new entry\n");
295 /* reallocate ownertable with one more entry */
297 ExAllocatePoolWithTag(NonPagedPool
,
299 (Resource
->OwnerThreads
[1].a
.TableSize
+1),
301 if (freeEntry
== NULL
)
306 memcpy(freeEntry
,Resource
->OwnerTable
,
307 sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
));
308 ExFreePool(Resource
->OwnerTable
);
309 Resource
->OwnerTable
=freeEntry
;
310 freeEntry
=&Resource
->OwnerTable
[Resource
->OwnerThreads
[1].a
.TableSize
];
311 Resource
->OwnerThreads
[1].a
.TableSize
++;
313 DPRINT("Creating entry\n");
314 freeEntry
->OwnerThread
=ExGetCurrentResourceThread();
315 freeEntry
->a
.OwnerCount
=1;
316 Resource
->ActiveCount
++;
325 ExAcquireResourceSharedLite (
330 * FUNCTION: Acquires the given resource for shared access by the calling
333 * Resource = Points to the resource to acquire
334 * Wait = Is set to TRUE if the caller should be put into wait state
335 * until the resource can be acquired if it cannot be acquired
337 * RETURNS: TRUE, if the resource is acquire
343 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
346 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
348 /* first, resolve trivial cases */
349 if (Resource
->ActiveCount
== 0)
351 EiAddSharedOwner(Resource
);
352 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
353 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
357 if ((Resource
->Flag
& ResourceOwnedExclusive
)
358 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
360 /* exclusive, but by same thread : it's ok */
362 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
364 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
365 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
366 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
370 if ((Resource
->Flag
& ResourceOwnedExclusive
)
371 || Resource
->NumberOfExclusiveWaiters
)
373 /* exclusive by another thread , or thread waiting for exclusive */
376 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
377 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
382 Resource
->NumberOfSharedWaiters
++;
385 /* wait for the semaphore */
386 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
387 KeWaitForSingleObject(Resource
->SharedWaiters
,0, KernelMode
, FALSE
, NULL
);
388 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
389 /* the spin lock was released we must check again */
391 while ((Resource
->Flag
& ResourceOwnedExclusive
)
392 || Resource
->NumberOfExclusiveWaiters
);
393 Resource
->NumberOfSharedWaiters
--;
397 EiAddSharedOwner(Resource
);
398 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
399 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
408 ExConvertExclusiveToSharedLite (
412 * FUNCTION: Converts a given resource from acquired for exclusive access
413 * to acquire for shared access
415 * Resource = Points to the resource for which the access should be
417 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
423 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource
);
425 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
427 oldWaiters
= Resource
->NumberOfSharedWaiters
;
429 if (!(Resource
->Flag
& ResourceOwnedExclusive
))
431 /* Might not be what the caller expects, better bug check */
433 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
437 //transfer infos from entry 0 to entry 1 and erase entry 0
438 Resource
->OwnerThreads
[1].OwnerThread
=Resource
->OwnerThreads
[0].OwnerThread
;
439 Resource
->OwnerThreads
[1].a
.OwnerCount
=Resource
->OwnerThreads
[0].a
.OwnerCount
;
440 Resource
->OwnerThreads
[0].OwnerThread
=0;
441 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
442 /* erase exclusive flag */
443 Resource
->Flag
&= (~ResourceOwnedExclusive
);
444 /* if no shared waiters, that's all */
447 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
450 /* else, awake the waiters */
451 KeReleaseSemaphore(Resource
->SharedWaiters
,0,oldWaiters
,0);
452 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
453 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
461 ExDisableResourceBoostLite (
465 Resource
->Flag
|= ResourceDisableBoost
;
473 ExGetExclusiveWaiterCount (
477 return(Resource
->NumberOfExclusiveWaiters
);
485 ExAcquireSharedStarveExclusive (
490 * FUNCTION: Acquires a given resource for shared access without waiting
491 * for any pending attempts to acquire exclusive access to the
494 * Resource = Points to the resource to be acquired for shared access
495 * Wait = Is set to TRUE if the caller will wait until the resource
496 * becomes available when access can't be granted immediately
497 * RETURNS: TRUE if the requested access is granted. The routine returns
498 * FALSE if the input Wait is FALSE and shared access can't be
499 * granted immediately
504 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
507 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
509 /* no owner, it's easy */
510 if (Resource
->ActiveCount
== 0)
512 Resource
->OwnerThreads
[1].OwnerThread
=ExGetCurrentResourceThread();
513 Resource
->OwnerThreads
[1].a
.OwnerCount
=1;
514 Resource
->ActiveCount
=1;
515 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
516 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
520 if ((Resource
->Flag
& ResourceOwnedExclusive
)
521 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
523 /* exclusive, but by same thread : it's ok */
524 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
525 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
526 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
530 if (Resource
->Flag
& ResourceOwnedExclusive
)
532 /* exclusive by another thread */
535 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
536 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
541 Resource
->NumberOfSharedWaiters
++;
542 /* wait for the semaphore */
543 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
544 KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0);
545 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
546 Resource
->NumberOfSharedWaiters
--;
549 EiAddSharedOwner(Resource
);
550 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
551 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
560 ExAcquireSharedWaitForExclusive (
565 return(ExAcquireResourceSharedLite(Resource
,Wait
));
577 return(ExDeleteResourceLite(Resource
));
585 ExDeleteResourceLite (
589 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource
);
590 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
591 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
592 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
593 return(STATUS_SUCCESS
);;
601 ExGetSharedWaiterCount (
605 return(Resource
->NumberOfSharedWaiters
);
613 ExInitializeResource (
617 return(ExInitializeResourceLite(Resource
));
624 ExInitializeResourceLite (PERESOURCE Resource
)
626 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource
);
627 memset(Resource
,0,sizeof(ERESOURCE
));
628 Resource
->NumberOfSharedWaiters
= 0;
629 Resource
->NumberOfExclusiveWaiters
= 0;
630 KeInitializeSpinLock(&Resource
->SpinLock
);
632 Resource
->ExclusiveWaiters
=
633 ExAllocatePoolWithTag(NonPagedPool
, sizeof(KEVENT
), TAG_EXCLUSIVE_LOCK
);
634 KeInitializeEvent(Resource
->ExclusiveWaiters
,
635 SynchronizationEvent
,
637 Resource
->SharedWaiters
=
638 ExAllocatePoolWithTag(NonPagedPool
,sizeof(KSEMAPHORE
), TAG_SHARED_SEM
);
639 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
640 Resource
->ActiveCount
= 0;
649 ExIsResourceAcquiredExclusiveLite (
653 * FUNCTION: Returns whether the current thread has exclusive access to
656 * Resource = Points to the resource to be queried
657 * RETURNS: TRUE if the caller has exclusive access to the resource,
661 return((Resource
->Flag
& ResourceOwnedExclusive
)
662 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread());
670 ExIsResourceAcquiredSharedLite (
674 * FUNCTION: Returns whether the current thread has shared access to a given
677 * Resource = Points to the resource to be queried
678 * RETURNS: The number of times the caller has acquired shared access to the
683 if (Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
685 return(Resource
->OwnerThreads
[0].a
.OwnerCount
);
687 if (Resource
->OwnerThreads
[1].OwnerThread
== ExGetCurrentResourceThread())
689 return(Resource
->OwnerThreads
[1].a
.OwnerCount
);
691 if (!Resource
->OwnerThreads
[1].a
.TableSize
)
695 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
697 if (Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
699 return Resource
->OwnerTable
[i
].a
.OwnerCount
;
710 ExReinitializeResourceLite (
714 Resource
->NumberOfSharedWaiters
= 0;
715 Resource
->NumberOfExclusiveWaiters
= 0;
716 KeInitializeSpinLock(&Resource
->SpinLock
);
718 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,
720 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
721 Resource
->ActiveCount
= 0;
722 if (Resource
->OwnerTable
)
724 ExFreePool(Resource
->OwnerTable
);
726 Resource
->OwnerThreads
[0].OwnerThread
=0;
727 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
728 Resource
->OwnerThreads
[1].OwnerThread
=0;
729 Resource
->OwnerThreads
[1].a
.OwnerCount
=0;
737 ExReleaseResourceLite (
741 return(ExReleaseResourceForThreadLite(Resource
,
742 ExGetCurrentResourceThread()));
750 ExReleaseResourceForThread (
752 ERESOURCE_THREAD ResourceThreadId
755 return(ExReleaseResourceForThreadLite(Resource
,ResourceThreadId
));
763 ExReleaseResourceForThreadLite (
765 ERESOURCE_THREAD ResourceThreadId
768 * FUNCTION: Releases a resource for the given thread
770 * Resource = Points to the release to release
771 * ResourceThreadId = Identifies the thread that originally acquired
773 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
774 * BUG: We don't support starving exclusive waiters
779 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
780 Resource
, ResourceThreadId
);
782 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
784 if (Resource
->Flag
& ResourceOwnedExclusive
)
786 DPRINT("Releasing from exclusive access\n");
788 Resource
->OwnerThreads
[0].a
.OwnerCount
--;
789 if (Resource
->OwnerThreads
[0].a
.OwnerCount
> 0)
791 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
792 DPRINT("ExReleaseResourceForThreadLite() finished\n");
796 Resource
->OwnerThreads
[0].OwnerThread
= 0;
797 Resource
->ActiveCount
--;
798 Resource
->Flag
&=(~ResourceOwnedExclusive
);
799 assert(Resource
->ActiveCount
== 0);
800 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
801 Resource
->NumberOfExclusiveWaiters
);
802 if (Resource
->NumberOfExclusiveWaiters
)
804 /* get resource to first exclusive waiter */
805 KeSetEvent(Resource
->ExclusiveWaiters
,
808 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
809 DPRINT("ExReleaseResourceForThreadLite() finished\n");
812 DPRINT("Resource->NumberOfSharedWaiters %d\n",
813 Resource
->NumberOfSharedWaiters
);
814 if (Resource
->NumberOfSharedWaiters
)
816 DPRINT("Releasing semaphore\n");
817 KeReleaseSemaphore(Resource
->SharedWaiters
,
819 Resource
->NumberOfSharedWaiters
,
822 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
823 DPRINT("ExReleaseResourceForThreadLite() finished\n");
827 EiRemoveSharedOwner(Resource
, ResourceThreadId
);
829 if (Resource
->ActiveCount
== 0)
831 if (Resource
->NumberOfExclusiveWaiters
)
833 /* get resource to first exclusive waiter */
834 KeSetEvent(Resource
->ExclusiveWaiters
,
840 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
841 DPRINT("ExReleaseResourceForThreadLite() finished\n");
850 ExSetResourceOwnerPointer (
851 IN PERESOURCE Resource
,
852 IN PVOID OwnerPointer