1 /* $Id: resource.c,v 1.14 2000/07/04 01:27:58 ekohl 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>
44 #include <internal/string.h>
47 #include <internal/debug.h>
49 /* FUNCTIONS *****************************************************************/
54 ExTryToAcquireResourceExclusiveLite (
58 * FUNCTION: Attempts to require the resource for exclusive access
60 * Resource = Points to the resource of be acquired
61 * RETURNS: TRUE if the resource was acquired for the caller
62 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
65 return(ExAcquireResourceExclusiveLite(Resource
,FALSE
));
70 ExAcquireResourceExclusive (
75 return(ExAcquireResourceExclusiveLite(Resource
,Wait
));
80 ExAcquireResourceExclusiveLite (
85 * FUNCTION: Acquires a resource exclusively for the calling thread
87 * Resource = Points to the resource to acquire
88 * Wait = Is set to TRUE if the caller should wait to acquire the
89 * resource if it can't be acquired immediately
90 * RETURNS: TRUE if the resource was acquired,
92 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
97 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
100 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
102 /* resource already locked */
103 if((Resource
->Flag
& ResourceOwnedExclusive
)
104 && Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
106 /* it's ok : same lock for same thread */
107 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
108 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
109 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
113 if (Resource
->ActiveCount
&& !Wait
)
115 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
116 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
121 * This is slightly better than it looks because other exclusive
122 * threads who are waiting won't be woken up but there is a race
123 * with new threads trying to grab the resource so we must have
124 * the spinlock, still normally this loop will only be executed
126 * NOTE: We might want to set a timeout to detect deadlock
129 while (Resource
->ActiveCount
)
131 Resource
->NumberOfExclusiveWaiters
++;
132 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
133 KeWaitForSingleObject(Resource
->ExclusiveWaiters
,
138 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
139 Resource
->NumberOfExclusiveWaiters
--;
141 Resource
->Flag
|= ResourceOwnedExclusive
;
142 Resource
->ActiveCount
= 1;
143 Resource
->OwnerThreads
[0].OwnerThread
= ExGetCurrentResourceThread();
144 Resource
->OwnerThreads
[0].a
.OwnerCount
= 1;
145 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
146 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
150 static BOOLEAN
EiRemoveSharedOwner(PERESOURCE Resource
,
151 ERESOURCE_THREAD ResourceThreadId
)
153 * FUNCTION: Removes the current thread from the shared owners of the resource
155 * Resource = Pointer to the resource for which the thread is to be
157 * NOTE: Must be called with the resource spinlock held
162 if (Resource
->OwnerThreads
[1].OwnerThread
== ResourceThreadId
)
164 Resource
->OwnerThreads
[1].a
.OwnerCount
--;
165 Resource
->ActiveCount
--;
166 if (Resource
->OwnerThreads
[1].a
.OwnerCount
== 0)
168 Resource
->OwnerThreads
[1].OwnerThread
= 0;
173 if (Resource
->OwnerThreads
[1].OwnerThread
)
175 /* Oh dear, the caller didn't own the resource after all */
179 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
181 if (Resource
->OwnerTable
[i
].OwnerThread
== ResourceThreadId
)
183 Resource
->OwnerTable
[1].a
.OwnerCount
--;
184 Resource
->ActiveCount
--;
185 if (Resource
->OwnerTable
[1].a
.OwnerCount
== 0)
187 Resource
->OwnerTable
[i
].OwnerThread
= 0;
195 static BOOLEAN
EiAddSharedOwner(PERESOURCE Resource
)
197 * FUNCTION: Adds the current thread to the shared owners of the resource
199 * Resource = Pointer to the resource for which the thread is to be
201 * NOTE: Must be called with the resource spinlock held
204 ERESOURCE_THREAD CurrentThread
= ExGetCurrentResourceThread();
205 POWNER_ENTRY freeEntry
;
208 DPRINT("EiAddSharedOwner(Resource %x)\n", Resource
);
210 if (Resource
->ActiveCount
== 0)
212 /* no owner, it's easy */
213 Resource
->OwnerThreads
[1].OwnerThread
= ExGetCurrentResourceThread();
214 Resource
->OwnerThreads
[1].a
.OwnerCount
= 1;
215 if (Resource
->OwnerTable
!= NULL
)
217 ExFreePool(Resource
->OwnerTable
);
219 Resource
->OwnerTable
= NULL
;
220 Resource
->ActiveCount
= 1;
221 DPRINT("EiAddSharedOwner() = TRUE\n");
226 * now, we must search if this thread has already acquired this resource
227 * then increase ownercount if found, else create new entry or reuse free
230 if (Resource
->OwnerTable
== NULL
)
232 DPRINT("Creating owner table\n");
234 /* allocate ownertable,memset to 0, initialize first entry */
235 Resource
->OwnerTable
= ExAllocatePool(NonPagedPool
,
236 sizeof(OWNER_ENTRY
)*3);
237 if (Resource
->OwnerTable
== NULL
)
242 memset(Resource
->OwnerTable
,sizeof(OWNER_ENTRY
)*3,0);
243 memcpy(&Resource
->OwnerTable
[0], &Resource
->OwnerThreads
[1],
244 sizeof(OWNER_ENTRY
));
246 Resource
->OwnerThreads
[1].OwnerThread
= 0;
247 Resource
->OwnerThreads
[1].a
.TableSize
= 3;
249 Resource
->OwnerTable
[1].OwnerThread
= CurrentThread
;
250 Resource
->OwnerTable
[1].a
.OwnerCount
= 1;
255 DPRINT("Search free entries\n");
257 DPRINT("Number of entries %d\n",
258 Resource
->OwnerThreads
[1].a
.TableSize
);
261 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
263 if (Resource
->OwnerTable
[i
].OwnerThread
== CurrentThread
)
265 DPRINT("Thread already owns resource\n");
266 Resource
->OwnerTable
[i
].a
.OwnerCount
++;
269 if (Resource
->OwnerTable
[i
].OwnerThread
== 0)
271 freeEntry
= &Resource
->OwnerTable
[i
];
275 DPRINT("Found free entry %x\n", freeEntry
);
279 DPRINT("Allocating new entry\n");
281 /* reallocate ownertable with one more entry */
282 freeEntry
= ExAllocatePool(NonPagedPool
,
284 (Resource
->OwnerThreads
[1].a
.TableSize
+1));
285 if (freeEntry
== NULL
)
290 memcpy(freeEntry
,Resource
->OwnerTable
,
291 sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
));
292 ExFreePool(Resource
->OwnerTable
);
293 Resource
->OwnerTable
=freeEntry
;
294 freeEntry
=&Resource
->OwnerTable
[Resource
->OwnerThreads
[1].a
.TableSize
];
295 Resource
->OwnerThreads
[1].a
.TableSize
++;
297 DPRINT("Creating entry\n");
298 freeEntry
->OwnerThread
=ExGetCurrentResourceThread();
299 freeEntry
->a
.OwnerCount
=1;
300 Resource
->ActiveCount
++;
306 ExAcquireResourceSharedLite (
311 * FUNCTION: Acquires the given resource for shared access by the calling
314 * Resource = Points to the resource to acquire
315 * Wait = Is set to TRUE if the caller should be put into wait state
316 * until the resource can be acquired if it cannot be acquired
318 * RETURNS: TRUE, if the resource is acquire
324 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
327 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
329 /* first, resolve trivial cases */
330 if (Resource
->ActiveCount
== 0)
332 EiAddSharedOwner(Resource
);
333 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
334 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
338 if ((Resource
->Flag
& ResourceOwnedExclusive
)
339 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
341 /* exclusive, but by same thread : it's ok */
343 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
345 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
346 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
347 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
351 if ((Resource
->Flag
& ResourceOwnedExclusive
)
352 || Resource
->NumberOfExclusiveWaiters
)
354 /* exclusive by another thread , or thread waiting for exclusive */
357 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
358 DPRINT("ExAcquireResourceSharedLite() = FALSE\n");
363 Resource
->NumberOfSharedWaiters
++;
364 /* wait for the semaphore */
365 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
366 KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0);
367 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
368 Resource
->NumberOfSharedWaiters
--;
372 EiAddSharedOwner(Resource
);
373 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
374 DPRINT("ExAcquireResourceSharedLite() = TRUE\n");
380 ExConvertExclusiveToSharedLite (
384 * FUNCTION: Converts a given resource from acquired for exclusive access
385 * to acquire for shared access
387 * Resource = Points to the resource for which the access should be
389 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
395 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource
);
397 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
399 oldWaiters
= Resource
->NumberOfSharedWaiters
;
401 if (!(Resource
->Flag
& ResourceOwnedExclusive
))
403 /* Might not be what the caller expects, better bug check */
405 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
409 //transfer infos from entry 0 to entry 1 and erase entry 0
410 Resource
->OwnerThreads
[1].OwnerThread
=Resource
->OwnerThreads
[0].OwnerThread
;
411 Resource
->OwnerThreads
[1].a
.OwnerCount
=Resource
->OwnerThreads
[0].a
.OwnerCount
;
412 Resource
->OwnerThreads
[0].OwnerThread
=0;
413 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
414 /* erase exclusive flag */
415 Resource
->Flag
&= (~ResourceOwnedExclusive
);
416 /* if no shared waiters, that's all */
419 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
422 /* else, awake the waiters */
423 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
424 KeReleaseSemaphore(Resource
->SharedWaiters
,0,oldWaiters
,0);
425 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
430 ExDisableResourceBoostLite (
434 Resource
->Flag
|= ResourceDisableBoost
;
439 ExGetExclusiveWaiterCount (
443 return(Resource
->NumberOfExclusiveWaiters
);
448 ExAcquireSharedStarveExclusive (
453 * FUNCTION: Acquires a given resource for shared access without waiting
454 * for any pending attempts to acquire exclusive access to the
457 * Resource = Points to the resource to be acquired for shared access
458 * Wait = Is set to TRUE if the caller will wait until the resource
459 * becomes available when access can't be granted immediately
460 * RETURNS: TRUE if the requested access is granted. The routine returns
461 * FALSE if the input Wait is FALSE and shared access can't be
462 * granted immediately
467 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
470 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
472 /* no owner, it's easy */
473 if (Resource
->ActiveCount
== 0)
475 Resource
->OwnerThreads
[1].OwnerThread
=ExGetCurrentResourceThread();
476 Resource
->OwnerThreads
[1].a
.OwnerCount
=1;
477 Resource
->ActiveCount
=1;
478 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
479 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
483 if ((Resource
->Flag
& ResourceOwnedExclusive
)
484 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
486 /* exclusive, but by same thread : it's ok */
487 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
488 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
489 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
493 if (Resource
->Flag
& ResourceOwnedExclusive
)
495 /* exclusive by another thread */
498 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
499 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
504 Resource
->NumberOfSharedWaiters
++;
505 /* wait for the semaphore */
506 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
507 KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0);
508 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
509 Resource
->NumberOfSharedWaiters
--;
512 EiAddSharedOwner(Resource
);
513 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
514 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
520 ExAcquireSharedWaitForExclusive (
525 return(ExAcquireResourceSharedLite(Resource
,Wait
));
534 return(ExDeleteResourceLite(Resource
));
539 ExDeleteResourceLite (
543 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource
);
544 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
545 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
546 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
547 return(STATUS_SUCCESS
);;
552 ExGetSharedWaiterCount (
556 return(Resource
->NumberOfSharedWaiters
);
561 ExInitializeResource (
565 return(ExInitializeResourceLite(Resource
));
570 ExInitializeResourceLite (
574 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource
);
575 memset(Resource
,0,sizeof(ERESOURCE
));
576 Resource
->NumberOfSharedWaiters
= 0;
577 Resource
->NumberOfExclusiveWaiters
= 0;
578 KeInitializeSpinLock(&Resource
->SpinLock
);
580 Resource
->ExclusiveWaiters
= ExAllocatePool(NonPagedPool
, sizeof(KEVENT
));
581 KeInitializeEvent(Resource
->ExclusiveWaiters
,
582 SynchronizationEvent
,
584 Resource
->SharedWaiters
= ExAllocatePool(NonPagedPool
,sizeof(KSEMAPHORE
));
585 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
586 Resource
->ActiveCount
= 0;
592 ExIsResourceAcquiredExclusiveLite (
596 * FUNCTION: Returns whether the current thread has exclusive access to
599 * Resource = Points to the resource to be queried
600 * RETURNS: TRUE if the caller has exclusive access to the resource,
604 return((Resource
->Flag
& ResourceOwnedExclusive
)
605 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread());
610 ExIsResourceAcquiredSharedLite (
614 * FUNCTION: Returns whether the current thread has shared access to a given
617 * Resource = Points to the resource to be queried
618 * RETURNS: The number of times the caller has acquired shared access to the
623 if (Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
625 return(Resource
->OwnerThreads
[0].a
.OwnerCount
);
627 if (Resource
->OwnerThreads
[1].OwnerThread
== ExGetCurrentResourceThread())
629 return(Resource
->OwnerThreads
[1].a
.OwnerCount
);
631 if (!Resource
->OwnerThreads
[1].a
.TableSize
)
635 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
637 if (Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
639 return Resource
->OwnerTable
[i
].a
.OwnerCount
;
647 ExReinitializeResourceLite (
651 Resource
->NumberOfSharedWaiters
= 0;
652 Resource
->NumberOfExclusiveWaiters
= 0;
653 KeInitializeSpinLock(&Resource
->SpinLock
);
655 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,
657 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
658 Resource
->ActiveCount
= 0;
659 if (Resource
->OwnerTable
)
661 ExFreePool(Resource
->OwnerTable
);
663 Resource
->OwnerThreads
[0].OwnerThread
=0;
664 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
665 Resource
->OwnerThreads
[1].OwnerThread
=0;
666 Resource
->OwnerThreads
[1].a
.OwnerCount
=0;
671 ExReleaseResourceLite (
675 return(ExReleaseResourceForThreadLite(Resource
,
676 ExGetCurrentResourceThread()));
681 ExReleaseResourceForThread (
683 ERESOURCE_THREAD ResourceThreadId
686 return(ExReleaseResourceForThreadLite(Resource
,ResourceThreadId
));
691 ExReleaseResourceForThreadLite (
693 ERESOURCE_THREAD ResourceThreadId
696 * FUNCTION: Releases a resource for the given thread
698 * Resource = Points to the release to release
699 * ResourceThreadId = Identifies the thread that originally acquired
701 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
702 * BUG: We don't support starving exclusive waiters
707 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
708 Resource
, ResourceThreadId
);
710 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
712 if (Resource
->Flag
& ResourceOwnedExclusive
)
714 DPRINT("Releasing from exclusive access\n");
716 Resource
->OwnerThreads
[0].a
.OwnerCount
--;
717 if (Resource
->OwnerThreads
[0].a
.OwnerCount
> 0)
719 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
720 DPRINT("ExReleaseResourceForThreadLite() finished\n");
724 Resource
->OwnerThreads
[0].OwnerThread
= 0;
725 Resource
->ActiveCount
--;
726 Resource
->Flag
&=(~ResourceOwnedExclusive
);
727 assert(Resource
->ActiveCount
== 0);
728 DPRINT("Resource->NumberOfExclusiveWaiters %d\n",
729 Resource
->NumberOfExclusiveWaiters
);
730 if (Resource
->NumberOfExclusiveWaiters
)
732 /* get resource to first exclusive waiter */
733 KeSetEvent(Resource
->ExclusiveWaiters
,
736 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
737 DPRINT("ExReleaseResourceForThreadLite() finished\n");
740 DPRINT("Resource->NumberOfSharedWaiters %d\n",
741 Resource
->NumberOfSharedWaiters
);
742 if (Resource
->NumberOfSharedWaiters
)
744 DPRINT("Releasing semaphore\n");
745 KeReleaseSemaphore(Resource
->SharedWaiters
,
747 Resource
->NumberOfSharedWaiters
,
750 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
751 DPRINT("ExReleaseResourceForThreadLite() finished\n");
755 EiRemoveSharedOwner(Resource
, ResourceThreadId
);
757 if (Resource
->ActiveCount
== 0)
759 if (Resource
->NumberOfExclusiveWaiters
)
761 /* get resource to first exclusive waiter */
762 KeSetEvent(Resource
->ExclusiveWaiters
,
768 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
769 DPRINT("ExReleaseResourceForThreadLite() finished\n");
775 ExSetResourceOwnerPointer (
776 IN PERESOURCE Resource
,
777 IN PVOID OwnerPointer