530047e26b4dbc3613058c55934913e4dbfff11a
[reactos.git] / reactos / ntoskrnl / ex / resource.c
1 /*
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)
7 * UPDATE HISTORY:
8 * Created 22/05/98
9 */
10
11
12 /*
13 * Usage of ERESOURCE members is not documented.
14 * From names of members and functionnalities, we can assume :
15 *
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
26 * else
27 * OwnerThread=0
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.
31 *
32 */
33
34 #define ResourceOwnedExclusive 0x80
35
36 /* INCLUDES *****************************************************************/
37
38 #include <ddk/ntddk.h>
39 #include <internal/ke.h>
40 #include <string.h>
41 #include <internal/string.h>
42
43 #define NDEBUG
44 #include <internal/debug.h>
45
46 /* FUNCTIONS *****************************************************************/
47
48 BOOLEAN ExAcquireResourceExclusive(PERESOURCE Resource, BOOLEAN Wait)
49 {
50 return ExAcquireResourceExclusiveLite(Resource,Wait);
51 }
52
53 BOOLEAN ExAcquireResourceExclusiveLite(PERESOURCE Resource, BOOLEAN Wait)
54 {
55 // FIXME : protect essential tasks of this function from interrupts
56 // perhaps with KeRaiseIrql(SYNCH_LEVEL);
57 if(Resource->ActiveCount) // resource already locked
58 {
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++;
63 return TRUE;
64 }
65 if( ! Wait) return FALSE;
66 Resource->NumberOfExclusiveWaiters++;
67 if(KeWaitForSingleObject(Resource->ExclusiveWaiters,Executive,KernelMode,FALSE,NULL)
68 ==STATUS_TIMEOUT)
69 {
70 //FIXME : what to do if timeout ?
71 Resource->NumberOfExclusiveWaiters--;
72 return FALSE;
73 }
74 Resource->NumberOfExclusiveWaiters--;
75 }
76 Resource->OwnerThreads[0].a.OwnerCount=1;
77 Resource->Flag |= ResourceOwnedExclusive;
78 Resource->ActiveCount=1;
79 Resource->OwnerThreads[0].OwnerThread=ExGetCurrentResourceThread();
80 return TRUE;
81 }
82
83 BOOLEAN ExAcquireResourceSharedLite(PERESOURCE Resource, BOOLEAN Wait)
84 {
85 POWNER_ENTRY freeEntry;
86 unsigned long i;
87 // FIXME : protect from interrupts
88 // first, resolve trivial cases
89 if(Resource->ActiveCount==0) // no owner, it's easy
90 {
91 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
92 Resource->OwnerThreads[1].a.OwnerCount=1;
93 Resource->ActiveCount=1;
94 return TRUE;
95 }
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++;
100 return TRUE;
101 }
102 if((Resource->Flag & ResourceOwnedExclusive)
103 || Resource->NumberOfExclusiveWaiters)
104 { //exclusive by another thread , or thread waiting for exclusive
105 if(!Wait) return FALSE;
106 else
107 {
108 Resource->NumberOfSharedWaiters++;
109 // wait for the semaphore
110 if(KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0)==STATUS_TIMEOUT)
111 {
112 //FIXME : what to do ?
113 return FALSE;
114 }
115 }
116 }
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)
120 {
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;
130 return TRUE;
131 }
132 freeEntry=NULL;
133 for(i=0;i<Resource->OwnerThreads[1].a.TableSize;i++)
134 {
135 if(Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
136 {// old entry for this thread found
137 Resource->OwnerTable[i].a.OwnerCount++;
138 return TRUE;
139 }
140 if(Resource->OwnerTable[i].OwnerThread==0)
141 freeEntry = &Resource->OwnerTable[i];
142 }
143 if(! freeEntry)
144 {
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 ++;
154 }
155 freeEntry->OwnerThread=ExGetCurrentResourceThread();
156 freeEntry->a.OwnerCount=1;
157 Resource->ActiveCount++;
158 return TRUE;
159 }
160
161 VOID ExConvertExclusiveToSharedLite(PERESOURCE Resource)
162 {
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);
178 }
179
180 ULONG ExGetExclusiveWaiterCount(PERESOURCE Resource)
181 {
182 return Resource->NumberOfExclusiveWaiters;
183 }
184
185 BOOLEAN ExAcquireSharedStarveExclusive(PERESOURCE Resource, BOOLEAN Wait)
186 {
187 POWNER_ENTRY freeEntry;
188 unsigned long i;
189 // FIXME : protect from interrupts
190 // first, resolve trivial cases
191 if(Resource->ActiveCount==0) // no owner, it's easy
192 {
193 Resource->OwnerThreads[1].OwnerThread=ExGetCurrentResourceThread();
194 Resource->OwnerThreads[1].a.OwnerCount=1;
195 Resource->ActiveCount=1;
196 return TRUE;
197 }
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++;
202 return TRUE;
203 }
204 if(Resource->Flag & ResourceOwnedExclusive)
205 { //exclusive by another thread , or thread waiting for exclusive
206 if(!Wait) return FALSE;
207 else
208 {
209 Resource->NumberOfSharedWaiters++;
210 // wait for the semaphore
211 if(KeWaitForSingleObject(Resource->SharedWaiters,0,0,0,0)==STATUS_TIMEOUT)
212 {
213 //FIXME : what to do ?
214 return FALSE;
215 }
216 }
217 }
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)
221 {
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;
231 return TRUE;
232 }
233 freeEntry=NULL;
234 for(i=0;i<Resource->OwnerThreads[1].a.TableSize;i++)
235 {
236 if(Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
237 {// old entry for this thread found
238 Resource->OwnerTable[i].a.OwnerCount++;
239 return TRUE;
240 }
241 if(Resource->OwnerTable[i].OwnerThread==0)
242 freeEntry = &Resource->OwnerTable[i];
243 }
244 if(! freeEntry)
245 {
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 ++;
255 }
256 freeEntry->OwnerThread=ExGetCurrentResourceThread();
257 freeEntry->a.OwnerCount=1;
258 Resource->ActiveCount++;
259 return TRUE;
260 }
261
262 BOOLEAN ExAcquireSharedWaitForExclusive(PERESOURCE Resource, BOOLEAN Wait)
263 {
264 return ExAcquireResourceSharedLite(Resource,Wait);
265 }
266
267 NTSTATUS ExDeleteResource(PERESOURCE Resource)
268 {
269 if(Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
270 if(Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
271 if(Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
272 return 0;
273 }
274
275 NTSTATUS ExDeleteResourceLite(PERESOURCE Resource)
276 {
277 if(Resource->OwnerTable) ExFreePool(Resource->OwnerTable);
278 if(Resource->SharedWaiters) ExFreePool(Resource->SharedWaiters);
279 if(Resource->ExclusiveWaiters) ExFreePool(Resource->ExclusiveWaiters);
280 return 0;
281 }
282
283 ERESOURCE_THREAD ExGetCurrentResourceThread()
284 {
285 return ((ERESOURCE_THREAD)PsGetCurrentThread() );
286 }
287
288 ULONG ExGetSharedWaiterCount(PERESOURCE Resource)
289 {
290 return Resource->NumberOfSharedWaiters;
291 }
292
293 NTSTATUS ExInitializeResource(PERESOURCE Resource)
294 {
295 return ExInitializeResourceLite(Resource);
296 }
297
298 NTSTATUS ExInitializeResourceLite(PERESOURCE Resource)
299 {
300 memset(Resource,0,sizeof(ERESOURCE));
301 // Resource->NumberOfSharedWaiters = 0;
302 // Resource->NumberOfExclusiveWaiters = 0;
303 KeInitializeSpinLock(&Resource->SpinLock);
304 // Resource->Flag=0;
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,
309 FALSE);
310 Resource->SharedWaiters=ExAllocatePool(NonPagedPool ,sizeof(KSEMAPHORE));
311 KeInitializeSemaphore(Resource->SharedWaiters,0,0x7fffffff);
312 // Resource->ActiveCount = 0;
313 return 0;
314 }
315
316 BOOLEAN ExIsResourceAcquiredExclusiveLite(PERESOURCE Resource)
317 {
318 return((Resource->Flag & ResourceOwnedExclusive)
319 && Resource->OwnerThreads[0].OwnerThread==ExGetCurrentResourceThread());
320 }
321
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
325 * PERESOURCE struct
326 */
327 ULONG ExIsResourceAcquiredSharedLite(PERESOURCE Resource)
328 {
329 long i;
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++)
336 {
337 if(Resource->OwnerTable[i].OwnerThread==ExGetCurrentResourceThread())
338 return Resource->OwnerTable[i].a.OwnerCount;
339 }
340 return 0;
341 }
342
343 VOID ExReinitializeResourceLite(PERESOURCE Resource)
344 {
345 Resource->NumberOfSharedWaiters = 0;
346 Resource->NumberOfExclusiveWaiters = 0;
347 KeInitializeSpinLock(&Resource->SpinLock);
348 Resource->Flag=0;
349 KeInitializeEvent(Resource->ExclusiveWaiters,SynchronizationEvent,
350 FALSE);
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;
358 }
359
360 VOID ExReleaseResourceLite(PERESOURCE Resource)
361 {
362 return ExReleaseResourceForThreadLite(Resource,ExGetCurrentResourceThread());
363 }
364
365 VOID ExReleaseResource(PERESOURCE Resource)
366 {
367 return ExReleaseResourceForThreadLite(Resource,ExGetCurrentResourceThread());
368 }
369
370 VOID ExReleaseResourceForThread(PERESOURCE Resource,
371 ERESOURCE_THREAD ResourceThreadId)
372 {
373 return ExReleaseResourceForThreadLite(Resource,ResourceThreadId);
374 }
375
376 VOID ExReleaseResourceForThreadLite(PERESOURCE Resource,
377 ERESOURCE_THREAD ResourceThreadId)
378 {
379 long i;
380 //FIXME : protect this function from interrupts
381 // perhaps with KeRaiseIrql(SYNCH_LEVEL);
382 if (Resource->Flag & ResourceOwnedExclusive)
383 {
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);
391 return;
392 }
393 else
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);
400 return;
401 }
402 }
403 return;
404 }
405 //search the entry for this thread
406 if(Resource->OwnerThreads[1].OwnerThread==ResourceThreadId)
407 {
408 if( --Resource->OwnerThreads[1].a.OwnerCount == 0)
409 {
410 Resource->OwnerThreads[1].OwnerThread=0;
411 if( --Resource->ActiveCount ==0) //normally always true
412 {
413 if(Resource->NumberOfExclusiveWaiters)
414 {// get resource to first exclusive waiter
415 KeDispatcherObjectWake(&Resource->ExclusiveWaiters->Header);
416 }
417 }
418 }
419 return;
420 }
421 if(Resource->OwnerThreads[1].OwnerThread) return ;
422 for(i=0;i<Resource->OwnerThreads[1].a.TableSize;i++)
423 {
424 if(Resource->OwnerTable[i].OwnerThread==ResourceThreadId)
425 {
426 if( --Resource->OwnerTable[1].a.OwnerCount == 0)
427 {
428 Resource->OwnerTable[i].OwnerThread=0;
429 if( --Resource->ActiveCount ==0)
430 {
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);
437 }
438 }
439 }
440 return;
441 }
442 }
443 }
444
445 BOOLEAN ExTryToAcquireResourceExclusiveLite(PERESOURCE Resource)
446 {
447 return ExAcquireResourceExclusiveLite(Resource,FALSE);
448 }
449
450