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