2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: ntoskrnl/ex/resource.c
5 * PURPOSE: Resource synchronization construct
13 * Usage of ERESOURCE members is not documented.
14 * From names of members and functionnalities, we can assume :
16 * OwnerTable = list of threads who have shared access(if more than one)
17 * ActiveCount = number of threads who have access to the resource
18 * Flag = bits : ResourceOwnedExclusive=0x80
19 * ResourceNeverExclusive=0x10
20 * ResourceReleaseByOtherThread=0x20
21 * SharedWaiters = semaphore, used to manage wait list of shared waiters.
22 * ExclusiveWaiters = event, used to manage wait list of exclusive waiters.
23 * OwnerThreads[0]= thread who have exclusive access
24 * OwnerThreads[1]= if only one thread own the resource
25 * thread who have shared access
28 * and TableSize = number of entries in the owner table
29 * NumberOfExclusiveWaiters = number of threads waiting for exclusive access.
30 * NumberOfSharedWaiters = number of threads waiting for exclusive access.
34 #define ResourceOwnedExclusive 0x80
36 /* INCLUDES *****************************************************************/
38 #include <ddk/ntddk.h>
39 #include <internal/ke.h>
41 #include <internal/string.h>
44 #include <internal/debug.h>
46 /* FUNCTIONS *****************************************************************/
49 BOOLEAN
ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource
)
51 * FUNCTION: Attempts to require the resource for exclusive access
53 * Resource = Points to the resource of be acquired
54 * RETURNS: TRUE if the resource was acquired for the caller
55 * NOTES: Must be acquired at IRQL < DISPATCH_LEVEL
58 return(ExAcquireResourceExclusiveLite(Resource
,FALSE
));
61 BOOLEAN
ExAcquireResourceExclusive(PERESOURCE Resource
, BOOLEAN Wait
)
63 return(ExAcquireResourceExclusiveLite(Resource
,Wait
));
66 BOOLEAN
ExAcquireResourceExclusiveLite(PERESOURCE Resource
, BOOLEAN Wait
)
68 * FUNCTION: Acquires a resource exclusively for the calling thread
70 * Resource = Points to the resource to acquire
71 * Wait = Is set to TRUE if the caller should wait to acquire the
72 * resource if it can't be acquired immediately
73 * RETURNS: TRUE if the resource was acquired,
75 * NOTES: Must be called at IRQL < DISPATCH_LEVEL
80 DPRINT("ExAcquireResourceExclusiveLite(Resource %x, Wait %d)\n",
83 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
85 /* resource already locked */
86 if((Resource
->Flag
& ResourceOwnedExclusive
)
87 && Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
89 /* it's ok : same lock for same thread */
90 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
91 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
92 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
96 if (Resource
->ActiveCount
&& !Wait
)
98 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
99 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
104 * This is slightly better than it looks because other exclusive
105 * threads who are waiting won't be woken up but there is a race
106 * with new threads trying to grab the resource so we must have
107 * the spinlock, still normally this loop will only be executed
109 * NOTE: We might want to set a timeout to detect deadlock
112 while (Resource
->ActiveCount
)
114 Resource
->NumberOfExclusiveWaiters
++;
115 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
116 KeWaitForSingleObject(Resource
->ExclusiveWaiters
,
121 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
122 Resource
->NumberOfExclusiveWaiters
--;
124 Resource
->Flag
|= ResourceOwnedExclusive
;
125 Resource
->ActiveCount
= 1;
126 Resource
->OwnerThreads
[0].OwnerThread
= ExGetCurrentResourceThread();
127 Resource
->OwnerThreads
[0].a
.OwnerCount
= 1;
128 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
129 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
133 static BOOLEAN
EiRemoveSharedOwner(PERESOURCE Resource
,
134 ERESOURCE_THREAD ResourceThreadId
)
136 * FUNCTION: Removes the current thread from the shared owners of the resource
138 * Resource = Pointer to the resource for which the thread is to be
140 * NOTE: Must be called with the resource spinlock held
145 if (Resource
->OwnerThreads
[1].OwnerThread
== ResourceThreadId
)
147 Resource
->OwnerThreads
[1].a
.OwnerCount
--;
148 Resource
->ActiveCount
--;
149 if (Resource
->OwnerThreads
[1].a
.OwnerCount
== 0)
151 Resource
->OwnerThreads
[1].OwnerThread
= 0;
156 if (Resource
->OwnerThreads
[1].OwnerThread
)
158 /* Oh dear, the caller didn't own the resource after all */
162 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
164 if (Resource
->OwnerTable
[i
].OwnerThread
== ResourceThreadId
)
166 Resource
->OwnerTable
[1].a
.OwnerCount
--;
167 Resource
->ActiveCount
--;
168 if (Resource
->OwnerTable
[1].a
.OwnerCount
== 0)
170 Resource
->OwnerTable
[i
].OwnerThread
= 0;
178 static BOOLEAN
EiAddSharedOwner(PERESOURCE Resource
)
180 * FUNCTION: Adds the current thread to the shared owners of the resource
182 * Resource = Pointer to the resource for which the thread is to be
184 * NOTE: Must be called with the resource spinlock held
187 ERESOURCE_THREAD CurrentThread
= ExGetCurrentResourceThread();
188 POWNER_ENTRY freeEntry
;
192 * now, we must search if this thread has already acquired this resource
193 * then increase ownercount if found, else create new entry or reuse free
196 if (!Resource
->OwnerThreads
[1].a
.TableSize
)
198 /* allocate ownertable,memset to 0, initialize first entry */
199 Resource
->OwnerTable
= ExAllocatePool(NonPagedPool
,
200 sizeof(OWNER_ENTRY
)*3);
201 if (Resource
->OwnerTable
== NULL
)
206 memset(Resource
->OwnerTable
,sizeof(OWNER_ENTRY
)*3,0);
207 memcpy(&Resource
->OwnerTable
[0], &Resource
->OwnerThreads
[1],
208 sizeof(OWNER_ENTRY
));
210 Resource
->OwnerThreads
[1].OwnerThread
= 0;
211 Resource
->OwnerThreads
[1].a
.TableSize
= 3;
213 Resource
->OwnerTable
[1].OwnerThread
= CurrentThread
;
214 Resource
->OwnerTable
[1].a
.OwnerCount
= 1;
220 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
222 if (Resource
->OwnerTable
[i
].OwnerThread
== CurrentThread
)
224 Resource
->OwnerTable
[i
].a
.OwnerCount
++;
227 if (Resource
->OwnerTable
[i
].OwnerThread
== 0)
229 freeEntry
= &Resource
->OwnerTable
[i
];
235 /* reallocate ownertable with one more entry */
236 freeEntry
= ExAllocatePool(NonPagedPool
,
238 (Resource
->OwnerThreads
[1].a
.TableSize
+1));
239 if (freeEntry
== NULL
)
244 memcpy(freeEntry
,Resource
->OwnerTable
,
245 sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
));
246 ExFreePool(Resource
->OwnerTable
);
247 Resource
->OwnerTable
=freeEntry
;
248 freeEntry
=&Resource
->OwnerTable
[Resource
->OwnerThreads
[1].a
.TableSize
];
249 Resource
->OwnerThreads
[1].a
.TableSize
++;
251 freeEntry
->OwnerThread
=ExGetCurrentResourceThread();
252 freeEntry
->a
.OwnerCount
=1;
253 Resource
->ActiveCount
++;
257 BOOLEAN
ExAcquireResourceSharedLite(PERESOURCE Resource
, BOOLEAN Wait
)
259 * FUNCTION: Acquires the given resource for shared access by the calling
262 * Resource = Points to the resource to acquire
263 * Wait = Is set to TRUE if the caller should be put into wait state
264 * until the resource can be acquired if it cannot be acquired
266 * RETURNS: TRUE, if the resource is acquire
272 DPRINT("ExAcquireResourceSharedLite(Resource %x, Wait %d)\n",
275 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
277 /* first, resolve trivial cases */
278 if (Resource
->ActiveCount
== 0)
280 /* no owner, it's easy */
281 Resource
->OwnerThreads
[1].OwnerThread
= ExGetCurrentResourceThread();
282 Resource
->OwnerThreads
[1].a
.OwnerCount
= 1;
283 Resource
->ActiveCount
= 1;
284 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
285 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
289 if ((Resource
->Flag
& ResourceOwnedExclusive
)
290 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
292 /* exclusive, but by same thread : it's ok */
294 * NOTE: Is this correct? Seems the same as ExConvertExclusiveToShared
296 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
297 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
298 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
302 if ((Resource
->Flag
& ResourceOwnedExclusive
)
303 || Resource
->NumberOfExclusiveWaiters
)
305 /* exclusive by another thread , or thread waiting for exclusive */
308 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
309 DPRINT("ExAcquireResourceExclusiveLite() = FALSE\n");
314 Resource
->NumberOfSharedWaiters
++;
315 /* wait for the semaphore */
316 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
317 KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0);
318 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
319 Resource
->NumberOfSharedWaiters
--;
323 EiAddSharedOwner(Resource
);
324 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
325 DPRINT("ExAcquireResourceExclusiveLite() = TRUE\n");
329 VOID
ExConvertExclusiveToSharedLite(PERESOURCE Resource
)
331 * FUNCTION: Converts a given resource from acquired for exclusive access
332 * to acquire for shared access
334 * Resource = Points to the resource for which the access should be
336 * NOTES: Caller must be running at IRQL < DISPATCH_LEVEL
342 DPRINT("ExConvertExclusiveToSharedLite(Resource %x)\n", Resource
);
344 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
346 oldWaiters
= Resource
->NumberOfSharedWaiters
;
348 if (!(Resource
->Flag
& ResourceOwnedExclusive
))
350 /* Might not be what the caller expects, better bug check */
352 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
356 //transfer infos from entry 0 to entry 1 and erase entry 0
357 Resource
->OwnerThreads
[1].OwnerThread
=Resource
->OwnerThreads
[0].OwnerThread
;
358 Resource
->OwnerThreads
[1].a
.OwnerCount
=Resource
->OwnerThreads
[0].a
.OwnerCount
;
359 Resource
->OwnerThreads
[0].OwnerThread
=0;
360 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
361 /* erase exclusive flag */
362 Resource
->Flag
&= (~ResourceOwnedExclusive
);
363 /* if no shared waiters, that's all */
366 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
369 /* else, awake the waiters */
370 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
371 KeReleaseSemaphore(Resource
->SharedWaiters
,0,oldWaiters
,0);
372 DPRINT("ExConvertExclusiveToSharedLite() finished\n");
375 ULONG
ExGetExclusiveWaiterCount(PERESOURCE Resource
)
377 return(Resource
->NumberOfExclusiveWaiters
);
380 BOOLEAN
ExAcquireSharedStarveExclusive(PERESOURCE Resource
, BOOLEAN Wait
)
382 * FUNCTION: Acquires a given resource for shared access without waiting
383 * for any pending attempts to acquire exclusive access to the
386 * Resource = Points to the resource to be acquired for shared access
387 * Wait = Is set to TRUE if the caller will wait until the resource
388 * becomes available when access can't be granted immediately
389 * RETURNS: TRUE if the requested access is granted. The routine returns
390 * FALSE if the input Wait is FALSE and shared access can't be
391 * granted immediately
396 DPRINT("ExAcquireSharedStarveExclusive(Resource %x, Wait %d)\n",
399 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
401 /* no owner, it's easy */
402 if (Resource
->ActiveCount
== 0)
404 Resource
->OwnerThreads
[1].OwnerThread
=ExGetCurrentResourceThread();
405 Resource
->OwnerThreads
[1].a
.OwnerCount
=1;
406 Resource
->ActiveCount
=1;
407 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
408 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
412 if ((Resource
->Flag
& ResourceOwnedExclusive
)
413 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
415 /* exclusive, but by same thread : it's ok */
416 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
417 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
418 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
422 if (Resource
->Flag
& ResourceOwnedExclusive
)
424 /* exclusive by another thread */
427 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
428 DPRINT("ExAcquireSharedStarveExclusive() = FALSE\n");
433 Resource
->NumberOfSharedWaiters
++;
434 /* wait for the semaphore */
435 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
436 KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0);
437 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
438 Resource
->NumberOfSharedWaiters
--;
441 EiAddSharedOwner(Resource
);
442 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
443 DPRINT("ExAcquireSharedStarveExclusive() = TRUE\n");
447 BOOLEAN
ExAcquireSharedWaitForExclusive(PERESOURCE Resource
, BOOLEAN Wait
)
449 return(ExAcquireResourceSharedLite(Resource
,Wait
));
452 NTSTATUS
ExDeleteResource(PERESOURCE Resource
)
454 return(ExDeleteResourceLite(Resource
));
457 NTSTATUS
ExDeleteResourceLite(PERESOURCE Resource
)
459 DPRINT("ExDeleteResourceLite(Resource %x)\n", Resource
);
460 if (Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
461 if (Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
462 if (Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
463 return(STATUS_SUCCESS
);;
466 ERESOURCE_THREAD
ExGetCurrentResourceThread()
468 return((ERESOURCE_THREAD
)PsGetCurrentThread());
471 ULONG
ExGetSharedWaiterCount(PERESOURCE Resource
)
473 return(Resource
->NumberOfSharedWaiters
);
476 NTSTATUS
ExInitializeResource(PERESOURCE Resource
)
478 return(ExInitializeResourceLite(Resource
));
481 NTSTATUS
ExInitializeResourceLite(PERESOURCE Resource
)
483 DPRINT("ExInitializeResourceLite(Resource %x)\n", Resource
);
484 memset(Resource
,0,sizeof(ERESOURCE
));
485 Resource
->NumberOfSharedWaiters
= 0;
486 Resource
->NumberOfExclusiveWaiters
= 0;
487 KeInitializeSpinLock(&Resource
->SpinLock
);
489 Resource
->ExclusiveWaiters
= ExAllocatePool(NonPagedPool
, sizeof(KEVENT
));
490 KeInitializeEvent(Resource
->ExclusiveWaiters
,
491 SynchronizationEvent
,
493 Resource
->SharedWaiters
= ExAllocatePool(NonPagedPool
,sizeof(KSEMAPHORE
));
494 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
495 Resource
->ActiveCount
= 0;
499 BOOLEAN
ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource
)
501 * FUNCTION: Returns whether the current thread has exclusive access to
504 * Resource = Points to the resource to be queried
505 * RETURNS: TRUE if the caller has exclusive access to the resource,
509 return((Resource
->Flag
& ResourceOwnedExclusive
)
510 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread());
513 ULONG
ExIsResourceAcquiredSharedLite(PERESOURCE Resource
)
515 * FUNCTION: Returns whether the current thread has shared access to a given
518 * Resource = Points to the resource to be queried
519 * RETURNS: The number of times the caller has acquired shared access to the
524 if (Resource
->OwnerThreads
[0].OwnerThread
== ExGetCurrentResourceThread())
526 return(Resource
->OwnerThreads
[0].a
.OwnerCount
);
528 if (Resource
->OwnerThreads
[1].OwnerThread
== ExGetCurrentResourceThread())
530 return(Resource
->OwnerThreads
[1].a
.OwnerCount
);
532 if (!Resource
->OwnerThreads
[1].a
.TableSize
)
536 for (i
=0; i
<Resource
->OwnerThreads
[1].a
.TableSize
; i
++)
538 if (Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
540 return Resource
->OwnerTable
[i
].a
.OwnerCount
;
546 VOID
ExReinitializeResourceLite(PERESOURCE Resource
)
548 Resource
->NumberOfSharedWaiters
= 0;
549 Resource
->NumberOfExclusiveWaiters
= 0;
550 KeInitializeSpinLock(&Resource
->SpinLock
);
552 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,
554 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
555 Resource
->ActiveCount
= 0;
556 if (Resource
->OwnerTable
)
558 ExFreePool(Resource
->OwnerTable
);
560 Resource
->OwnerThreads
[0].OwnerThread
=0;
561 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
562 Resource
->OwnerThreads
[1].OwnerThread
=0;
563 Resource
->OwnerThreads
[1].a
.OwnerCount
=0;
566 VOID
ExReleaseResourceLite(PERESOURCE Resource
)
568 return(ExReleaseResourceForThreadLite(Resource
,
569 ExGetCurrentResourceThread()));
572 VOID
ExReleaseResource(PERESOURCE Resource
)
574 return ExReleaseResourceForThreadLite(Resource
,ExGetCurrentResourceThread());
577 VOID
ExReleaseResourceForThread(PERESOURCE Resource
,
578 ERESOURCE_THREAD ResourceThreadId
)
580 return(ExReleaseResourceForThreadLite(Resource
,ResourceThreadId
));
583 VOID
ExReleaseResourceForThreadLite(PERESOURCE Resource
,
584 ERESOURCE_THREAD ResourceThreadId
)
586 * FUNCTION: Releases a resource for the given thread
588 * Resource = Points to the release to release
589 * ResourceThreadId = Identifies the thread that originally acquired
591 * NOTES: Must be running at IRQL < DISPATCH_LEVEL
592 * BUG: We don't support starving exclusive waiters
597 DPRINT("ExReleaseResourceForThreadLite(Resource %x, ResourceThreadId %x)\n",
598 Resource
, ResourceThreadId
);
600 KeAcquireSpinLock(&Resource
->SpinLock
, &oldIrql
);
602 if (Resource
->Flag
& ResourceOwnedExclusive
)
604 Resource
->OwnerThreads
[0].a
.OwnerCount
--;
605 if (Resource
->OwnerThreads
[0].a
.OwnerCount
> 0)
607 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
608 DPRINT("ExReleaseResourceForThreadLite() finished\n");
612 Resource
->OwnerThreads
[0].OwnerThread
= 0;
613 Resource
->ActiveCount
--;
614 Resource
->Flag
&=(~ResourceOwnedExclusive
);
615 assert(Resource
->ActiveCount
== 0);
616 if (Resource
->NumberOfExclusiveWaiters
)
618 /* get resource to first exclusive waiter */
619 KeSetEvent(Resource
->ExclusiveWaiters
,
622 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
623 DPRINT("ExReleaseResourceForThreadLite() finished\n");
626 if (Resource
->NumberOfSharedWaiters
)
628 KeReleaseSemaphore(Resource
->SharedWaiters
,
630 Resource
->ActiveCount
,
633 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
634 DPRINT("ExReleaseResourceForThreadLite() finished\n");
638 EiRemoveSharedOwner(Resource
, ResourceThreadId
);
640 if (Resource
->ActiveCount
== 0)
642 if (Resource
->NumberOfExclusiveWaiters
)
644 /* get resource to first exclusive waiter */
645 KeSetEvent(Resource
->ExclusiveWaiters
,
651 KeReleaseSpinLock(&Resource
->SpinLock
, oldIrql
);
652 DPRINT("ExReleaseResourceForThreadLite() finished\n");