1 /* $Id: resource.c,v 1.20 2002/09/07 15:12:50 chorns 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 *****************************************************************/
44 #include <internal/debug.h>
47 /* GLOBALS *******************************************************************/
49 #define TAG_OWNER_TABLE TAG('R', 'O', 'W', 'N')
50 #define TAG_EXCLUSIVE_LOCK TAG('E', 'R', 'E', 'L')
51 #define TAG_SHARED_SEM TAG('E', 'R', 'S', 'S')
53 /* FUNCTIONS *****************************************************************/
57 ExAcquireResourceExclusiveLite (
62 * FUNCTION: Acquires a resource exclusively for the calling thread
64 * Resource = Points to the resource to acquire
65 * Wait = Is set to TRUE if the caller should wait to acquire the
66 * resource if it can't be acquired immediately
67 * RETURNS: TRUE if the resource was acquired,
69 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
74 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
77 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
79 /* resource already locked */
80 if((Resource
->Flag
& ResourceOwnedExclusive
)
81 && Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
83 /* it's ok : same lock for same thread */
84 Resource
->OwnerThreads
[0].OwnerCount
++;
85 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
86 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
90 if (Resource
->ActiveCount
&& !Wait
)
92 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
93 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
98 * This is slightly better than it looks because other exclusive
99 * threads who are waiting won't be woken up but there is a race
100 * with new threads trying to grab the resource so we must have
101 * the spinlock, still normally this loop will only be executed
103 * NOTE: We might want to set a timeout to detect deadlock
106 while (Resource
->ActiveCount
)
108 Resource
->NumberOfExclusiveWaiters
++;
109 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
110 KeWaitForSingleObject(Resource
->ExclusiveWaiters
,
115 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
116 Resource
->NumberOfExclusiveWaiters
--;
118 Resource
->Flag
|= ResourceOwnedExclusive
;
119 Resource
->ActiveCount
= 1;
120 Resource
->OwnerThreads
[0].OwnerThread
= ExGetCurrentResourceThread();
121 Resource
->OwnerThreads
[0].OwnerCount
= 1;
122 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
123 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
129 ExTryToAcquireResourceExclusiveLite (
133 * FUNCTION: Attempts to require the resource for exclusive access
135 * Resource = Points to the resource of be acquired
136 * RETURNS: TRUE if the resource was acquired for the caller
137 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
140 return(ExAcquireResourceExclusiveLite(Resource
,FALSE
));
143 #undef ExAcquireResourceExclusive
147 ExAcquireResourceExclusive (
152 return(ExAcquireResourceExclusiveLite(Resource
,Wait
));
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].OwnerCount
--;
170 if (Resource
->OwnerThreads
[1].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].TableSize
; i
++)
186 if (Resource
->OwnerTable
[i
].OwnerThread
== ResourceThreadId
)
188 Resource
->OwnerTable
[i
].OwnerCount
--;
189 if (Resource
->OwnerTable
[i
].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].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].TableSize
= 3;
255 Resource
->OwnerTable
[1].OwnerThread
= CurrentThread
;
256 Resource
->OwnerTable
[1].OwnerCount
= 1;
257 Resource
->ActiveCount
++;
262 DPRINT("Search free entries\n");
264 DPRINT("Number of entries %d\n",
265 Resource
->OwnerThreads
[1].TableSize
);
268 for (i
=0; i
<Resource
->OwnerThreads
[1].TableSize
; i
++)
270 if (Resource
->OwnerTable
[i
].OwnerThread
== CurrentThread
)
272 DPRINT("Thread already owns resource\n");
273 Resource
->OwnerTable
[i
].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].TableSize
+1),
295 if (freeEntry
== NULL
)
300 memcpy(freeEntry
,Resource
->OwnerTable
,
301 sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].TableSize
));
302 ExFreePool(Resource
->OwnerTable
);
303 Resource
->OwnerTable
=freeEntry
;
304 freeEntry
=&Resource
->OwnerTable
[Resource
->OwnerThreads
[1].TableSize
];
305 Resource
->OwnerThreads
[1].TableSize
++;
307 DPRINT("Creating entry\n");
308 freeEntry
->OwnerThread
=ExGetCurrentResourceThread();
309 freeEntry
->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].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].OwnerCount
=Resource
->OwnerThreads
[0].OwnerCount
;
428 Resource
->OwnerThreads
[0].OwnerThread
=0;
429 Resource
->OwnerThreads
[0].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].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].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
));
544 #undef ExDeleteResource
552 return(ExDeleteResourceLite(Resource
));
557 ExDeleteResourceLite (
561 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource
);
562 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
563 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
564 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
565 return(STATUS_SUCCESS
);;
570 ExGetSharedWaiterCount (
574 return(Resource
->NumberOfSharedWaiters
);
577 #undef ExInitializeResource
581 ExInitializeResource (
585 return(ExInitializeResourceLite(Resource
));
589 ExInitializeResourceLite (PERESOURCE Resource
)
591 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource
);
592 memset(Resource
,0,sizeof(ERESOURCE
));
593 Resource
->NumberOfSharedWaiters
= 0;
594 Resource
->NumberOfExclusiveWaiters
= 0;
595 KeInitializeSpinLock(&Resource
->SpinLock
);
597 Resource
->ExclusiveWaiters
=
598 ExAllocatePoolWithTag(NonPagedPool
, sizeof(KEVENT
), TAG_EXCLUSIVE_LOCK
);
599 KeInitializeEvent(Resource
->ExclusiveWaiters
,
600 SynchronizationEvent
,
602 Resource
->SharedWaiters
=
603 ExAllocatePoolWithTag(NonPagedPool
,sizeof(KSEMAPHORE
), TAG_SHARED_SEM
);
604 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
605 Resource
->ActiveCount
= 0;
611 ExIsResourceAcquiredExclusiveLite (
615 * FUNCTION: Returns whether the current thread has exclusive access to
618 * Resource = Points to the resource to be queried
619 * RETURNS: TRUE if the caller has exclusive access to the resource,
623 return((Resource
->Flag
& ResourceOwnedExclusive
)
624 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread());
629 ExIsResourceAcquiredSharedLite (
633 * FUNCTION: Returns whether the current thread has shared access to a given
636 * Resource = Points to the resource to be queried
637 * RETURNS: The number of times the caller has acquired shared access to the
642 if (Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
644 return(Resource
->OwnerThreads
[0].OwnerCount
);
646 if (Resource
->OwnerThreads
[1].OwnerThread
== ExGetCurrentResourceThread())
648 return(Resource
->OwnerThreads
[1].OwnerCount
);
650 if (!Resource
->OwnerThreads
[1].TableSize
)
654 for (i
=0; i
<Resource
->OwnerThreads
[1].TableSize
; i
++)
656 if (Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
658 return Resource
->OwnerTable
[i
].OwnerCount
;
666 ExReinitializeResourceLite (
670 Resource
->NumberOfSharedWaiters
= 0;
671 Resource
->NumberOfExclusiveWaiters
= 0;
672 KeInitializeSpinLock(&Resource
->SpinLock
);
674 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,
676 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
677 Resource
->ActiveCount
= 0;
678 if (Resource
->OwnerTable
)
680 ExFreePool(Resource
->OwnerTable
);
682 Resource
->OwnerThreads
[0].OwnerThread
=0;
683 Resource
->OwnerThreads
[0].OwnerCount
=0;
684 Resource
->OwnerThreads
[1].OwnerThread
=0;
685 Resource
->OwnerThreads
[1].OwnerCount
=0;
690 ExReleaseResourceLite (
694 ExReleaseResourceForThreadLite(Resource
,
695 ExGetCurrentResourceThread());
698 #undef ExReleaseResourceForThread
702 ExReleaseResourceForThread (
704 ERESOURCE_THREAD ResourceThreadId
707 ExReleaseResourceForThreadLite(Resource
,ResourceThreadId
);
712 ExReleaseResourceForThreadLite (
714 ERESOURCE_THREAD ResourceThreadId
717 * FUNCTION: Releases a resource for the given thread
719 * Resource = Points to the release to release
720 * ResourceThreadId = Identifies the thread that originally acquired
722 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
723 * BUG: We don't support starving exclusive waiters
728 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
729 Resource
, ResourceThreadId
);
731 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
733 if (Resource
->Flag
& ResourceOwnedExclusive
)
735 DPRINT("Releasing from exclusive access\n");
737 Resource
->OwnerThreads
[0].OwnerCount
--;
738 if (Resource
->OwnerThreads
[0].OwnerCount
> 0)
740 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
741 DPRINT("ExReleaseResourceForThreadLite() finished\n");
745 Resource
->OwnerThreads
[0].OwnerThread
= 0;
746 Resource
->ActiveCount
--;
747 Resource
->Flag
&=(~ResourceOwnedExclusive
);
748 assert(Resource
->ActiveCount
== 0);
749 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
750 Resource
->NumberOfExclusiveWaiters
);
751 if (Resource
->NumberOfExclusiveWaiters
)
753 /* get resource to first exclusive waiter */
754 KeSetEvent(Resource
->ExclusiveWaiters
,
757 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
758 DPRINT("ExReleaseResourceForThreadLite() finished\n");
761 DPRINT("Resource->NumberOfSharedWaiters %d\n",
762 Resource
->NumberOfSharedWaiters
);
763 if (Resource
->NumberOfSharedWaiters
)
765 DPRINT("Releasing semaphore\n");
766 KeReleaseSemaphore(Resource
->SharedWaiters
,
768 Resource
->NumberOfSharedWaiters
,
771 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
772 DPRINT("ExReleaseResourceForThreadLite() finished\n");
776 EiRemoveSharedOwner(Resource
, ResourceThreadId
);
778 if (Resource
->ActiveCount
== 0)
780 if (Resource
->NumberOfExclusiveWaiters
)
782 /* get resource to first exclusive waiter */
783 KeSetEvent(Resource
->ExclusiveWaiters
,
789 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
790 DPRINT("ExReleaseResourceForThreadLite() finished\n");
796 ExSetResourceOwnerPointer (
797 IN PERESOURCE Resource
,
798 IN PVOID OwnerPointer