2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * FILE: kernel/excutive/resource.c
5 * PURPOSE: Graceful system shutdown if a bug is detected
6 * PROGRAMMER: David Welch (welch@mcmail.com)
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 *****************************************************************/
48 BOOLEAN
ExAcquireResourceExclusive(PERESOURCE Resource
, BOOLEAN Wait
)
50 return ExAcquireResourceExclusiveLite(Resource
,Wait
);
53 BOOLEAN
ExAcquireResourceExclusiveLite(PERESOURCE Resource
, BOOLEAN Wait
)
55 // FIXME : protect essential tasks of this function from interrupts
56 // perhaps with KeRaiseIrql(SYNCH_LEVEL);
57 if(Resource
->ActiveCount
) // resource already locked
59 if((Resource
->Flag
& ResourceOwnedExclusive
)
60 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
61 { // it's ok : same lock for same thread
62 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
65 if( ! Wait
) return FALSE
;
66 Resource
->NumberOfExclusiveWaiters
++;
67 if(KeWaitForSingleObject(Resource
->ExclusiveWaiters
,Executive
,KernelMode
,FALSE
,NULL
)
70 //FIXME : what to do if timeout ?
71 Resource
->NumberOfExclusiveWaiters
--;
74 Resource
->NumberOfExclusiveWaiters
--;
76 Resource
->OwnerThreads
[0].a
.OwnerCount
=1;
77 Resource
->Flag
|= ResourceOwnedExclusive
;
78 Resource
->ActiveCount
=1;
79 Resource
->OwnerThreads
[0].OwnerThread
=ExGetCurrentResourceThread();
83 BOOLEAN
ExAcquireResourceSharedLite(PERESOURCE Resource
, BOOLEAN Wait
)
85 POWNER_ENTRY freeEntry
;
87 // FIXME : protect from interrupts
88 // first, resolve trivial cases
89 if(Resource
->ActiveCount
==0) // no owner, it's easy
91 Resource
->OwnerThreads
[1].OwnerThread
=ExGetCurrentResourceThread();
92 Resource
->OwnerThreads
[1].a
.OwnerCount
=1;
93 Resource
->ActiveCount
=1;
96 if((Resource
->Flag
& ResourceOwnedExclusive
)
97 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
98 {// exclusive, but by same thread : it's ok
99 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
102 if((Resource
->Flag
& ResourceOwnedExclusive
)
103 || Resource
->NumberOfExclusiveWaiters
)
104 { //exclusive by another thread , or thread waiting for exclusive
105 if(!Wait
) return FALSE
;
108 Resource
->NumberOfSharedWaiters
++;
109 // wait for the semaphore
110 if(KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0)==STATUS_TIMEOUT
)
112 //FIXME : what to do ?
117 //now, we must search if this thread has already acquired this resource
118 //then increase ownercount if found, else create new entry or reuse free entry
119 if(!Resource
->OwnerThreads
[1].a
.TableSize
)
121 // allocate ownertable,memset to 0, initialize first entry
122 Resource
->OwnerTable
=ExAllocatePool(NonPagedPool
,sizeof(OWNER_ENTRY
)*3);
123 memset(Resource
->OwnerTable
,sizeof(OWNER_ENTRY
)*3,0);
124 Resource
->OwnerTable
[0].OwnerThread
= Resource
->OwnerThreads
[1].OwnerThread
;
125 Resource
->OwnerTable
[0].a
.OwnerCount
=Resource
->OwnerThreads
[1].a
.OwnerCount
;
126 Resource
->OwnerThreads
[1].OwnerThread
=0;;
127 Resource
->OwnerThreads
[1].a
.TableSize
=3;
128 Resource
->OwnerTable
[1].OwnerThread
=ExGetCurrentResourceThread();
129 Resource
->OwnerTable
[1].a
.OwnerCount
=1;
133 for(i
=0;i
<Resource
->OwnerThreads
[1].a
.TableSize
;i
++)
135 if(Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
136 {// old entry for this thread found
137 Resource
->OwnerTable
[i
].a
.OwnerCount
++;
140 if(Resource
->OwnerTable
[i
].OwnerThread
==0)
141 freeEntry
= &Resource
->OwnerTable
[i
];
145 // reallocate ownertable with one more entry
146 freeEntry
=ExAllocatePool(NonPagedPool
147 ,sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
+1));
148 memcpy(freeEntry
,Resource
->OwnerTable
149 ,sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
));
150 ExFreePool(Resource
->OwnerTable
);
151 Resource
->OwnerTable
=freeEntry
;
152 freeEntry
=&Resource
->OwnerTable
[Resource
->OwnerThreads
[1].a
.TableSize
];
153 Resource
->OwnerThreads
[1].a
.TableSize
++;
155 freeEntry
->OwnerThread
=ExGetCurrentResourceThread();
156 freeEntry
->a
.OwnerCount
=1;
157 Resource
->ActiveCount
++;
161 VOID
ExConvertExclusiveToSharedLite(PERESOURCE Resource
)
163 int oldWaiters
=Resource
->NumberOfSharedWaiters
;
164 if(!Resource
->Flag
& ResourceOwnedExclusive
) return;
165 //transfer infos from entry 0 to entry 1 and erase entry 0
166 Resource
->OwnerThreads
[1].OwnerThread
=Resource
->OwnerThreads
[0].OwnerThread
;
167 Resource
->OwnerThreads
[1].a
.OwnerCount
=Resource
->OwnerThreads
[0].a
.OwnerCount
;
168 Resource
->OwnerThreads
[0].OwnerThread
=0;
169 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
170 //erase exclusive flag
171 Resource
->Flag
&= (~ResourceOwnedExclusive
);
172 //if no shared waiters, that's all :
173 if(!oldWaiters
) return;
174 //else, awake the waiters :
175 Resource
->ActiveCount
+= oldWaiters
;
176 Resource
->NumberOfSharedWaiters
=0;
177 KeReleaseSemaphore(Resource
->SharedWaiters
,0,oldWaiters
,0);
180 ULONG
ExGetExclusiveWaiterCount(PERESOURCE Resource
)
182 return Resource
->NumberOfExclusiveWaiters
;
185 BOOLEAN
ExAcquireSharedStarveExclusive(PERESOURCE Resource
, BOOLEAN Wait
)
187 POWNER_ENTRY freeEntry
;
189 // FIXME : protect from interrupts
190 // first, resolve trivial cases
191 if(Resource
->ActiveCount
==0) // no owner, it's easy
193 Resource
->OwnerThreads
[1].OwnerThread
=ExGetCurrentResourceThread();
194 Resource
->OwnerThreads
[1].a
.OwnerCount
=1;
195 Resource
->ActiveCount
=1;
198 if((Resource
->Flag
& ResourceOwnedExclusive
)
199 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
200 {// exclusive, but by same thread : it's ok
201 Resource
->OwnerThreads
[0].a
.OwnerCount
++;
204 if(Resource
->Flag
& ResourceOwnedExclusive
)
205 { //exclusive by another thread , or thread waiting for exclusive
206 if(!Wait
) return FALSE
;
209 Resource
->NumberOfSharedWaiters
++;
210 // wait for the semaphore
211 if(KeWaitForSingleObject(Resource
->SharedWaiters
,0,0,0,0)==STATUS_TIMEOUT
)
213 //FIXME : what to do ?
218 //now, we must search if this thread has already acquired this resource
219 //then increase ownercount if found, else create new entry or reuse free entry
220 if(!Resource
->OwnerThreads
[1].a
.TableSize
)
222 // allocate ownertable,memset to 0, initialize first entry
223 Resource
->OwnerTable
=ExAllocatePool(NonPagedPool
,sizeof(OWNER_ENTRY
)*3);
224 memset(Resource
->OwnerTable
,sizeof(OWNER_ENTRY
)*3,0);
225 Resource
->OwnerTable
[0].OwnerThread
= Resource
->OwnerThreads
[1].OwnerThread
;
226 Resource
->OwnerTable
[0].a
.OwnerCount
=Resource
->OwnerThreads
[1].a
.OwnerCount
;
227 Resource
->OwnerThreads
[1].OwnerThread
=0;;
228 Resource
->OwnerThreads
[1].a
.TableSize
=3;
229 Resource
->OwnerTable
[1].OwnerThread
=ExGetCurrentResourceThread();
230 Resource
->OwnerTable
[1].a
.OwnerCount
=1;
234 for(i
=0;i
<Resource
->OwnerThreads
[1].a
.TableSize
;i
++)
236 if(Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
237 {// old entry for this thread found
238 Resource
->OwnerTable
[i
].a
.OwnerCount
++;
241 if(Resource
->OwnerTable
[i
].OwnerThread
==0)
242 freeEntry
= &Resource
->OwnerTable
[i
];
246 // reallocate ownertable with one more entry
247 freeEntry
=ExAllocatePool(NonPagedPool
248 ,sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
+1));
249 memcpy(freeEntry
,Resource
->OwnerTable
250 ,sizeof(OWNER_ENTRY
)*(Resource
->OwnerThreads
[1].a
.TableSize
));
251 ExFreePool(Resource
->OwnerTable
);
252 Resource
->OwnerTable
=freeEntry
;
253 freeEntry
=&Resource
->OwnerTable
[Resource
->OwnerThreads
[1].a
.TableSize
];
254 Resource
->OwnerThreads
[1].a
.TableSize
++;
256 freeEntry
->OwnerThread
=ExGetCurrentResourceThread();
257 freeEntry
->a
.OwnerCount
=1;
258 Resource
->ActiveCount
++;
262 BOOLEAN
ExAcquireSharedWaitForExclusive(PERESOURCE Resource
, BOOLEAN Wait
)
264 return ExAcquireResourceSharedLite(Resource
,Wait
);
267 NTSTATUS
ExDeleteResource(PERESOURCE Resource
)
269 if(Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
270 if(Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
271 if(Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
275 NTSTATUS
ExDeleteResourceLite(PERESOURCE Resource
)
277 if(Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
278 if(Resource
->SharedWaiters
) ExFreePool(Resource
->SharedWaiters
);
279 if(Resource
->ExclusiveWaiters
) ExFreePool(Resource
->ExclusiveWaiters
);
283 ERESOURCE_THREAD
ExGetCurrentResourceThread()
285 return ((ERESOURCE_THREAD
)PsGetCurrentThread() );
288 ULONG
ExGetSharedWaiterCount(PERESOURCE Resource
)
290 return Resource
->NumberOfSharedWaiters
;
293 NTSTATUS
ExInitializeResource(PERESOURCE Resource
)
295 return ExInitializeResourceLite(Resource
);
298 NTSTATUS
ExInitializeResourceLite(PERESOURCE Resource
)
300 memset(Resource
,0,sizeof(ERESOURCE
));
301 // Resource->NumberOfSharedWaiters = 0;
302 // Resource->NumberOfExclusiveWaiters = 0;
303 KeInitializeSpinLock(&Resource
->SpinLock
);
305 // FIXME : I'm not sure it's a good idea to allocate and initialize
306 // immediately Waiters.
307 Resource
->ExclusiveWaiters
=ExAllocatePool(NonPagedPool
, sizeof(KEVENT
));
308 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,
310 Resource
->SharedWaiters
=ExAllocatePool(NonPagedPool
,sizeof(KSEMAPHORE
));
311 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
312 // Resource->ActiveCount = 0;
316 BOOLEAN
ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource
)
318 return((Resource
->Flag
& ResourceOwnedExclusive
)
319 && Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread());
322 /* note : this function is defined USHORT in nt4, but ULONG in nt5
323 * ULONG is more logical, since return value is number of times the resource
324 * is acquired by the caller, and this value is defined ULONG in
327 ULONG
ExIsResourceAcquiredSharedLite(PERESOURCE Resource
)
330 if(Resource
->OwnerThreads
[0].OwnerThread
==ExGetCurrentResourceThread())
331 return Resource
->OwnerThreads
[0].a
.OwnerCount
;
332 if(Resource
->OwnerThreads
[1].OwnerThread
==ExGetCurrentResourceThread())
333 return Resource
->OwnerThreads
[1].a
.OwnerCount
;
334 if(!Resource
->OwnerThreads
[1].a
.TableSize
) return 0;
335 for(i
=0;i
<Resource
->OwnerThreads
[1].a
.TableSize
;i
++)
337 if(Resource
->OwnerTable
[i
].OwnerThread
==ExGetCurrentResourceThread())
338 return Resource
->OwnerTable
[i
].a
.OwnerCount
;
343 VOID
ExReinitializeResourceLite(PERESOURCE Resource
)
345 Resource
->NumberOfSharedWaiters
= 0;
346 Resource
->NumberOfExclusiveWaiters
= 0;
347 KeInitializeSpinLock(&Resource
->SpinLock
);
349 KeInitializeEvent(Resource
->ExclusiveWaiters
,SynchronizationEvent
,
351 KeInitializeSemaphore(Resource
->SharedWaiters
,0,0x7fffffff);
352 Resource
->ActiveCount
= 0;
353 if(Resource
->OwnerTable
) ExFreePool(Resource
->OwnerTable
);
354 Resource
->OwnerThreads
[0].OwnerThread
=0;
355 Resource
->OwnerThreads
[0].a
.OwnerCount
=0;
356 Resource
->OwnerThreads
[1].OwnerThread
=0;
357 Resource
->OwnerThreads
[1].a
.OwnerCount
=0;
360 VOID
ExReleaseResourceLite(PERESOURCE Resource
)
362 return ExReleaseResourceForThreadLite(Resource
,ExGetCurrentResourceThread());
365 VOID
ExReleaseResource(PERESOURCE Resource
)
367 return ExReleaseResourceForThreadLite(Resource
,ExGetCurrentResourceThread());
370 VOID
ExReleaseResourceForThread(PERESOURCE Resource
,
371 ERESOURCE_THREAD ResourceThreadId
)
373 return ExReleaseResourceForThreadLite(Resource
,ResourceThreadId
);
376 VOID
ExReleaseResourceForThreadLite(PERESOURCE Resource
,
377 ERESOURCE_THREAD ResourceThreadId
)
380 //FIXME : protect this function from interrupts
381 // perhaps with KeRaiseIrql(SYNCH_LEVEL);
382 if (Resource
->Flag
& ResourceOwnedExclusive
)
384 if(--Resource
->OwnerThreads
[0].a
.OwnerCount
== 0)
385 {//release the resource
386 Resource
->OwnerThreads
[0].OwnerThread
=0;
387 assert(--Resource
->ActiveCount
== 0);
388 if(Resource
->NumberOfExclusiveWaiters
)
389 {// get resource to first exclusive waiter
390 KeDispatcherObjectWake(&Resource
->ExclusiveWaiters
->Header
);
394 Resource
->Flag
&=(~ResourceOwnedExclusive
);
395 if(Resource
->NumberOfSharedWaiters
)
396 {// get resource to waiters
397 Resource
->ActiveCount
=Resource
->NumberOfSharedWaiters
;
398 Resource
->NumberOfSharedWaiters
=0;
399 KeReleaseSemaphore(Resource
->SharedWaiters
,0,Resource
->ActiveCount
,0);
405 //search the entry for this thread
406 if(Resource
->OwnerThreads
[1].OwnerThread
==ResourceThreadId
)
408 if( --Resource
->OwnerThreads
[1].a
.OwnerCount
== 0)
410 Resource
->OwnerThreads
[1].OwnerThread
=0;
411 if( --Resource
->ActiveCount
==0) //normally always true
413 if(Resource
->NumberOfExclusiveWaiters
)
414 {// get resource to first exclusive waiter
415 KeDispatcherObjectWake(&Resource
->ExclusiveWaiters
->Header
);
421 if(Resource
->OwnerThreads
[1].OwnerThread
) return ;
422 for(i
=0;i
<Resource
->OwnerThreads
[1].a
.TableSize
;i
++)
424 if(Resource
->OwnerTable
[i
].OwnerThread
==ResourceThreadId
)
426 if( --Resource
->OwnerTable
[1].a
.OwnerCount
== 0)
428 Resource
->OwnerTable
[i
].OwnerThread
=0;
429 if( --Resource
->ActiveCount
==0)
431 ExFreePool(Resource
->OwnerTable
);
432 Resource
->OwnerTable
=NULL
;
433 Resource
->OwnerThreads
[1].a
.TableSize
=0;
434 if(Resource
->NumberOfExclusiveWaiters
)
435 {// get resource to first exclusive waiter
436 KeDispatcherObjectWake(&Resource
->ExclusiveWaiters
->Header
);
445 BOOLEAN
ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource
)
447 return ExAcquireResourceExclusiveLite(Resource
,FALSE
);