* Sync up to trunk head (r65074).
[reactos.git] / ntoskrnl / ob / obwait.c
1 /*
2 * PROJECT: ReactOS Kernel
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: ntoskrnl/ob/obwait.c
5 * PURPOSE: Handles Waiting on Objects
6 * PROGRAMMERS: Alex Ionescu (alex@relsoft.net)
7 * Thomas Weidenmueller (w3seek@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include <ntoskrnl.h>
13 #define NDEBUG
14 #include <debug.h>
15
16 /* FUNCTIONS *****************************************************************/
17
18 /*++
19 * @name NtWaitForMultipleObjects
20 * @implemented NT4
21 *
22 * The NtWaitForMultipleObjects routine <FILLMEIN>
23 *
24 * @param ObjectCount
25 * <FILLMEIN>
26 *
27 * @param HandleArray
28 * <FILLMEIN>
29 *
30 * @param WaitType
31 * <FILLMEIN>
32 *
33 * @param Alertable
34 * <FILLMEIN>
35 *
36 * @param TimeOut
37 * <FILLMEIN>
38 *
39 * @return STATUS_SUCCESS or appropriate error value.
40 *
41 * @remarks None.
42 *
43 *--*/
44 NTSTATUS
45 NTAPI
46 NtWaitForMultipleObjects(IN ULONG ObjectCount,
47 IN PHANDLE HandleArray,
48 IN WAIT_TYPE WaitType,
49 IN BOOLEAN Alertable,
50 IN PLARGE_INTEGER TimeOut OPTIONAL)
51 {
52 PKWAIT_BLOCK WaitBlockArray = NULL;
53 HANDLE Handles[MAXIMUM_WAIT_OBJECTS], KernelHandle;
54 PVOID Objects[MAXIMUM_WAIT_OBJECTS];
55 PVOID WaitObjects[MAXIMUM_WAIT_OBJECTS];
56 ULONG i = 0, ReferencedObjects = 0, j;
57 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
58 LARGE_INTEGER SafeTimeOut;
59 BOOLEAN LockInUse;
60 PHANDLE_TABLE_ENTRY HandleEntry;
61 POBJECT_HEADER ObjectHeader;
62 PHANDLE_TABLE HandleTable;
63 ACCESS_MASK GrantedAccess;
64 PVOID DefaultObject;
65 NTSTATUS Status;
66 PAGED_CODE();
67
68 /* Enter a critical region since we'll play with handles */
69 LockInUse = TRUE;
70 KeEnterCriticalRegion();
71
72 /* Check for valid Object Count */
73 if ((ObjectCount > MAXIMUM_WAIT_OBJECTS) || !(ObjectCount))
74 {
75 /* Fail */
76 Status = STATUS_INVALID_PARAMETER_1;
77 goto Quickie;
78 }
79
80 /* Check for valid Wait Type */
81 if ((WaitType != WaitAll) && (WaitType != WaitAny))
82 {
83 /* Fail */
84 Status = STATUS_INVALID_PARAMETER_3;
85 goto Quickie;
86 }
87
88 /* Enter SEH */
89 _SEH2_TRY
90 {
91 /* Check if the call came from user mode */
92 if (PreviousMode != KernelMode)
93 {
94 /* Check if we have a timeout */
95 if (TimeOut)
96 {
97 /* Make a local copy of the timeout on the stack */
98 SafeTimeOut = ProbeForReadLargeInteger(TimeOut);
99 TimeOut = &SafeTimeOut;
100 }
101
102 /* Probe all the handles */
103 ProbeForRead(HandleArray,
104 ObjectCount * sizeof(HANDLE),
105 sizeof(HANDLE));
106 }
107
108 /*
109 * Make a copy so we don't have to guard with SEH later and keep
110 * track of what objects we referenced if dereferencing pointers
111 * suddenly fails
112 */
113 RtlCopyMemory(Handles,
114 HandleArray,
115 ObjectCount * sizeof(HANDLE));
116 }
117 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
118 {
119 /* Return the exception code */
120 Status = _SEH2_GetExceptionCode();
121 _SEH2_YIELD(goto Quickie);
122 }
123 _SEH2_END;
124
125 /* Check if we can use the internal Wait Array */
126 if (ObjectCount > THREAD_WAIT_OBJECTS)
127 {
128 /* Allocate from Pool */
129 WaitBlockArray = ExAllocatePoolWithTag(NonPagedPool,
130 ObjectCount *
131 sizeof(KWAIT_BLOCK),
132 TAG_WAIT);
133 if (!WaitBlockArray)
134 {
135 /* Fail */
136 Status = STATUS_INSUFFICIENT_RESOURCES;
137 goto Quickie;
138 }
139 }
140
141 /* Start the loop */
142 do
143 {
144 /* Use the right Executive Handle */
145 if (ObpIsKernelHandle(Handles[i], PreviousMode))
146 {
147 /* Use the System Handle Table and decode */
148 HandleTable = ObpKernelHandleTable;
149 KernelHandle = ObKernelHandleToHandle(Handles[i]);
150
151 /* Get a pointer to it */
152 HandleEntry = ExMapHandleToPointer(HandleTable, KernelHandle);
153 }
154 else
155 {
156 /* Use the Process' Handle table and get the Ex Handle */
157 HandleTable = PsGetCurrentProcess()->ObjectTable;
158
159 /* Get a pointer to it */
160 HandleEntry = ExMapHandleToPointer(HandleTable, Handles[i]);
161 }
162
163 /* Check if we have an entry */
164 if (!HandleEntry)
165 {
166 /* Fail, handle is invalid */
167 Status = STATUS_INVALID_HANDLE;
168 DPRINT1("Invalid handle passed to NtWaitForMultipleObjects\n");
169 goto Quickie;
170 }
171
172 /* Check for synchronize access */
173 GrantedAccess = HandleEntry->GrantedAccess;
174 if ((PreviousMode != KernelMode) && (!(GrantedAccess & SYNCHRONIZE)))
175 {
176 /* Unlock the entry and fail */
177 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
178 DPRINT1("Handle does not have SYNCHRONIZE access\n");
179 Status = STATUS_ACCESS_DENIED;
180 goto Quickie;
181 }
182
183 /* Get the Object Header */
184 ObjectHeader = ObpGetHandleObject(HandleEntry);
185
186 /* Get default Object */
187 DefaultObject = ObjectHeader->Type->DefaultObject;
188
189 /* Check if it's the internal offset */
190 if (IsPointerOffset(DefaultObject))
191 {
192 /* Increase reference count */
193 InterlockedIncrement(&ObjectHeader->PointerCount);
194 ReferencedObjects++;
195
196 /* Save the Object and Wait Object, this is a relative offset */
197 Objects[i] = &ObjectHeader->Body;
198 WaitObjects[i] = (PVOID)((ULONG_PTR)&ObjectHeader->Body +
199 (ULONG_PTR)DefaultObject);
200 }
201 else
202 {
203 /* This is our internal Object */
204 ReferencedObjects++;
205 Objects[i] = NULL;
206 WaitObjects[i] = DefaultObject;
207 }
208
209 /* Unlock the Handle Table Entry */
210 ExUnlockHandleTableEntry(HandleTable, HandleEntry);
211
212 /* Keep looping */
213 i++;
214 } while (i < ObjectCount);
215
216 /* For a Waitall, we can't have the same object more then once */
217 if (WaitType == WaitAll)
218 {
219 /* Clear the main loop variable */
220 i = 0;
221
222 /* Start the loop */
223 do
224 {
225 /* Check the current and forward object */
226 for (j = i + 1; j < ObjectCount; j++)
227 {
228 /* Make sure they don't match */
229 if (WaitObjects[i] == WaitObjects[j])
230 {
231 /* Fail */
232 Status = STATUS_INVALID_PARAMETER_MIX;
233 DPRINT1("Passed a duplicate object to NtWaitForMultipleObjects\n");
234 goto Quickie;
235 }
236 }
237
238 /* Keep looping */
239 i++;
240 } while (i < ObjectCount);
241 }
242
243 /* Now we can finally wait. Use SEH since it can raise an exception */
244 _SEH2_TRY
245 {
246 /* We're done playing with handles */
247 LockInUse = FALSE;
248 KeLeaveCriticalRegion();
249
250 /* Do the kernel wait */
251 Status = KeWaitForMultipleObjects(ObjectCount,
252 WaitObjects,
253 WaitType,
254 UserRequest,
255 PreviousMode,
256 Alertable,
257 TimeOut,
258 WaitBlockArray);
259 }
260 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
261 {
262 /* Get the exception code */
263 Status = _SEH2_GetExceptionCode();
264 }
265 _SEH2_END;
266
267 Quickie:
268 /* First derefence */
269 while (ReferencedObjects)
270 {
271 /* Decrease the number of objects */
272 ReferencedObjects--;
273
274 /* Check if we had a valid object in this position */
275 if (Objects[ReferencedObjects])
276 {
277 /* Dereference it */
278 ObDereferenceObject(Objects[ReferencedObjects]);
279 }
280 }
281
282 /* Free wait block array */
283 if (WaitBlockArray) ExFreePoolWithTag(WaitBlockArray, TAG_WAIT);
284
285 /* Re-enable APCs if needed */
286 if (LockInUse) KeLeaveCriticalRegion();
287
288 /* Return status */
289 return Status;
290 }
291
292 /*++
293 * @name NtWaitForMultipleObjects32
294 * @implemented NT5.1
295 *
296 * The NtWaitForMultipleObjects32 routine <FILLMEIN>
297 *
298 * @param ObjectCount
299 * <FILLMEIN>
300 *
301 * @param HandleArray
302 * <FILLMEIN>
303 *
304 * @param WaitType
305 * <FILLMEIN>
306 *
307 * @param Alertable
308 * <FILLMEIN>
309 *
310 * @param TimeOut
311 * <FILLMEIN>
312 *
313 * @return STATUS_SUCCESS or appropriate error value.
314 *
315 * @remarks None.
316 *
317 *--*/
318 NTSTATUS
319 NTAPI
320 NtWaitForMultipleObjects32(IN ULONG ObjectCount,
321 IN PLONG Handles,
322 IN WAIT_TYPE WaitType,
323 IN BOOLEAN Alertable,
324 IN PLARGE_INTEGER TimeOut OPTIONAL)
325 {
326 /* FIXME WOW64 */
327 return NtWaitForMultipleObjects(ObjectCount,
328 (PHANDLE)Handles,
329 WaitType,
330 Alertable,
331 TimeOut);
332 }
333
334 /*++
335 * @name NtWaitForSingleObject
336 * @implemented NT4
337 *
338 * The NtWaitForSingleObject routine <FILLMEIN>
339 *
340 * @param ObjectHandle
341 * <FILLMEIN>
342 *
343 * @param Alertable
344 * <FILLMEIN>
345 *
346 * @param TimeOut
347 * <FILLMEIN>
348 *
349 * @return STATUS_SUCCESS or appropriate error value.
350 *
351 * @remarks None.
352 *
353 *--*/
354 NTSTATUS
355 NTAPI
356 NtWaitForSingleObject(IN HANDLE ObjectHandle,
357 IN BOOLEAN Alertable,
358 IN PLARGE_INTEGER TimeOut OPTIONAL)
359 {
360 PVOID Object, WaitableObject;
361 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
362 LARGE_INTEGER SafeTimeOut;
363 NTSTATUS Status;
364
365 /* Check if we came with a timeout from user mode */
366 if ((TimeOut) && (PreviousMode != KernelMode))
367 {
368 /* Enter SEH for proving */
369 _SEH2_TRY
370 {
371 /* Make a copy on the stack */
372 SafeTimeOut = ProbeForReadLargeInteger(TimeOut);
373 TimeOut = &SafeTimeOut;
374 }
375 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
376 {
377 /* Return the exception code */
378 _SEH2_YIELD(return _SEH2_GetExceptionCode());
379 }
380 _SEH2_END;
381 }
382
383 /* Get the Object */
384 Status = ObReferenceObjectByHandle(ObjectHandle,
385 SYNCHRONIZE,
386 NULL,
387 PreviousMode,
388 &Object,
389 NULL);
390 if (NT_SUCCESS(Status))
391 {
392 /* Get the Waitable Object */
393 WaitableObject = OBJECT_TO_OBJECT_HEADER(Object)->Type->DefaultObject;
394
395 /* Is it an offset for internal objects? */
396 if (IsPointerOffset(WaitableObject))
397 {
398 /* Turn it into a pointer */
399 WaitableObject = (PVOID)((ULONG_PTR)Object +
400 (ULONG_PTR)WaitableObject);
401 }
402
403 /* SEH this since it can also raise an exception */
404 _SEH2_TRY
405 {
406 /* Ask the kernel to do the wait */
407 Status = KeWaitForSingleObject(WaitableObject,
408 UserRequest,
409 PreviousMode,
410 Alertable,
411 TimeOut);
412 }
413 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
414 {
415 /* Get the exception code */
416 Status = _SEH2_GetExceptionCode();
417 }
418 _SEH2_END;
419
420 /* Dereference the Object */
421 ObDereferenceObject(Object);
422 }
423 else
424 {
425 DPRINT1("Failed to reference the handle with status 0x%x\n", Status);
426 }
427
428 /* Return the status */
429 return Status;
430 }
431
432 /*++
433 * @name NtSignalAndWaitForSingleObject
434 * @implemented NT4
435 *
436 * The NtSignalAndWaitForSingleObject routine <FILLMEIN>
437 *
438 * @param ObjectHandleToSignal
439 * <FILLMEIN>
440 *
441 * @param WaitableObjectHandle
442 * <FILLMEIN>
443 *
444 * @param Alertable
445 * <FILLMEIN>
446 *
447 * @param TimeOut
448 * <FILLMEIN>
449 *
450 * @return STATUS_SUCCESS or appropriate error value.
451 *
452 * @remarks None.
453 *
454 *--*/
455 NTSTATUS
456 NTAPI
457 NtSignalAndWaitForSingleObject(IN HANDLE ObjectHandleToSignal,
458 IN HANDLE WaitableObjectHandle,
459 IN BOOLEAN Alertable,
460 IN PLARGE_INTEGER TimeOut OPTIONAL)
461 {
462 KPROCESSOR_MODE PreviousMode = ExGetPreviousMode();
463 POBJECT_TYPE Type;
464 PVOID SignalObj, WaitObj, WaitableObject;
465 LARGE_INTEGER SafeTimeOut;
466 OBJECT_HANDLE_INFORMATION HandleInfo;
467 NTSTATUS Status;
468
469 /* Check if we came with a timeout from user mode */
470 if ((TimeOut) && (PreviousMode != KernelMode))
471 {
472 /* Enter SEH for probing */
473 _SEH2_TRY
474 {
475 /* Make a copy on the stack */
476 SafeTimeOut = ProbeForReadLargeInteger(TimeOut);
477 TimeOut = &SafeTimeOut;
478 }
479 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
480 {
481 /* Return the exception code */
482 _SEH2_YIELD(return _SEH2_GetExceptionCode());
483 }
484 _SEH2_END;
485 }
486
487 /* Start by getting the signal object*/
488 Status = ObReferenceObjectByHandle(ObjectHandleToSignal,
489 0,
490 NULL,
491 PreviousMode,
492 &SignalObj,
493 &HandleInfo);
494 if (!NT_SUCCESS(Status)) return Status;
495
496 /* Now get the wait object */
497 Status = ObReferenceObjectByHandle(WaitableObjectHandle,
498 SYNCHRONIZE,
499 NULL,
500 PreviousMode,
501 &WaitObj,
502 NULL);
503 if (!NT_SUCCESS(Status))
504 {
505 /* Failed to reference the wait object */
506 ObDereferenceObject(SignalObj);
507 return Status;
508 }
509
510 /* Get the real waitable object */
511 WaitableObject = OBJECT_TO_OBJECT_HEADER(WaitObj)->Type->DefaultObject;
512
513 /* Handle internal offset */
514 if (IsPointerOffset(WaitableObject))
515 {
516 /* Get real pointer */
517 WaitableObject = (PVOID)((ULONG_PTR)WaitObj +
518 (ULONG_PTR)WaitableObject);
519 }
520
521 /* Check Signal Object Type */
522 Type = OBJECT_TO_OBJECT_HEADER(SignalObj)->Type;
523 if (Type == ExEventObjectType)
524 {
525 /* Check if we came from user-mode without the right access */
526 if ((PreviousMode != KernelMode) &&
527 !(HandleInfo.GrantedAccess & EVENT_MODIFY_STATE))
528 {
529 /* Fail: lack of rights */
530 Status = STATUS_ACCESS_DENIED;
531 goto Quickie;
532 }
533
534 /* Set the Event */
535 KeSetEvent(SignalObj, EVENT_INCREMENT, TRUE);
536 }
537 else if (Type == ExMutantObjectType)
538 {
539 /* This can raise an exception */
540 _SEH2_TRY
541 {
542 /* Release the mutant */
543 KeReleaseMutant(SignalObj, MUTANT_INCREMENT, FALSE, TRUE);
544 }
545 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
546 {
547 /* Get the exception code */
548 Status = _SEH2_GetExceptionCode();
549 }
550 _SEH2_END;
551 }
552 else if (Type == ExSemaphoreObjectType)
553 {
554 /* Check if we came from user-mode without the right access */
555 if ((PreviousMode != KernelMode) &&
556 !(HandleInfo.GrantedAccess & SEMAPHORE_MODIFY_STATE))
557 {
558 /* Fail: lack of rights */
559 Status = STATUS_ACCESS_DENIED;
560 goto Quickie;
561 }
562
563 /* This can raise an exception*/
564 _SEH2_TRY
565 {
566 /* Release the semaphore */
567 KeReleaseSemaphore(SignalObj, SEMAPHORE_INCREMENT, 1, TRUE);
568 }
569 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
570 {
571 /* Get the exception code */
572 Status = _SEH2_GetExceptionCode();
573 }
574 _SEH2_END;
575 }
576 else
577 {
578 /* This isn't a valid object to be waiting on */
579 Status = STATUS_OBJECT_TYPE_MISMATCH;
580 }
581
582 /* Make sure we didn't fail */
583 if (NT_SUCCESS(Status))
584 {
585 /* SEH this since it can also raise an exception */
586 _SEH2_TRY
587 {
588 /* Perform the wait now */
589 Status = KeWaitForSingleObject(WaitableObject,
590 UserRequest,
591 PreviousMode,
592 Alertable,
593 TimeOut);
594 }
595 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
596 {
597 /* Get the exception code */
598 Status = _SEH2_GetExceptionCode();
599 }
600 _SEH2_END;
601 }
602
603 /* We're done here, dereference both objects */
604 Quickie:
605 ObDereferenceObject(SignalObj);
606 ObDereferenceObject(WaitObj);
607 return Status;
608 }
609
610 /* EOF */