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