Corrected additional object manager issues pointed by Philip Susi
[reactos.git] / reactos / ntoskrnl / ke / wait.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS project
4 * FILE: ntoskrnl/ke/wait.c
5 * PURPOSE: Manages non-busy waiting
6 * PROGRAMMER: David Welch (welch@mcmail.com)
7 * REVISION HISTORY:
8 * 21/07/98: Created
9 * 12/1/99: Phillip Susi: Fixed wake code in KeDispatcherObjectWake
10 * so that things like KeWaitForXXX() return the correct value
11 */
12
13 /* NOTES ********************************************************************
14 *
15 */
16
17 /* INCLUDES ******************************************************************/
18
19 #include <ddk/ntddk.h>
20 #include <internal/ke.h>
21 #include <internal/ps.h>
22
23 #define NDEBUG
24 #include <internal/debug.h>
25
26 /* GLOBALS ******************************************************************/
27
28 static KSPIN_LOCK DispatcherDatabaseLock;
29 static BOOLEAN WaitSet = FALSE;
30 static KIRQL oldlvl = PASSIVE_LEVEL;
31 static PKTHREAD Owner = NULL;
32
33 /* FUNCTIONS *****************************************************************/
34
35 VOID KeInitializeDispatcherHeader(DISPATCHER_HEADER* Header,
36 ULONG Type,
37 ULONG Size,
38 ULONG SignalState)
39 {
40 Header->Type = Type;
41 Header->Absolute = 0;
42 Header->Inserted = 0;
43 Header->Size = Size;
44 Header->SignalState = SignalState;
45 InitializeListHead(&(Header->WaitListHead));
46 }
47
48 VOID KeAcquireDispatcherDatabaseLock(BOOLEAN Wait)
49 /*
50 * PURPOSE: Acquires the dispatcher database lock for the caller
51 */
52 {
53 DPRINT("KeAcquireDispatcherDatabaseLock(Wait %x)\n",Wait);
54 if (WaitSet && Owner == KeGetCurrentThread())
55 {
56 return;
57 }
58 KeAcquireSpinLock(&DispatcherDatabaseLock,&oldlvl);
59 WaitSet = Wait;
60 Owner = KeGetCurrentThread();
61 }
62
63 VOID KeReleaseDispatcherDatabaseLock(BOOLEAN Wait)
64 {
65 DPRINT("KeReleaseDispatcherDatabaseLock(Wait %x)\n",Wait);
66 assert(Wait==WaitSet);
67 if (!Wait)
68 {
69 Owner = NULL;
70 KeReleaseSpinLock(&DispatcherDatabaseLock, oldlvl);
71 }
72 }
73
74 VOID KiSideEffectsBeforeWake(DISPATCHER_HEADER* hdr)
75 /*
76 * FUNCTION: Perform side effects on object before a wait for a thread is
77 * satisfied
78 */
79 {
80 switch (hdr->Type)
81 {
82 case InternalSynchronizationEvent:
83 hdr->SignalState = FALSE;
84 break;
85
86 case InternalSemaphoreType:
87 hdr->SignalState--;
88 break;
89
90 case InternalProcessType:
91 break;
92
93 case InternalThreadType:
94 break;
95
96 case InternalNotificationEvent:
97 break;
98
99 case InternalSynchronizationTimer:
100 hdr->SignalState = FALSE;
101 break;
102
103 case InternalNotificationTimer:
104 break;
105
106 default:
107 DbgPrint("(%s:%d) Dispatcher object %x has unknown type\n",
108 __FILE__,__LINE__,hdr);
109 KeBugCheck(0);
110 }
111
112 }
113
114 static BOOLEAN KiIsObjectSignalled(DISPATCHER_HEADER* hdr)
115 {
116 if (hdr->SignalState <= 0)
117 {
118 return(FALSE);
119 }
120 KiSideEffectsBeforeWake(hdr);
121 return(TRUE);
122 }
123
124 VOID KeRemoveAllWaitsThread(PETHREAD Thread, NTSTATUS WaitStatus)
125 {
126 PKWAIT_BLOCK WaitBlock;
127 BOOLEAN WasWaiting = FALSE;
128
129 KeAcquireDispatcherDatabaseLock(FALSE);
130
131 WaitBlock = (PKWAIT_BLOCK)Thread->Tcb.WaitBlockList;
132 if (WaitBlock != NULL)
133 {
134 WasWaiting = TRUE;
135 }
136 while (WaitBlock != NULL)
137 {
138 RemoveEntryList(&WaitBlock->WaitListEntry);
139 WaitBlock = WaitBlock->NextWaitBlock;
140 }
141 Thread->Tcb.WaitBlockList = NULL;
142
143 if (WasWaiting)
144 {
145 PsUnfreezeThread(Thread, &WaitStatus);
146 }
147
148 KeReleaseDispatcherDatabaseLock(FALSE);
149 }
150
151 static BOOLEAN KeDispatcherObjectWakeAll(DISPATCHER_HEADER* hdr)
152 {
153 PKWAIT_BLOCK current;
154 PLIST_ENTRY current_entry;
155 PKWAIT_BLOCK PrevBlock;
156 NTSTATUS Status;
157
158 DPRINT("KeDispatcherObjectWakeAll(hdr %x)\n",hdr);
159
160 if (IsListEmpty(&hdr->WaitListHead))
161 {
162 return(FALSE);
163 }
164
165 while (!IsListEmpty(&(hdr->WaitListHead)))
166 {
167 current_entry = RemoveHeadList(&hdr->WaitListHead);
168 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
169 WaitListEntry);
170 DPRINT("Waking %x\n",current->Thread);
171 if (current->WaitType == WaitAny)
172 {
173 DPRINT("WaitAny: Remove all wait blocks.\n");
174 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
175 if( PrevBlock != current )
176 RemoveEntryList( &(PrevBlock->WaitListEntry) );
177 current->Thread->WaitBlockList = 0;
178 }
179 else
180 {
181 DPRINT("WaitAll: Remove the current wait block only.\n");
182
183 PrevBlock = current->Thread->WaitBlockList;
184 if (PrevBlock == current)
185 {
186 DPRINT( "WaitAll: Current block is list head.\n" );
187 current->Thread->WaitBlockList = current->NextWaitBlock;
188 }
189 else
190 {
191 DPRINT( "WaitAll: Current block is not list head.\n" );
192 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
193 {
194 PrevBlock = PrevBlock->NextWaitBlock;
195 }
196 if (PrevBlock)
197 {
198 PrevBlock->NextWaitBlock = current->NextWaitBlock;
199 }
200 }
201 }
202 KiSideEffectsBeforeWake(hdr);
203 Status = current->WaitKey;
204 if( current->Thread->WaitBlockList == NULL )
205 PsUnfreezeThread( CONTAINING_RECORD( current->Thread,ETHREAD,Tcb ), &Status );
206 }
207 return(TRUE);
208 }
209
210 static BOOLEAN KeDispatcherObjectWakeOne(DISPATCHER_HEADER* hdr)
211 {
212 PKWAIT_BLOCK current;
213 PLIST_ENTRY current_entry;
214 PKWAIT_BLOCK PrevBlock;
215 NTSTATUS Status;
216
217 DPRINT("KeDispatcherObjectWakeOn(hdr %x)\n",hdr);
218 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
219 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
220 if (IsListEmpty(&(hdr->WaitListHead)))
221 {
222 return(FALSE);
223 }
224 current_entry = RemoveHeadList(&(hdr->WaitListHead));
225 current = CONTAINING_RECORD(current_entry,KWAIT_BLOCK,
226 WaitListEntry);
227 DPRINT("current_entry %x current %x\n",current_entry,current);
228
229 if (current->WaitType == WaitAny)
230 {
231 DPRINT("WaitAny: Remove all wait blocks.\n");
232 for( PrevBlock = current->Thread->WaitBlockList; PrevBlock; PrevBlock = PrevBlock->NextWaitBlock )
233 if( PrevBlock != current )
234 RemoveEntryList( &(PrevBlock->WaitListEntry) );
235 current->Thread->WaitBlockList = 0;
236 }
237 else
238 {
239 DPRINT("WaitAll: Remove the current wait block only.\n");
240
241 PrevBlock = current->Thread->WaitBlockList;
242 if (PrevBlock == current)
243 {
244 DPRINT( "WaitAll: Current block is list head.\n" );
245 current->Thread->WaitBlockList = current->NextWaitBlock;
246 }
247 else
248 {
249 DPRINT( "WaitAll: Current block is not list head.\n" );
250 while ( PrevBlock && PrevBlock->NextWaitBlock != current)
251 {
252 PrevBlock = PrevBlock->NextWaitBlock;
253 }
254 if (PrevBlock)
255 {
256 PrevBlock->NextWaitBlock = current->NextWaitBlock;
257 }
258 }
259 }
260
261 DPRINT("Waking %x\n",current->Thread);
262 KiSideEffectsBeforeWake(hdr);
263 Status = current->WaitKey;
264 PsUnfreezeThread( CONTAINING_RECORD( current->Thread, ETHREAD, Tcb ), &Status );
265 return(TRUE);
266 }
267
268 BOOLEAN KeDispatcherObjectWake(DISPATCHER_HEADER* hdr)
269 /*
270 * FUNCTION: Wake threads waiting on a dispatcher object
271 * NOTE: The exact semantics of waking are dependant on the type of object
272 */
273 {
274 BOOL Ret;
275
276 DPRINT("Entering KeDispatcherObjectWake(hdr %x)\n",hdr);
277 // DPRINT("hdr->WaitListHead %x hdr->WaitListHead.Flink %x\n",
278 // &hdr->WaitListHead,hdr->WaitListHead.Flink);
279 DPRINT("hdr->Type %x\n",hdr->Type);
280 switch (hdr->Type)
281 {
282 case InternalNotificationEvent:
283 return(KeDispatcherObjectWakeAll(hdr));
284
285 case InternalNotificationTimer:
286 return(KeDispatcherObjectWakeAll(hdr));
287
288 case InternalSynchronizationEvent:
289 return(KeDispatcherObjectWakeOne(hdr));
290
291
292 case InternalSynchronizationTimer:
293 return(KeDispatcherObjectWakeOne(hdr));
294
295 case InternalSemaphoreType:
296 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
297 if(hdr->SignalState>0)
298 {
299 do
300 {
301 DPRINT("Waking one semaphore waiter\n");
302 Ret = KeDispatcherObjectWakeOne(hdr);
303 } while(hdr->SignalState > 0 && Ret) ;
304 return(Ret);
305 }
306 else return FALSE;
307
308 case InternalProcessType:
309 return(KeDispatcherObjectWakeAll(hdr));
310
311 case InternalThreadType:
312 return(KeDispatcherObjectWakeAll(hdr));
313
314 case InternalMutexType:
315 return(KeDispatcherObjectWakeOne(hdr));
316 }
317 DbgPrint("Dispatcher object %x has unknown type %d\n", hdr, hdr->Type);
318 KeBugCheck(0);
319 return(FALSE);
320 }
321
322
323 NTSTATUS KeWaitForSingleObject(PVOID Object,
324 KWAIT_REASON WaitReason,
325 KPROCESSOR_MODE WaitMode,
326 BOOLEAN Alertable,
327 PLARGE_INTEGER Timeout)
328 /*
329 * FUNCTION: Puts the current thread into a wait state until the
330 * given dispatcher object is set to signalled
331 * ARGUMENTS:
332 * Object = Object to wait on
333 * WaitReason = Reason for the wait (debugging aid)
334 * WaitMode = Can be KernelMode or UserMode, if UserMode then
335 * user-mode APCs can be delivered and the thread's
336 * stack can be paged out
337 * Altertable = Specifies if the wait is a alertable
338 * Timeout = Optional timeout value
339 * RETURNS: Status
340 */
341 {
342 DISPATCHER_HEADER* hdr = (DISPATCHER_HEADER *)Object;
343 PKTHREAD CurrentThread;
344 NTSTATUS Status;
345
346 DPRINT("Entering KeWaitForSingleObject(Object %x) "
347 "PsGetCurrentThread() %x\n",Object,PsGetCurrentThread());
348
349 CurrentThread = KeGetCurrentThread();
350
351 if (Alertable && !IsListEmpty(&CurrentThread->ApcState.ApcListHead[1]))
352 {
353 DPRINT("Thread is alertable and user APCs are pending\n");
354 return(STATUS_USER_APC);
355 }
356
357 if (Timeout != NULL)
358 {
359 KeAddThreadTimeout(CurrentThread,Timeout);
360 }
361
362 do
363 {
364 KeAcquireDispatcherDatabaseLock(FALSE);
365
366 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
367
368 if (KiIsObjectSignalled(hdr))
369 {
370 KeReleaseDispatcherDatabaseLock(FALSE);
371 if (Timeout != NULL)
372 {
373 KeCancelTimer(&KeGetCurrentThread()->Timer);
374 }
375 return(STATUS_WAIT_0);
376 }
377
378 /* Append wait block to the KTHREAD wait block list */
379 CurrentThread->WaitBlockList = &CurrentThread->WaitBlock[0];
380
381 CurrentThread->WaitBlock[0].Object = Object;
382 CurrentThread->WaitBlock[0].Thread = CurrentThread;
383 CurrentThread->WaitBlock[0].WaitKey = 0;
384 CurrentThread->WaitBlock[0].WaitType = WaitAny;
385 CurrentThread->WaitBlock[0].NextWaitBlock = NULL;
386 DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x CurrentThread->WaitBlock[0].WaitListEntry = %x\n",
387 hdr->WaitListHead.Flink,hdr->WaitListHead.Blink, CurrentThread->WaitBlock[0].WaitListEntry );
388 InsertTailList(&hdr->WaitListHead, &CurrentThread->WaitBlock[0].WaitListEntry);
389 KeReleaseDispatcherDatabaseLock(FALSE);
390 DPRINT("Waiting for %x with irql %d\n", Object, KeGetCurrentIrql());
391 PsFreezeThread(PsGetCurrentThread(),
392 &Status,
393 (UCHAR)Alertable,
394 WaitMode);
395 DPRINT("Woke from wait\n");
396 } while (Status == STATUS_KERNEL_APC);
397
398 if (Timeout != NULL)
399 {
400 KeCancelTimer(&KeGetCurrentThread()->Timer);
401 }
402
403 DPRINT("Returning from KeWaitForSingleObject()\n");
404 return Status;
405 }
406
407
408 NTSTATUS KeWaitForMultipleObjects(ULONG Count,
409 PVOID Object[],
410 WAIT_TYPE WaitType,
411 KWAIT_REASON WaitReason,
412 KPROCESSOR_MODE WaitMode,
413 BOOLEAN Alertable,
414 PLARGE_INTEGER Timeout,
415 PKWAIT_BLOCK WaitBlockArray)
416 {
417 DISPATCHER_HEADER* hdr;
418 PKWAIT_BLOCK blk;
419 PKTHREAD CurrentThread;
420 ULONG CountSignaled;
421 ULONG i;
422 NTSTATUS Status;
423
424 DPRINT("Entering KeWaitForMultipleObjects(Count %lu Object[] %p) "
425 "PsGetCurrentThread() %x\n",Count,Object,PsGetCurrentThread());
426
427 CountSignaled = 0;
428 CurrentThread = KeGetCurrentThread();
429
430 if (WaitBlockArray == NULL)
431 {
432 if (Count > 4)
433 {
434 DbgPrint("(%s:%d) Too many objects!\n",
435 __FILE__,__LINE__);
436 return STATUS_UNSUCCESSFUL;
437 }
438 blk = &CurrentThread->WaitBlock[0];
439 }
440 else
441 {
442 if (Count > 64)
443 {
444 DbgPrint("(%s:%d) Too many objects!\n",
445 __FILE__,__LINE__);
446 return STATUS_UNSUCCESSFUL;
447 }
448 blk = WaitBlockArray;
449 }
450
451 KeAcquireDispatcherDatabaseLock(FALSE);
452
453 for (i = 0; i < Count; i++)
454 {
455 hdr = (DISPATCHER_HEADER *)Object[i];
456
457 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
458
459 if (KiIsObjectSignalled(hdr))
460 {
461 CountSignaled++;
462
463 if (WaitType == WaitAny)
464 {
465 KeReleaseDispatcherDatabaseLock(FALSE);
466 DPRINT("One object is already signaled!\n");
467 return(STATUS_WAIT_0 + i);
468 }
469 }
470 }
471
472 if ((WaitType == WaitAll) && (CountSignaled == Count))
473 {
474 KeReleaseDispatcherDatabaseLock(FALSE);
475 DPRINT("All objects are already signaled!\n");
476 return(STATUS_WAIT_0);
477 }
478
479 if (Timeout != NULL)
480 {
481 KeAddThreadTimeout(CurrentThread,Timeout);
482 }
483
484 /* Append wait block to the KTHREAD wait block list */
485 CurrentThread->WaitBlockList = blk;
486
487 for (i = 0; i < Count; i++)
488 {
489 hdr = (DISPATCHER_HEADER *)Object[i];
490
491 DPRINT("hdr->SignalState %d\n", hdr->SignalState);
492
493 blk->Object = Object[i];
494 blk->Thread = CurrentThread;
495 blk->WaitKey = i;
496 blk->WaitType = WaitType;
497 if (i == Count - 1)
498 blk->NextWaitBlock = NULL;
499 else
500 blk->NextWaitBlock = blk + 1;
501 DPRINT("blk %p blk->NextWaitBlock %p\n",
502 blk, blk->NextWaitBlock);
503 InsertTailList(&(hdr->WaitListHead),&(blk->WaitListEntry));
504 // DPRINT("hdr->WaitListHead.Flink %x hdr->WaitListHead.Blink %x\n",
505 // hdr->WaitListHead.Flink,hdr->WaitListHead.Blink);
506
507 blk = blk->NextWaitBlock;
508 }
509
510 KeReleaseDispatcherDatabaseLock(FALSE);
511
512 DPRINT("Waiting at %s:%d with irql %d\n", __FILE__, __LINE__,
513 KeGetCurrentIrql());
514 PsFreezeThread(PsGetCurrentThread(),
515 &Status,
516 Alertable,
517 WaitMode);
518
519 if (Timeout != NULL)
520 KeCancelTimer(&KeGetCurrentThread()->Timer);
521 DPRINT("Returning from KeWaitForMultipleObjects()\n");
522 return(Status);
523 }
524
525 VOID KeInitializeDispatcher(VOID)
526 {
527 KeInitializeSpinLock(&DispatcherDatabaseLock);
528 }
529
530 NTSTATUS STDCALL NtWaitForMultipleObjects(IN ULONG Count,
531 IN HANDLE Object [],
532 IN CINT WaitType,
533 IN BOOLEAN Alertable,
534 IN PLARGE_INTEGER Time)
535 {
536 KWAIT_BLOCK WaitBlockArray[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
537 PVOID ObjectPtrArray[64]; /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
538 NTSTATUS Status;
539 ULONG i, j;
540
541 DPRINT("NtWaitForMultipleObjects(Count %lu Object[] %x, Alertable %d, Time %x)\n",
542 Count,Object,Alertable,Time);
543
544 if (Count > 64) /* FIXME: use MAXIMUM_WAIT_OBJECTS instead */
545 return STATUS_UNSUCCESSFUL;
546
547 /* reference all objects */
548 for (i = 0; i < Count; i++)
549 {
550 Status = ObReferenceObjectByHandle(Object[i],
551 SYNCHRONIZE,
552 NULL,
553 UserMode,
554 &ObjectPtrArray[i],
555 NULL);
556 if (Status != STATUS_SUCCESS)
557 {
558 /* dereference all referenced objects */
559 for (j = 0; j < i; i++)
560 {
561 ObDereferenceObject(ObjectPtrArray[j]);
562 }
563
564 return(Status);
565 }
566 }
567
568 Status = KeWaitForMultipleObjects(Count,
569 ObjectPtrArray,
570 WaitType,
571 UserRequest,
572 UserMode,
573 Alertable,
574 Time,
575 WaitBlockArray);
576
577 /* dereference all objects */
578 for (i = 0; i < Count; i++)
579 {
580 ObDereferenceObject(ObjectPtrArray[i]);
581 }
582
583 return(Status);
584 }
585
586
587 NTSTATUS STDCALL NtWaitForSingleObject (IN HANDLE Object,
588 IN BOOLEAN Alertable,
589 IN PLARGE_INTEGER Time)
590 {
591 PVOID ObjectPtr;
592 NTSTATUS Status;
593
594 DPRINT("NtWaitForSingleObject(Object %x, Alertable %d, Time %x)\n",
595 Object,Alertable,Time);
596
597 Status = ObReferenceObjectByHandle(Object,
598 SYNCHRONIZE,
599 NULL,
600 UserMode,
601 &ObjectPtr,
602 NULL);
603 if (Status != STATUS_SUCCESS)
604 {
605 return(Status);
606 }
607
608 Status = KeWaitForSingleObject(ObjectPtr,
609 UserMode,
610 UserMode,
611 Alertable,
612 Time);
613
614 ObDereferenceObject(ObjectPtr);
615
616 return(Status);
617 }
618
619
620 NTSTATUS STDCALL NtSignalAndWaitForSingleObject (IN HANDLE EventHandle,
621 IN BOOLEAN Alertable,
622 IN PLARGE_INTEGER Time,
623 PULONG
624 NumberOfWaitingThreads OPTIONAL)
625 {
626 UNIMPLEMENTED;
627 }